事务、事务的特性、排它锁、共享锁、死锁、乐观锁、悲观锁

事务

  • 什么是事务?事务的特性是什么?

    事务是数据库中执行操作的最小执行单元,不可再分,要么全都成功,要么全都失败.

    • 事务的使用:开启事务-->执行的操作-->提交事务/回滚事务
  • 数据库中执行增删改查操作是否会涉及事务?

    • mysql中执行增删改操作时事务自动开启,执行操作成功后事务自动提交,若失败,自动回滚。

    • mysql事务是自动提交的,我们可以通过设置将事务设置为手动提交。即事务管理变为手动的。

手动管理事务的sql:
开启事务: begin;
提交事务: commit;
回滚事务: rollback;
​
如何关闭事务的自动提交?
   set autocommit=off  //关闭自动提交,默认值是on 开启的
如何查看自动提交的状态值?
   show variables like 'autocommit';
  • 手动提交事务案例:
    • 事务中 执行的操作 在 未提交之前 对外部不可见
前提:开启2个客户端,在客户端1中关闭事务自动提交
1. 查看当前数据库的autocommit值是否为on
    show variables like 'autocommit';
2. 设置当前数据库的事务提交为手动提交
    set autocommit=off  关闭自动提交,由我们手动commit:
3. 在客户端1中,手动开启事务,执行insert语句
    begin;
    insert into ... 
    select..在本事务中查看表数据,
            结果显示可以看到新数据.
4. 在客户端1未提交之前,在客户端2中查询该表中的数据,看数据是否插入成功,结果为未成功.
5. 在客户端1中提交事务,再次在客户端2中查看,可以查看到新插入的数据
​
以上案例表明:事务中执行的操作在未提交之前对外部不可见.

事务的ACID特性

  • 原子性:事务是最小执行单元,不可再分,要么全都成功,要么都失败

  • 一致性:从一个一致性状态转换到另一个一致性状态

    • 案例1:银行转账,事务前后总额一致
    • 案例2:多事务并发执行,同一个事务内多次查询结果要保证一致.除非自己做了修改
    • 注意:一致性是基于原子性和隔离性实现的.
      • 因为隔离性,事务A 才不会看到事务B的操作
      • 也就是说因为隔离性 才有了 一致性
  • 隔离性:多事务并发执行,事务之间互不影响

    • 前提:
      • 同一个事务内,不管别的事务修改数据提交还是没提交
      • 当前事务内只要没有关闭(没有提交commit;)那么查询的数据就是一致的,即使事务D修改了数据,当前事务查询的仍然是之前未修改的
      • 即:一个事务内,不同时间点,查到的数据是一致的,除非这个事务自己改了
        • 出现的问题就是,如果事务C将数据都删除了,然后事务D查看的还是全部所有的数据,事务D此时在插入一条数据,就会成为脏数据
    • 事务C 查询
    • 事务D 修改
  • 持久性:事务提交后,数据会持久化到数据库中.

Spring的声明式事务 

  • Spring的声明式事务管理,其实就是数据库的事务进行管理
    • 这里开启了,就相当于数据库的事务开启了
    • 当我们执行的增删改操作,只不过是由spring框架来管理事务了(相当于帮助我们去手动开启事务,成功提交事务,失败回滚)
      • begin;
      • commit;
      • rollback;
面试题:描述Spring的声明式事务:添加注解@Transactional, 

业务层某方法中涉及多步增删改操作,需要在方法上方添加注解@Transactional, 
即为Spring的声明式事务管理

面试题:Spring中的事务管理有哪些?
声明式事务管理 -- aop的应用:spring
编程式事务管理:自己写事务 向hibernate
  • 事务的隔离级别(难点)

  • 若两个事务完全并发执行,产生的问题.

    • 脏读
    • 不可重复读
    • 幻读
  • 模拟最初事务,没有ACID特性,完全并发 
  • 如果没有隔离性的话,当并发读取的时候,
    • 如下图:事务A 开始,事务B开始
    • 事务A读取余额为1000元,事务B花费-500元
    • 事务A读取余额为500,事务B,扣款失败,此时还剩1000元
    • 事务A 读取余额,花费200,此时余额为300,而事务B还有1000元
    • 用户1 的余额应该是800,但是读出了300元,这就是脏数据
    • 这就是脏读:某事物读取到了其他事务回滚前的数据
    • 脏堵产生的原因:事务的内部对外可见(提交或回滚之前,数据对外可见)

