MYSQL篇: 中篇(事务)

176 阅读7分钟

1、事务4大特性

事务4大特性: 原子性、一致性、隔离性、持久性

原⼦性: 事务是最⼩的执⾏单位,不允许分割。事务的原⼦性确保动作要么全部完成,要么全不执行

一致性: 执⾏事务前后,数据保持⼀致,多个事务对同⼀个数据读取的结果是相同的;

隔离性: 并发访问数据库时,⼀个⽤户的事务不被其他事务所⼲扰,各并发事务之间数据库是独⽴的;

持久性: ⼀个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发⽣故障也不应该对其有任何影响。

实现保证: MySQL的存储引擎InnoDB使用重做日志保证一致性与持久性,回滚日志保证原子性,使用各种锁来保证隔离性。

2、事务隔离级别

读未提交: 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。

读已提交: 允许读取并发事务已经提交的数据,可以阻⽌脏读,但是幻读或不可重复读仍有可能发⽣。

可重复读: 同⼀字段的多次读取结果都是⼀致的,除⾮数据是被本身事务⾃⼰所修改,可以阻⽌脏读和不可重复读,会有幻读。

串行化: 最⾼的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执⾏,这样事务之间就完全不可能产⽣⼲扰。

隔离级别并发问题
读未提交可能会导致脏读、幻读或不可重复读
读已提交可能会导致幻读或不可重复读
可重复读可能会导致幻读
可串行化不会产⽣⼲扰

3、默认隔离级别-RR

默认隔离级别: 可重复读;

同⼀字段的多次读取结果都是⼀致的,除⾮数据是被本身事务⾃⼰所修改;

可重复读是有可能出现幻读的,如果要保证绝对的安全只能把隔离级别设置成SERIALIZABLE;这样所有事务都只能顺序执行,自然不会因为并发有什么影响了,但是性能会下降许多。

第二种方式,使用MVCC解决快照读幻读问题(如简单select),读取的不是最新的数据。维护一个字段作为version,这样可以控制到每次只能有一个人更新一个版本。

select id from table_xx where id = ? and version = V
update id from table_xx where id = ? and version = V+1

第三种方式,如果需要读最新的数据,可以通过GapLock+Next-KeyLock可以解决当前读幻读问题

select id from table_xx where id > 100 for update;
select id from table_xx where id > 100 lock in share mode;

4、RR和RC使用场景

事务隔离级别RC(read commit)和RR(repeatable read)两种事务隔离级别基于多版本并发控制MVCC(multi-version concurrency control)来实现。

RCRR
实现多条查询语句会创建多个不同的ReadView仅需要一个版本的ReadView
粒度语句级读一致性事务级读一致性
准确性每次语句执行时间点的数据第一条语句执行时间点的数据

5、行锁,表锁,意向锁

InnoDB⽀持⾏级锁(row-level locking)和表级锁,默认为⾏级锁

InnoDB按照不同的分类的锁:

共享/排它锁(Shared and Exclusive Locks):行级别锁,

意向锁(Intention Locks),表级别锁

间隙锁(Gap Locks),锁定一个区间

记录锁(Record Locks),锁定一个行记录

表级锁:(串行化)

Mysql中锁定 粒度最大的一种锁,对当前操作的整张表加锁,实现简单 ,资源消耗也比较少,加锁快,不会出现死锁 。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM和 InnoDB引擎都支持表级锁。

行级锁:(RR、RC)

Mysql中锁定 粒度最小 的一种锁,只针对当前操作的行进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。 InnoDB支持的行级锁,包括如下几种:

记录锁(Record Lock): 对索引项加锁,锁定符合条件的行。其他事务不能修改和删除加锁项;

间隙锁(Gap Lock): 对索引项之间的“间隙”加锁,锁定记录的范围,不包含索引项本身,其他事务不能在锁范围内插入数据。

Next-key Lock: 锁定索引项本身和索引范围。即Record Lock和Gap Lock的结合。可解决幻读问题。

InnoDB 支持多粒度锁(multiple granularity locking),它允许行级锁与表级锁共存,而意向锁就是其中的一种表锁。

共享锁( shared lock, S )锁允许持有锁读取行的事务。加锁时将自己和子节点全加S锁,父节点直到表头全加IS锁

排他锁( exclusive lock, X )锁允许持有锁修改行的事务。 加锁时将自己和子节点全加X锁,父节点直到表头全加IX锁

意向共享锁(intention shared lock, IS):事务有意向对表中的某些行加共享锁(S锁)

意向排他锁(intention exclusive lock, IX):事务有意向对表中的某些行加排他锁(X锁)

互斥性共享锁(S)排它锁(X)意向共享锁IS意向排他锁IX
共享锁(S)
排它锁(X)
意向共享锁IS
意向排他锁IX

6、MVCC多版本并发控制

MVCC是一种多版本并发控制机制,通过事务的可见性看到自己预期的数据,能降低其系统开销.(RC和RR级别工作)

InnoDB的MVCC,是通过在每行记录后面保存系统版本号(可以理解为事务的ID),每开始一个新的事务,系统版本号就会自动递增,事务开始时刻的系统版本号会作为事务的ID。这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的,防止幻读的产生。

1.MVCC手段只适用于Msyql隔离级别中的读已提交(Read committed)和可重复读(Repeatable Read).

2.Read uncimmitted由于存在脏读,即能读到未提交事务的数据行,所以不适用MVCC.

3.简单的select快照度不会加锁,删改及select for update等需要当前读的场景会加锁

原因是MVCC的创建版本和删除版本只要在事务提交后才会产生。客观上,mysql使用的是乐观锁的一整实现方式,就是每行都有版本号,保存时根据版本号决定是否成功。Innodb的MVCC使用到的快照存储在Undo日志中,该日志通过回滚指针把一个数据行所有快照连接起来。

版本链

在InnoDB引擎表中,它的聚簇索引记录中有两个必要的隐藏列:

trx_id

这个id用来存储的每次对某条聚簇索引记录进行修改的时候的事务id。

roll_pointer

每次对哪条聚簇索引记录有修改的时候,都会把老版本写入undo日志中。这个roll_pointer就是存了一个指针,它指向这条聚簇索引记录的上一个版本的位置,通过它来获得上一个版本的记录信息。(注意插入操作的undo日志没有这个属性,因为它没有老版本)

每次修改都会在版本链中记录。SELECT可以去版本链中拿记录,这就实现了读-写,写-读的并发执行, 提升了系统的性能。


  • [ 萱儿AXW ]