事务
MySql事务启动方式
事务必有的四大特性(ACID)
并发事务处理带来的问题
事务隔离级别
事务隔离级别的查看与设置
隔离级别的实现方式
LBCC
MVCC
快照读与当前读
总结
参考资料

toc

 

事务

保证一组数据库操作,要么全部成功,要么全部失败。
MySql事务在引擎层实现,仅有InnoDb引擎支持事务。

MySql事务启动方式
  • 显示启动,begin或start transaction开启事务----> commit/rollback结束事务
  • 自动启动,DML语句会自动开启事务,autocommit为1时,语句执行后会自动提交,当autocommit为0时,必须手动提交/回滚/断开连接才能结束事务(可能导致长事务,建议总是set autocommit=1,并且显示启动事务)
事务必有的四大特性(ACID)
  • 原子性(Atomicity):事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行。
  • 一致性(Consistent):在事务开始和完成时,数据都必须保持一致状态。这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性;事务结束时,所有的内部数据结构(如B树索引或双向链表)也都必须是正确的。
  • 隔离性(Isolation):数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的“独立”环境执行。这意味着事务处理过程中的中间状态对外部是不可见的,反之亦然。
  • 持久性(Durable):事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能够保持。
并发事务处理带来的问题

并发事务,是数据库通过多线程的方式,并发执行用户操作,每个线程都开启事务,都进行增删改查。
相对于串行处理来说,并发事务处理能大大增加数据库资源的利用率,提高数据库系统的事务吞吐量,从而可以支持更多的用户。但并发事务处理也会带来一些问题,主要包括以下几种情况:

  • 更新丢失(Lost Update):当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,后一个事务的更新覆盖了由前面其他事务所做的更新。
  • 脏读(Dirty Reads):一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不一致状态;这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象地叫做"脏读"。
    简单的说就是,读到了另一事务还未提交,甚至是回滚之前的数据,读到的数据可能是无效的/不是真正想要的。
  • 不可重复读(Non-Repeatable Reads):一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了!这种现象就叫做“不可重复读”。
    简单的说就是,同一事务内,相同的查询条件,前后查询到了不同结果
  • 幻读(Phantom Reads):一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读”。
    简单的说就是,同一事务内,相同的查询条件,前后查询到了不同数目
事务隔离级别

上面提到的,并发事务带来的问题中,脏读、不可重复度、幻读都是数据读一致性问题,必须由数据库的事务隔离机制处理。

隔离级别 脏读 不可重复读 幻读 含义
读未提交Read uncommitted 可能 可能 可能 一个事物还未提交时,它做的变更就能被别的事务看到
读提交Read committed 不可能 可能 可能 一个事务提交后,它做的变更才会被其他事务看到(Oracle默认级别)
可重复读Repeatable read 不可能 不可能 可能(MySql5.5+、InnoDb不可能) 一个事务执行过程中看到的数据,总是和这个事务在启动时看到的数据是一致的;提交后变更才会被别的事务看到(MySql默认级别)
串行化Serializable 不可能 不可能 不可能 读操作加共享锁,其他事务可以并发读,写操作加加排它锁,其他事务不能并发读/写

从读未提交到串行化:数据一致性越来越高,但并发度越来越低。
一般情况下,读未提交与串行化是不使用的隔离级别。

事务隔离级别的查看与设置
  • 查看当前会话隔离级别
    select@@tx_isolation;
    MySql事务、隔离级别_MySql
  • 查看系统隔离级别
    select @@global.tx_isolation;
    MySql事务、隔离级别_MySql_02
  • 设置当前会话隔离级别
    set session transaction isolation level xxxx(前面的4种隔离级别)
    MySql事务、隔离级别_MySql_03
  • 设置系统隔离级别
    set global transaction isolation level xxxx(前面的4种隔离级别)
    MySql事务、隔离级别_MySql_04
隔离级别的实现方式

LBCC

LBCC(Lock Based Concurrency Control) 基于锁的并发控制:通过加锁,来阻止其他事务修改/读取数据
在事务中,查询数据时,会被加上"读锁",可允许多个事务可并行查询,但没法修改数据(加不上"写锁"),必须等到全部"读锁"解锁(提交/回滚)。
在事务中,修改数据时,会被加上"写锁",其他事务只能等待当前"写锁"解锁(提交/回滚)后才能对数据进行查询或修改(加不上锁)
LBCC串行化隔离级别的实现方式

MVCC

MVCC(Multi version Concurrency Control) 多版本并发控制:每次有事务更新数据便会产生一个新数据版本,一行数据可能有多个数据版本:
   1. 如果是RR级别,事务启动时,该事务仅能看到它启动后第一个select之前,已提交的数据版本。并以最后一个可见数据版本来创建一致性视图。该事物后续的普通查询全部共用此视图。
   2. 如果是RC级别,事务启动后,事务内的语句可以看到它执行之前已提交的数据版本。并且每条查询语句执行之前都会计算出一个新视图。

快照读与当前读
  • 快照读:事务内的普通select语句,读取的是一致性视图里的数据,不涉及任何加锁解锁操作。
  • 当前读:加锁的select(select...lock in share mode、select...for update),与update , delete (这两个语句都会被加上"写锁",试图先读最新版本,再进行操作),这些语句都会加锁,并试图读取最新的数据版本,如果最新版本上还有其他事务的锁,则阻塞直到其他事务将锁释放后才能继续当前读("读锁"间不阻塞,"读锁"与"写锁"、"写锁"与"写锁"阻塞)。
总结
  1. 事务具有ACID四大特性
  2. 并发事务可能带来一些问题,需要设置合理的隔离级别解决
  3. 隔离级别越高,一致性越好,但性能越差
  4. 隔离级别由锁与MVCC共同实现:
  • ru下不加任何锁
  • rc下读由MVCC控制,事务内
    • 每次普通查询均以最新一个已提交版本,来创建新视图,查询依赖这个新视图,普通查询不会加锁
    • 更新会拷贝最新一个已提交数据,并作为一个新的数据版本,写操作被加上"写锁",在这个版本上进行排他式修改,直到提交
  • rr下MVCC、锁同时存在,事务内
    • 全部普通查询依赖于第一次快照读时产生的视图,普通查询不会加锁
    • 更新会拷贝最新一个已提交数据,并作为一个新的数据版本,写操作被加上"写锁",在这个版本上进行排他式修改,直到提交
  • s下事务内全部读写,均由读写锁控制,读被加"读锁",写被加"写锁"
  1. MVCC下,普通查询与更新,访问的不是同一数据,不会产生读写阻塞现象
  2. 快照读:查询到的是某个历史版本数据,这个数据版本不会被更新,可以肆无忌惮的读取,也不会加锁,不会阻塞;当前读:会加"写锁"试图读取最新数据版本,遇到其他锁存在则阻塞
参考资料

MySQL实战45讲
InnoDB并发如此高,原因竟然在这?



来自为知笔记(Wiz)