数据库事务与事务隔离级别
数据库事务与事务隔离级别
什么是事务
- 事务是一件或多件时间的集合,这些事件要么全都做,要么都不做
- 事务如果映射到MySQL数据库中,事务就是一条或者多条的SQL语句组成的SQL单元,这个单元是不可分割的,要么全不执行,要么全都不执行
事务的特性
- 原子性(Atomicity); 指的是一个事务中的所有操作,要么全部执行,要么全都不执行,不会在中途停止,不会拆分开
- 一致性(Consistency): 在事务开始和完成时,数据库中的数据都保持一致的状态
- 隔离性(Isolation): 一个事务正在执行的时候,不能被其他事务干扰
- 持久性(Druability): 事务一旦提交,永久有效,不能回滚
- MySQL数据库是自动提交,Oracle数据库是手动提交
更改数据库自动提交状态
- 查看数据库自动提交状态
- show variables like %commit%
- MySQL默认是自动提交,自动提交状态是查询结果中autocommit的值 on为自动提交 off则为手动提交
- 为了数据安全,在执行SQL语句之前,最好更改为手动提交
- set autocommit = 0 设置为0手动提交,1为自动提交
- rollbake回滚: 当因失误的操作导致数据库需要返回上一步时,因为开启了手动提交,所以可以进行回滚,如果是自动提交,那就是一个很严重的事故了
- SavePoint保存点: 作用是在某个节点进行保存,出现错误的时候可以回滚到指定有保存点的位置,如果有四个保存点,当回滚到最顶端保存点时,顶端下面的保存点会全部失效,顶端下面的保存点会全部被覆盖掉
- commit提交: 当SQL语句执行结果满足项目要求并且没有问题之后,进行手动提交,将数据写入数据库
- 事务的隔离级别
- 隔离性就是一个事务在执行的过程中不能被其他事务干扰,为了防止事务操作间的混淆,所以必须采用一个串行化或序列化的方式来执行不能的事务,使在同一时间内有且仅有一个请求来操作同一数据--在事务正确提交之前,不允许把当前事务操作的数据提供给其他事务
- 通俗来说,就是一个事务在操作时间内,这个数据对外界不可见。
- 隔离级别
- 读未提交数据 read uncommitted: 会出现脏读、不可重复读、幻读
- 读已提交数据 read committed: 解决了脏读,会出现不可重复读、幻读
- 可重复度 reaptable read: 解决脏读、不可重复读、会出现幻读
- 串行化 serializble: 耗费的资源和花费的时间都非常巨大,但是可以解决全部情况
- MySQL默认的是第三个隔离级别
- 锁
- 锁就可以实现隔离级别
- 悲观锁: 在一个事务操作表时,将整张表锁定,其他事务访问不了
- 悲观锁主要是共享锁和排它锁
- 共享锁: 又称读锁,简称s锁,就是多个事务对于统一数据可以共享一把锁,都能访问到数据,但是只能读,不能改
- 排它锁: 又称写锁,简称x锁,排它锁不能与其他锁并存,如果一个事务获取了一个数据行的排它锁,其他事务就不能再获取到该行的其他锁,包括共享锁和排它锁。但是获取到排它锁的事务,可以对数据行读取和修改
- 先驱锁再访问的策略保证了数据在并发控制下的安全性
- 乐观锁: 乐观锁主要为字段锁,比如版本号、时间戳。主要依靠数据本身来保证数据的正确性
- 事务并发下可能出现的问题
- 更新丢失: 两个事务T1,T2读取同一条数据并修改,T2提交的结果覆盖了T提交的结果,T1的更新数据丢失了
- 脏读: 事务T1 修改了A表中的某一条记录,并将数据写回表中,没提交,事务T2读取了同一条数据,这是T1由于某种原因回滚了,数据就恢复到原始值,此时T2读取到的数据与数据库表中的数据"不一致" ,那么T2读取到的数据就是"脏"数据,也叫不正确的数据
- 不可重复读: 是指在一个事务内,T1多次读取同一条记录,这个事务还没结束T2也访问了这条记录,并进行了修改,这时T1多次读取的数据不一致啊;此时T1在这个事务中两次读取的数据不一致,这种现象就是不可重复读
- 幻读: 目前工资为5000的员工有10人, 事务T1读取所有工资为5000的人数为 10; 此时事务T2 插入一条工资为5000的记录这时 ,事务T1在读取工资为5000元的人数为 11 ;此时T1 就产生了 幻读
- 不可重复读的重点是修改,同样的条件,读过的数据再次读取时发现不一样了
- 幻读的重点在于新增或删除,同样的条件,第一次和第二次读的记录不一样
- 举例
//分别设置两个窗口为手动提交 set autocommit = 0 //悲观锁 select * from account where aid = 1 for update //乐观锁, 乐观锁是抽象的,没有锁的现象 insert into user(id,name,password,createTime) values(null,'admin','123456',now()); //行锁 id userName age password version版本号