事务的概念
事务就是是要保证一系列操作要么全部成功,要么全部失败。
在MySQL中,事务是在引擎层实现的。MySQL 的 MyISAM 引擎不支持事务,这也是 MyISAM 被 InnoDB 取代的原因之一。
事务的特性是ACID:Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性。
多个事务同时执行产生的问题
当多个事务同时执行时,可能会出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)
脏读 | 不可重复读 | 幻读 |
---|---|---|
当前事务读取到了其他事务未提交或回滚的数据修改并使用 | 当前事务多次操作某一数据时,其他事务对该数据做了修改,导致当前事务产生不可重复读 | 当前事务按照相同的查询条件重新检索之前检索过的数据,发现其他数据插入了满足查询条件的其他数据 |
读未提交 | 读未提交,读提交 | 读未提交,读提交,可重复读 |
事务的隔离级别
- 读未提交(read uncommitted)一个事务还没被提交时,其他事务可以看到它所做的更改
- 读提交(read committed)一个事务提交之后,其他事务才可以看到更改
- 可重复读 (repeatable read) 一个事务在执行过程中看到的数据,总是跟这个事务启动时看到的数据一致。未提交的变更,其他事务也是不可见的。
- 串行化(serializable) 对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突时,后访问的事务必须等前一个事务执行完成才可以继续。
几种隔离级别的隔离性依次上升,效率依次下降。
配置MySQL隔离级别的方式是将参数 transaction-isolation 的值设置成 READ-COMMITTED。
show variables like 'transaction_isolation';
事务隔离的实现原理
例如可重复度:在 MySQL 中,实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值。
并发版本控制(MVCC)
不同时刻启动的事务会有不同的 read-view。如图,在视图ABC中,一个记录的值各有不同,这就是数据库的多版本并发控制(MVCC)。
回滚日志在不需要的时候才会删除,系统会判断,没有事务在需要用到这些回滚日志时才会删除。
什么时候才不需要了呢?就是当系统里没有比这个回滚日志更早的 read-view 的时候。
由此可见,长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间。
除了对回滚段的影响,长事务还占用锁资源,也可能拖垮整个库。
如何避免长事务
事务的启动方式
在MySQL中,事务的启动方式有这几种:
- 显式启动事务语句,begin 或 start transaction。配套的提交语句是 commit,回滚语句时 rollback;
- set autocommit = 0,这个命令会将这个线程的自动提交关掉。意味着如果你只执行一个 select 语句,这个事务就启动了,而且并不会自动提交。这个事务持续存在直到你主动执行 commit 或 rollback 语句,或者断开连接。
有些客户端连接框架会默认连接成功后先执行一个 set autocommit=0 的命令。这就导致接下来的查询都在事务中,如果是长连接,就导致了意外的长事务。
所以日常开发中最好总是使用 set autocommit=1, 通过显式语句的方式来启动事务。
在 autocommit 为 1 的情况下,用 begin 显式启动的事务,如果执行 commit 则提交事务。如果执行 commit work and chain,则是提交事务并自动启动下一个事务,这样也省去了再次执行 begin 语句的开销。同时带来的好处是从程序开发的角度明确地知道每个语句是否处于事务中。
你可以在 information_schema 库的 innodb_trx 这个表中查询长事务,比如下面这个语句,用于查找持续时间超过 60s 的事务。
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60```
#### 小结
事务的特性是ACID:Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性。
SQL 标准的事务隔离级别有:读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(serializable )。
开发中尽量避免长事务。
网友评论