mysql事务与锁

1 事务针对范围:

(1)mysql的任何一个修改操作sql(insert update delete)事务都是客观伴随的,只不过默认是自动提交,这种情况,一旦执行sql,就自动commit了

(2)sql可以实现手动提交(关闭自动提交):

         (2.1) start  transaction / begin/ set autocimmit = 0

         (2.2)执行update/insert/delete sql 但是没有提交到数据库

         (2.3)commit 提交到数据库

2 事务特性

  (2.1) 原子性:一个事务(代码表现形式是:@Transactional注解的方法)里有很多条sql(修改操作),这些sql要么全部执行成功,要么全部执行失败,就像原子一样,不能再被分割

  (2.2)一致性:事务执行完成后,数据库状态从一个一致的状态转换到另一个一致的状态,即执行事务之前状态满足约束条件,事务执行完成后状态满足约束条件。

               例子:用户A给用户B转100,A-100,B+100。那是最终的A+B任然保持不变

  (2.3)隔离性:就是每个事务是在不同的事务之间相互隔离、互不影响。在commit事务之前,修改操作不会提交到数据库(持久化到磁盘)。

               “已提交读”的隔离情况下,事务1的select操作不能读取到事务2在commit之前修改数据库的结果,事务1和事务2仿佛隔离状态

               但在事务的update、delete会有加: 行锁 ,一定操作到的是真正commit的数据。(select不会加 行锁,故而胀读、不可重复读) 比如:

session1session2

begin 
update t_charge_record 

set `status` = 3 

where 

id = 'WK-BK-20171211163735578073203' 

and `status`= 1

affect  num : 1

 
 

update t_charge_record

set `status` = 3 

where 

id = 'WK-BK-20171211163735578073203' 

and `status`= 1

 这时,事实上这句update是任然存在事务(自动),由于session1对这行还没有的修改还没commit,所以mysql自身存在一个行锁,必须等session1提交后,才释放行锁

commit

session1释放行锁

 
 affect num : 0 由于session1提交后不满足where条件,返回受影响行数为0

(2.4)持久性

                一旦成功commit后,数据就写到磁盘里面,持久化了,不可改变(妹的。。。把磁盘打碎算不算)

3 事务隔离级别(都是在讨论“读”

 

 胀读不可重复读幻读更新丢失
未提交读
已提交读
可重复度(repeatable read )mysql 默认级别
可序列化

胀读:事务1更新记录1,但未提交。事务2 select 到事务1尚未commit的记录1, 并根据记录1做后续操作,则造成未提交数据依赖关系问题(万一事务1回滚了更新记录1呢?)故称胀读。这种隔离级别的并发性最好。防止手段就是在事务1未提交事务的情况下,事务2是禁止读取记录的,当然也降低了并发性(我改的时候你不能读)

已提交读:事务1是无法select到事务2 还没有commit到数据库的更新,只有事务2 commit了后,事务1才能select到

可重复读:事务1 读了记录1 ,事务2 更新为记录1' ,且commit,这时事务1再次读记录1,记录1已经变成1'了,就叫做不可重复读。不可重复读,就是在这里需要做隔离,估计防止手段是在事务1读取记录1,而未提交的时候,禁止事务2修改记录1,同样也降低了并发性(我的时候你不能改)

幻读:事务1按照条件A查询,事务2新插入数据,并且提交。事务1按照条件A搜索出更多的果集。防止手段就是在事务1未提交的情况下,禁止插入新记录,这样并发性很差(我读的时候,你不能插)

第一类更新丢失(回滚丢失)

事务1事务2
开始事务开始事务
查询账户1000查询账户1000
 更新账户1000+100=1100
 commit
更新账户1000-100=900 
rollback 
账户回滚到1000 

事务1的回滚覆盖了事务2的更新,但在sql标准中,任何隔离级别都是不允许这种情况的,所以无需考虑

第二类更新丢失:
账户余额并发修改,账户原有1000,扣两次100钱,由于并发情况,第一次1000-100=900,第二次1000-100=900。那么第一次的减100的信息就丢失了。更新丢失需要靠应用程序保证。
注意:(1)在上面业务中,并发线程(事务)几乎同时select到原有账户余额,由于事务1此时并没有执行到update这句,两个事务都是在select,所以避免了脏读的隔离级别也不能解决这个问题。即防止脏读是防止:一个事务刚执行了update,但未commit,另一个事务的select该记录,会被阻塞,那个这个时间颗粒度是事务2的select到事务1的commt,并非整个事务中。
(2)避免了不可重复读的隔离级别也不能解决这个问题,尽管由于事务1还没有commit,事务2执行的update账户操作会被阻塞,但是等到事务2执行是,事务执行的基准任然是1000,并不是900(事务1扣100)。即防止不可重复读是防止:事务1执行过select,但未commit,事务2不能update,会被阻塞,阻塞的时间颗粒度也是事务2的update到事务1的commt,并非整个事务

第二类更新丢失:

4 InnoDB的行锁(针对select)

这里区分一个概念,隔离级别讨论的是读select的问题,而所有更新操作(update、insert、delete)都是需要获得锁的;

InnoDB有两种形式select行锁(注意:update/delete本身默认就会加行锁):共享锁、排他锁

(4.1)共享锁:select * from table lock in share mode

只是确保该条记录在整个事务中,不被修改(update delete),包括自己session和其它session。其它session修改会被阻塞,自己session修改会死锁

(4.2)排他锁:select * from table for update

保证在整个事务中,其它session不能修改此记录,且在自己session会修改此记录

 

5 乐观锁和悲观锁

乐观锁利用update/delete 操作本身行锁性质 + 返回受影响行数 + 版本号 实现

悲观锁利用排他锁实现

6 sql:begin,commit,rollback指令,与spring transaction 提交、回滚效果区别

 

7 事务中加java锁
 

8 事务传播

以service方法为例,f1方法是一个事务,f2方法是一个事务,f1中调用f2时,f2的事务和f1事务的关系?

 

(9.1)PROPAGATION_REQUIRED:如果f1存在事务,则f2加入f1的事务。

              注意:spring默认事务传播,最常用!

(9.2)PROPAGATION_REQUIRES_NEW:无论f1是否有事务,f2都会新创建一个事务。

             注意:这种级别有死锁情况,f1和f2都更新一个表,会死锁。f1和f2更新不同表不会死锁,且事务独立

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值