隔离级别:

        什么是隔离:? 

               即:一个事务没执行完,另一个事务已经执行完了

  • 读未提交 (read-uncommited)-

    • 所有数据库都不会这么干,全乱了

  • 读已提交(read-commited) --

    • 操作一个数据行的:时候
    • 优点:解决脏读,缺点:出现不可重复读(其他事务进行了修改)
    • 这就是同一个事务的不可重复读,因为事务A,没有做操作,金额却变少了
    • 理想是:我没动它,上次看它多少,下一次看它应该还是多少
    • 重复读取的时候,可能会发生前后执行查询数据不一致
    • (事务A读,事务B改前和B改后,事务A没操作,前后读取的数据却不一致了)

  •  可重复读

    • (repeatable-read) -- 解决不可重复读,出现问题:幻读(其他事务进行了增删)
    • 测试:
      • 事务A开启事务,并执行了修改操作,没有提交,没有结束
      • 事务B开启事务,并执行了修改操作,此时并没有给修改成功,而是抛出了错误
        • 原因是事务A并没有结束,事务B修改的操作在阻塞中
        • 事务A执行的时候,给这一行加上了锁,别的事务不能给这行左update操作
      • 即:一个事务没执行完,另一个事务已经执行完了
      • 5、这就说明 解决了不可重复读的问题,但是会出现幻读

  • 锁行
    • 可通过给操作的数据行加锁排它 锁),加锁期间不允许其它事务执行update操作,从而解决不可重复读的问题.
      • 当事务A操作的时候,给数据加锁,只要该事务没有结束,释放锁,那么别的事务就不能对其进行update操作
      • 但是可以进行增删操作,事务A执行期间,当事务B进行新增commit;的时候,事务A前后读取的总数量就不一致了,这就出现了幻读
    • 为什么产生幻读:
      • 因为加锁的只是那一行,而增删却没有加锁
    • 没有幻读是符合用户的需求的,每次查到的过程都是一致的

  • 可串行化 

  • (Serializable): (顺序执行)-- 解决幻读(通常情况下不会使用,效率太低)
    • 就是串联,给表加锁顺序执行,一个执行完结束后,另一个事务才能进来
  • 锁表(就是把表锁上了,一个事务操作,别的事务不允许增删改查)
    • 实现机制为加表锁
  • 四种隔离级别按顺序由低到高
    • 隔离级别越高,数据越安全,多事务并发执行效率越低.
  • 四种隔离级别下可能会产生的问题:
          脏读	不可重复读	幻读
读未提交	√	  √	         √
读已提交	×	  √	         √
可重复读	×	  ×	         √
可串行化	×	  ×	         ×
  • 数据库默认的隔离级别:

    • mysql 的隔离级别默认为可重复读
    • oracle和sql servlet默认的隔离级别为读已提交
  • mysql默认隔离级别下测试是否会产生幻读?
  • 答案:不会!
    • mysql虽然是可重复读,但是在mysql中并不会产生幻读
      • mysql中读取数据采用快照读
        • 相当于第一次select查询的时候,对结果集拍了一张照,如果后序再次想要执行该select是直接使用之前拍的快照,之前查到的什么,这里显示的就是什么
        • 其他事务对其增删,该事务都不会管的
        • 也就是说:读取本事务最开始读取到的数据
      • 快照读相反的是当前读(每次读取均读取最新的数据)

  • mysql的存储引擎是(mysql是用来存储数据的,那么就得有存储引擎)
    • Innodb -- 支持事务,支持行锁
      • 这就说明有些存储引擎不支持事务

        MYISAM

        Memory

        InnoDB

        Archive

在MySQL的众多存储引擎中,只有InnoDB支持事务,关于事物隔离级别,以下说法错误的是()
A、Read uncommitted、Read committed 、Repeatable read、Serializable四种隔离级别并行性能依次降低,安全性依次提高。
B、脏读是某一事务读取了另外一个事务未提交的数据,不可重复读是读取了其他事务提交的数据,脏读和不可重复读都可以通过事物隔离级别控制。
C、RR隔离级别,只能返回比当前事务早的提交插入、更新、删除值。
D、RR和RC隔离级别都存在幻读,RR隔离级别幻读可以通过next-key lock避免。


C,和删除增加没关系

死锁 - DeadLock

  • 什么是死锁?
    • 是指多事务并发执行时,出现了事务之间彼此阻塞,无法继续执行的现象。
  • 数据库中如何产生死锁?
    • 数据库中的锁:
      • 按粒度分:
        • 表锁,行锁
      • 按锁的类别分:
        • 共享锁(S锁) 和 排它锁(X锁)
      • 共享锁和排它锁满足以下规则:
        • 在排他锁上,不可以加其它任何锁,
        • 在共享锁上,可以再加共享锁,不可以加排它锁
      • 排它锁:一般都是在写(增、删、改)的操作上加的;
        • 我再增删改的同时,不允许别人再来增删改,不允许再来增加排它锁
        • 排它锁,排斥一切锁
      • 共享锁:我锁住的数据可以共享,在读的操作会加
        • 事务A读取某一条数据的时候,给它加上共享锁,
        • 事务B也读取这条数据,它也需要加一个共享锁,是可以的,因为加的都是共享锁,
        • 但是如果加的共享锁,又来一个事务想要执行增删改操作,这是不可以的
          • 我正在读,你要改,绝对不可以(我是读之前的还是之后的)
  • mysql中哪些操作会涉及加锁?
    • 1、增删改操作,会默认给 行数据 加 排他锁(x锁): for update
    • 2、select操作默认不加任何锁
      • 但是,实际中有一些情况需要在select时给行数据加锁,
      • 此时是需要手动加锁的
  • 查询手动加锁

