MySQL数据库——事务(隔离级别是重点)

事务是访问并更新数据库各种数据项的一个程序执行单元,可以是一条语句,也可以是一组语句。

ACID(原子性、一致性、隔离性、持久性)

原子性:其中的操作要么都做,要么都不做。undolog(rollback)要么都执行要么都不执行。

一致性:满足某个状态,数据就是一致的。不满足某个状态,数据就是不一致的。代码层面保证。

隔离性:多个事务并发执行的时候,事务内部的操作和其他事务是隔离的,并发执行的各个事务之间不能互相干扰。锁和MVCC。

持久性:事务一旦提交,它对数据库的改变就应该是永久性的。持久化+redolog。

 

事务并发可能会出现的问题如下。

脏读:事务A在读取数据data,此时事务B在修改数据data,但是事务B没有提交,却被事务A读取出来了。

不可重复读:事务A在一个范围内多次查询返回不同数据值,因为在这个过程中事务B一直在改变查询的数据,并提交。

幻读:事务A要读一个不存在的主键为id的数据,此时事务B正在插入这条数据。插入时会出现冲突,读取不到记录却发现主键冲突,是因为记录实际上已经被其他事务插入了,但当前事务不可见。

 

关于隔离性。当数据库多事务同时执行,可能出现脏读、不可重复读、幻读的问题。隔离级别用来解决这些问题。

隔离越好,效率越低。事务隔离级别有:读未提交、读已提交、可重复读、串行化。

 

读未提交是事务还没提交,就能被其他事务看到,会有脏读问题。

读已提交是事务提交之后,才能被其他事务看到,会有不可重复读问题。(Oracle和微软的默认)

可重复读是事务执行过程中和刚启动时保持一致,即同一事务(函数段)的多个实例在并发读取数据时会看到同样的数据行,解决了不可重复读的问题。(InnoDB默认,两个事务彻底隔离)会有幻读问题。

串行化,可以对同一行数据加读锁和写锁。当读写锁冲突的时候,后一个事务要等前一个事务完成才能执行。

在隔离级别的实现方面,视图是一个关键部分,访问的时候以视图逻辑结果为准。(读不加锁,多版本并发控制MVCC)

比如可重复读,就是视图在事务启动时创建,整个事务存在期间都用这个视图。(数据核对场景,多版本并发控制MVCC)

比如读已提交,就是在每个SQL语句开始执行的时候创建视图。

读未提交是直接返回记录的最新值,没有视图的概念。

串行化是直接用加锁的方式避免并行访问。

 

mvcc多事务并发控制,能够提升并发性能。对于高并发场景,mvcc比行级锁开销更小。

mvcc的实现依赖于版本链,版本链是通过表的三个隐藏字段实现的。

DB_TRX_ID是当前事务的id,通过事务id的大小判断事务的时间顺序。

DB_ROLL_PTR是回滚指针,指向前一个版本,通过这个把多版本连接在一起形成undo log版本链。

DB_ROW_ID是主键。

更新的时候:首先用排他锁锁住该行,然后将该行原本的值拷贝到undo log,作为旧版本用于回滚,然后修改当前行的值,生成一个新版本,更新事务id,使回滚指针指向旧版本,形成一条版本链。

mvcc是通过read view和版本链实现的,版本链保存了历史版本记录,通过read view判断当前版本的数据是否可见。如果不可见,再从版本链中找到上一个版本,继续进行判断,直到找到一个可见的版本。

 

读取表有两种方式,一种是快照读,另一种是当前读。

快照读:读取的是快照版本,是通过mvcc来进行并发控制的,不用加锁。比如select。

当前读:读取的是最新版本,比如下面的一些指令。
update
delete
insert
select … lock in share
select … for update

关于幻读:在快照读的情况下,可以通过mvcc避免幻读现象。但是当前读还是会出现幻读现象,因为每次读取的都是最新数据,如果查询中有其他事务插入,就会出现幻读,因此可以通过next-key(行锁和间隙锁一起)来解决幻读。串行隔离级别也可以避免幻读,但是会锁住整张表,并发性很低,一般不会使用。

select…lock in share mode是共享锁。
select…for update是排他锁。
共享锁在多个事务同时更新同一表单的时候很容易造成死锁。

使用排他锁的时候,其他线程对这些数据的更改或删除操作会被阻塞(只能读操作),直到该事务被commit语句或rollback语句结束。for update只用于innodb,根据主键查询如果条件是like或不等于,直接产生表锁;根据非主键查询,会产生表锁。

 

在MySQL中,每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值可以通过回滚日志得到前一个状态的。

长事务是运行事件比较长并且长时间没有提交的事务。尽量不要使用长事务,因为系统里面会存在很老的事务视图,回滚日志回滚记录占用资源,长事务还造成阻塞且占用锁资源。

关于MySQL的事务启动机制。可以显示地启动事务set autocommit=1,begin或者start transaction,然后用commit提交,用rollback回滚。autocommit=0是自动启动事务,这会导致如果接下来的查询都在事务而且都是长连接,就会导致意外的长事务。

因此一般建议使用set autocommit=1。有人觉得麻烦也可以使用commit work and chain,这条提交了就自动执行下一条不是长事务的。可以在information_schema库的innodb_trx表中查询长事务,比如查询持续事件超过60秒的事务。

 

posted @   花与不易  阅读(44)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
历史上的今天:
2020-03-06 阶段性回顾总结与计划(☆_☆)
点击右上角即可分享
微信分享提示