从前文,我们了解到有一部分存储引擎是支持事务的。本文我们将探讨事务的重要概念以及实现原理。
事务
ACID概念
ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性)
隔离性和隔离级别
SQL标准里定义了四种事务隔离级别:
- 读未提交:一个事务还没提交时,它做的变更就能被别的事务看到;
- 读提交:一个事务提交之后,它做的变更才会被其他事务看到;
- 可重复读:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的;
- 串行化:对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
四种隔离级别区别:
隔离级别 | 脏读可能性 | 不可重复读可能性 | 幻读可能性 | 加锁读 |
---|---|---|---|---|
读未提交 | Y | Y | Y | N |
读提交 | N | Y | Y | N |
可重复读 | N | N | Y | N |
串行化 | N | N | N | Y |
MySQL中的事务
MySQL提供了两种事务型存储引擎:InnoDB 和 NDB Cluster,而MyISAM并不支持事务。
MVCC与事务隔离实现
四种隔离级别的在InnoDB实现:
- 读未提交:直接返回记录上的最新值,没有视图概念;
- 读提交:在每个SQL语句开始执行的时候创建视图;
- 可重复读:在事务启动时创建视图,整个事务存在期间都用这个视图;(注意,InnoDB利用MVCC已经在可重复读隔离级别下避免了幻读问题!!注意和上边的表格内容区分清楚!!!)
- 串行化:隔离级别下直接用加锁的方式来避免并行访问。
这个所谓的视图其实是通过多版本并发控制(MVCC)来实现。(详情见《高性能MySQL》第三版 1.4 多版本并发控制)
事务的启动
事务启动方式:
- 1.显式启动事务语句, begin 或 start transaction。配套的提交语句是 commit,回滚语句是 rollback。
- 2.set autocommit=0,这个命令会将这个线程的自动提交关掉(注意,仅影响当前线程。)。这意味着如果你只执行一个 select 语句,这个事务就启动了,而且并不会自动提交。这个事务持续存在直到你主动执行 commit 或 rollback 语句,或者断开连接。一般建议总是使用 set autocommit=1, 然后通过显式语句的方式来启动事务。
长事务会导致数据库空间,并且占用所资源,生产中应该尽量避免长事务。查询长事务方法:
// 查询超过60秒的事务
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60
查看修改事务隔离级别
MySQL默认的事务隔离级别是可重复读(REPEATABLE READ)。
可以通过sql查看MySQL事务隔离级别:
show variables like "%isolation%";
MySQL提供了SET TRANSACTION语句,该语句可以改变单个会话或全局的事务隔离级别。语法格式如下:
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
其中,SESSION 和 GLOBAL 关键字用来指定修改的事务隔离级别的范围:
- SESSION:表示修改的事务隔离级别将应用于当前 session(当前 cmd 窗口)内的所有事务;
- GLOBAL:表示修改的事务隔离级别将应用于所有 session(全局)中的所有事务,且当前已经存在的 session (包括当前session)不受影响;
- 如果省略 SESSION 和 GLOBAL,表示修改的事务隔离级别将应用于当前 session 内的下一个还未开始的事务。
任何用户都能改变会话的事务隔离级别,但是只有拥有 SUPER 权限的用户才能改变全局的事务隔离级别。
其他注意
- 更新数据都是先读后写的,而这个读,只能读当前的值,称为“当前读”(current read)。具体见:08 | 事务到底是隔离的还是不隔离的?
延伸阅读
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 duval1024@gmail.com