select ..from...  for share; //共享锁  分享
select..from.. for update; //排他锁,泛指所有写(增删改)操作
  • 多事务并发对同一条数据执行update(增、删、改)操作,则后来的事务会阻塞,直到前一个事务执行结束.
  • 也就是说多个事务并发,结果是看谁最后执行完
步骤:
1. 事务A开启 -->对用户4修改name
2. 事务B开启 -->删除用户4-->出现了阻塞
3. 提交事务A,事务A提交成功,事务B立即执行
4. 最后提交事务B

 死锁产生的场景:

  • 死锁一定是事务并发产生的
  • 事务A操作修改,然后操作删除
  • 事务B操作修改,然后操作删除
    • 写的操作都会对行上排它锁,发现该行有排它锁,就会进行阻塞等待
    • 跟java的死锁原理一样
      • java有必须两个对象
      • 数据库必须有两张表
      • 然后两个线程/事务 分别对两个对象/表操作,没有是释放,然后再交错加错(必须保证不释放),就形成了死锁
  • 事务A等事务B结束,事务B等事务A结束,彼此阻塞都无法释放就形成了死锁
  • 注意:mysql在发现死锁后的处理:
    • 会强制将一端进行回滚:
      • 事务并发  后执行的  回滚!
      • 事务并发  先执行的   成功!
    • 事务执行成功 该事务commit;后,回滚的一方也能看见最新的数据
    • 注意:等待超时后,自动回滚!

  •  发生死锁后,mysql做了有关死锁的处理
代码实现:
事务A:
    1. begin;
    2. update user set age=26 where id=1;
    5. delete from student where id=101;-->阻塞---发现没有锁了,继续往下执行,执行成功
    
事务B:
    3. begin;
    4. update student set age=16 where id=101;
    6. delete from user where id=1 -->检测到死锁---mysql做了处理 对事务B强制做了回滚

悲观锁和乐观锁

  • 是两种思想,数据库里并不存在这两种锁,是人的两种思想
    • 悲观锁
      • 事务总是悲观的认为在我访问期间,总会有其它事务并发访问相同的数据
      • 为了确保数据的安全,会在访问时立即给数据加排它锁,保证数据安全,但是效率会降低。
    • 注意:悲观锁中若执行查询操作想给数据加锁,此时sql语句写成:
      • select ..from ...for update;
    • 针对悲观锁效率太低,就出现了乐观锁
    • 乐观锁
      • 事务总是很乐观的认为,在本事务访问期间,不会有其它事务并发访问不会给数据加锁,但是多事务并发访问确实存在安全问题
      • 为了解决安全问题乐观锁采用version(版本号机制)来保证数据安全。
  • version机制(版本号机制)
    • 它是为我们表添加了一个字段,如果使用乐观锁,那么就往表里添加一个字段叫做version,它是个整数类型,初始默认值为0
      • 事务在提交时,判断数据库中的version值与事务已获取的version是否相等,
        • 若相等,则提交成功;
        • 若不相等,说明数据已经被其它事务修改了,事务回滚,之后事务再次尝试执行.
  • 如下:事务A和事务B都访问同一条数据并且执行的是update操作,悲观锁会加排它锁,但是乐观锁不存在排它锁,两个事务都要修改同一条数据,如何保证数据安全?
    • 做法:
      • java里的操作肯定先获得数据
      • 事务A做修改
      • 1、事务A先查询用户1的数据(会得到该版本号字段的值)
      • 2、执行修改操作,执行结束(事务A执行过程当中,预期结果和执行完的结果是一样的,(采用版本号机制还有一个操作 如下:swap更新的意思)
      • 3、修改完后,(又会得到版本号)在提交事务前(立即提交事务时,判断数据库中的version值,是否与步骤1获取到的version值一样,若一致则提交,若不一致则回滚,并自旋)会执行一次CAS(compare and swap)(判断两次获取的版本号是否相等,如相等则提交成功)然后执行提交操作
      • 注意:若提交成功,数据里的版本号会自增1,(执行加1),即:提交后数据库里的版本号会变成1;
      • 事务B做修改:
      • a、事务B查询用户1的数据是0
      • ...然后修改完成后,接下来涉及到提交操作
      • 提交时执行一次CAS,进行版本号的判断,数据库的版本号是1,a查到的版本号是0
      • CAS发现两个版本号不同,则事务回滚,然后尝试重新执行
      • 自旋发现版本号不同->回滚->再尝试重新执行,在指定时间内如果还不成功的话,此时会给出用户提示,重新操作,这就是自旋
    • 也就是说,并发情况下谁先抢到时间片,谁成功

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值