七、事务隔离级别和MVCC

transaction本意是买卖、交易。数据库世界为了强调数据的原子性,中译为 事务。

SQL标准规定不同隔离级别下产生的问题不同,会出现脏读、幻读与不可重复读的现象。对应四种事务的隔离级别:

  • Read UnCommitted 会出现脏读、幻读与不可重复读现象
  • ReadCommitted 只会出现幻读与不可重复读现象
  • Repeatable Read  只会出现幻读现象
  • Serializable 串行执行

不过,各种数据库实现对SQL标准支持不同。

解决脏读、幻读与不可重复读的现象的两种解决方案:

方案一:读操作使用mvcc,写操作加锁

mvcc也叫版本链。每条聚簇索引叶子节点的记录都有隐藏的列:trx_id与roll_pointer,每个事务都有唯一的id。在操作记录时,又会产生undo日志,undo日志形成的链表的引用就保存在roll_pointer位置。

在读取一条记录时,通过快照生成一个ReadView的结构,他会记录当前系统中正活动的事务id,遍历roll_pointer的版本链进行对比,如果ReadView里包含undo的事务id,则证明该事务并没有提交,不能采用此日志的处理结果。一直遍历得到的事务id不在ReadView里,并且小于ReadView的最小事务id(因为事务id有全局变量记录并递增,小的事务id说明产生的更早)的事务,则返回此事务处理后的结果。

简单的说,通过mvcc能区别已提交与未提交的事务,读取记录时只返回已提交的事务结果就能避免脏读与不可重复读。

其实通过mvcc就能解决读写冲突的问题,因为读操作是新事务,而读操作只会读取已提交事务的结果,两者不会存在冲突。但考虑到一些特殊业务必须读出的结果是最新的版本,就只能用锁来解决了。

方案二:读写都加锁

加锁的场景比较适合只允许读取最新的记录。比如银行转账需要先查出来最新的余额然后加上新增的数目,再存入数据库。

采用加锁的方式的话,读写需要排队执行,影响性能。但是某些业务要求如此,那也没有办法

当一个事务访问一条记录时,会在内存里生成一个与之关联的锁结构,这种行为可看成加锁。锁结构简单理解包含:事务信息、is_waiting字段,type字段。

当事务A访问记录A时,会给记录A加一个锁结构A,如果事务A时第一个访问记录A的,那么锁结构A的事务信息就是事务A的信息,is_waiting字段是false。当事务B也需要访问记录A,发现事务A先到的,同样也会生成锁结构B,不过is_waiting字段是true。等事务A访问结束,发现有事务B在等待那就激活事务B。

Mysql加锁的原理就是这样。在实现锁结构时,可以按照读-读,读-写,写-写不同的要求实现不同力度的锁,比如共享锁、独占锁、意向共享锁、意向独占锁等;也同样实现出不同粒度的锁,比如Innodb的行锁、表锁。行锁里比如间隙锁Gap Locks、记录锁(就是最常见的锁一条记录)Record Locks、Next-key Locks(相当于间隙所+记录锁)等;表锁里比如自增键可能使用的AUTO-INC锁等

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值