MySQL 事务
什么是事务?
事务是用来保证一组数据库操作,要么成功,如果某点失败则回滚所有操作。MySQL默认是自动提交的,每个数据操作都会当作一个事务。通过 autocommit 设置
ACID
事务的特性
- 原子性 atomicity
一个事务看做一个原子,作为一个完整的最小工作单元,只有两种结果,要么成功,要么失败,成功则提交,失败则回滚事务内所有操作。 - 一致性 consistency
数据库的实际数据是一致的,即在事务成功提交前,外部看数据库数据是没有变化的。 - 隔离性 isolation
事务在成功提交之前,对其他事务是不可见的。 - 持久性 durability
事务提交成功,数据的修改就会记录到数据库中。即使系统崩溃,数据也不会丢失。
在MySQL中,事务是在存储引擎层实现的。常见额InnoDB就是支持事务的
隔离级别
在特性ACID中,可见两个事务之间是相互隔离的,但在并发时隔离可能会造成数据的一些问题。比如对库存表同时有减库存的操作,开始查时都返回还有剩余库存,但最后执行却只有一个能成功提交,与前面查询结果判断不符。所以对隔离的级别做了配置:
- 未提交读(read uncommitted)
即事务还没提交,但他已做的修改是能被其他事务看到的。这种很可能读到未提交的数据,即脏读 - 提交读(read committed)
即事务提交后,他的修改才会被其他事务看到。大多引擎都默认这种级别 - 可重复读(repeatable read)
意为在事务执行的过程中,其他事务即使提交成功了,也看不到新的修改,InnoDB默认这一级别 - 可串行化(serializable)
最高的隔离级别。对同一行数据在一个事务操作时会加锁,其他事务要操作此行数据需要等待当前事务执行完成。此级别确保了数据的一致性,但在面对并发时造成了阻塞
隔离级别 | 脏读 | 不可重复读 | 幻读 | 加锁读 |
---|---|---|---|---|
未提交读 | Y | Y | Y | N |
提交读 | N | Y | Y | N |
可重复读 | N | N | Y | N |
可串行化 | N | N | N | Y |
通过 set transaction_isolation 命令来调整隔离级别
死锁
指两个或多个事务在争夺同一资源时发生的相互等待的现象,如果没有外力作用,就会一直等待下去,造成死锁。
为了解决死锁,数据库有各种检测和超时机制。InnoDB是可以检测到事务的死锁,回滚一个或多个事务来解决,但有些场景不能检测到,如事务操作中涉及的表用了别的引擎,或者使用了 lock tables 的表锁定语句。这时只能通过超时机制 innodb_lock_wait_timeout,来解决死锁。
为了减少死锁的发生概率,一般在程序设计时:
- 类似的业务,尽可能按照相同的访问顺序访问
- 同一事务中,尽可能一次锁定需要的所有资源
- 同一事务中,不要使用不同的数据引擎的表
- 控制事务中涉及的资源量
- 容易产生死锁的业务尝试提升锁的粒度,行锁提升到表锁
事务日志
事务日志即事务操作的日志,它可以提高事务的效率和安全性
- 修改表数据时,先在内存中修改,再持久化到硬盘上的事务日志,再将内存中的数据慢慢刷到硬盘上,这种方式称为预写式日志。
- 提高了效率,因为日志是追加式的,也就是顺序IO,相较于随机IO快很多
- 提高了而安全性,在系统崩溃后,可以通过日志恢复数据