“脏读”、“不可重复读”和“幻读”,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决 。数据库实现事务隔离的方式,基本可以分为以下两种 。
- 一种是在读取数据前,对其加锁,阻止其他事务对数据进行修改 。
- 另一种是不用加任何锁,通过一定机制生成一个数据请求时间点的一致性数据快照(Snapshot),并用这个快照来提供一定级别(语句级或事务级)的一致性读取 。从用户的角度,好像是数据库可以提供同一数据的多个版本,因此,这种技术叫做数据多版本并发控制(MultiVersion Concurrency Control,简称MVCC或MCC),也经常称为多版本数据库 。
在MVCC并发控制中,读操作可以分成两类:快照读 (snapshot read)与当前读 (current read) 。快照读,读取的是记录的可见版本 (有可能是历史版本),不用加锁 。当前读,读取的是记录的最新版本,并且,当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录 。
在一个支持MVCC并发控制的系统中,哪些读操作是快照读?哪些操作又是当前读呢?以MySQL InnoDB为例:
- 快照读:简单的select操作,属于快照读,不加锁 。(当然,也有例外)
select * from table where ?;
- 当前读:特殊的读操作,插入/更新/删除操作,属于当前读,需要加锁 。
下面语句都属于当前读,读取记录的最新版本 。并且,读取之后,还需要保证其他并发事务不能修改当前记录,对读取记录加锁 。其中,除了第一条语句,对读取记录加S锁 (共享锁)外,其他的操作,都加的是X锁 (排它锁) 。
select * from table where ? lock in share mode; select * from table where ? for update; insert into table values (…); update table set ? where ?; delete from table where ?;
数据库的事务隔离越严格,并发副作用越小,但付出的代价也就越大,因为事务隔离实质上就是使事务在一定程度上 “串行化”进行,这显然与“并发”是矛盾的 。同时,不同的应用对读一致性和事务隔离程度的要求也是不同的,比如许多应用对“不可重复读”和“幻读”并不敏 感,可能更关心数据并发访问的能力 。
为了解决“隔离”与“并发”的矛盾,ISO/ANSI SQL92定义了4个事务隔离级别,每个级别的隔离程度不同,允许出现的副作用也不同,应用可以根据自己的业务逻辑要求,通过选择不同的隔离级别来平衡 “隔离”与“并发”的矛盾 。下表很好地概括了这4个隔离级别的特性 。
文章插图
获取InonoD行锁争用情况
可以通过检查InnoDB_row_lock状态变量来分析系统上的行锁的争夺情况:
mysql> show status like 'innodb_row_lock%';
文章插图
如果发现锁争用比较严重,如InnoDB_row_lock_waits和InnoDB_row_lock_time_avg的值比较高,还可以通过设置InnoDB Monitors来进一步观察发生锁冲突的表、数据行等,并分析锁争用的原因 。
InnoDB的行锁模式及加锁方法
InnoDB实现了以下两种类型的行锁 。
- 共享锁(s):又称读锁 。允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁 。若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁 。这保证了其他事务可以读A,但在T释放A上的S锁之前不能对A做任何修改 。
- 排他锁(X):又称写锁 。允许获取排他锁的事务更新数据,阻止其他事务取得相同的数据集共享读锁和排他写锁 。若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁 。
- 对于共享锁大家可能很好理解,就是多个事务只能读数据不能改数据 。
对于排他锁大家的理解可能就有些差别,我当初就犯了一个错误,以为排他锁锁住一行数据后,其他事务就不能读取和修改该行数据,其实不是这样的 。排他锁指的是一个事务在一行数据加上排他锁后,其他事务不能再在其上加其他的锁 。mysql InnoDB引擎默认的修改数据语句:update,delete,insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何锁类型,如果加排他锁可以使用select …for update语句,加共享锁可以使用select … lock in share mode语句 。所以加过排他锁的数据行在其他事务种是不能修改数据的,也不能通过for update和lock in share mode锁的方式查询数据,但可以直接通过select …from…查询数据,因为普通查询没有任何锁机制 。
推荐阅读
- MySQL高级查询
- Swift 中的命令行应用
- M MySQL VARCHAR最多能存储多少数据
- 恋爱中的犀牛 话剧?犀牛的恋爱话剧台词
- yaya|泰星Yaya谈婚期:我已经准备好了,我梦想中的结婚地点是在挪威
- 分手大师|《分手大师》当中的男人梅远贵,拥有一个特殊的职业,让人意外
- 电影|《色·戒》当中的梁朝伟身为男主角,却扮演的是一个反派
- 容祖儿|赵本山戏中的“御用老伴”关婷娜,39岁的她身材丰腴,大秀美腿
- 藏红花油其中的功效和作用是什么?
- 九宫飞星图风水方位详解 九宫飞星图