文章目录
第三讲 MySQL 中的隔离性与隔离等级
在数据库管理系统中,事务的隔离性是一个关键概念,它确保了在并发环境下数据的一致性和正确性。MySQL 作为广泛使用的关系型数据库管理系统,提供了不同的隔离等级来满足各种应用场景的需求。本文将深入探讨 MySQL 的隔离性以及不同的隔离等级。
一、事务的隔离性概述
事务的隔离性是指一个事务的执行不能被其他事务干扰。也就是说,在事务执行过程中,数据的中间状态对其他事务是不可见的,只有当事务提交后,其结果才对其他事务可见。
隔离性的主要目标是防止出现以下问题:
- 脏读:一个事务读取了另一个未提交事务修改的数据。例如,事务 A 修改了一条数据但未提交,事务 B 读取了这条被修改的数据。如果此时事务 A 回滚,事务 B 读取到的数据就是无效的,这种情况称为脏读。
- 不可重复读:在一个事务中,多次读取同一数据集合的结果不一致。这可能是因为在事务执行过程中,其他事务对数据进行了修改并提交。例如,事务 A 读取了一条数据,然后事务 B 修改了这条数据并提交,当事务 A 再次读取这条数据时,得到的结果与第一次不同,这种情况称为不可重复读。
- 幻读:一个事务在执行过程中,发现满足某个条件的记录数量发生了变化。这通常是由于其他事务在该事务执行过程中插入或删除了满足条件的记录。例如,事务 A 查询满足某个条件的记录数量为 10,然后事务 B 插入了一条满足该条件的记录并提交,当事务 A 再次执行相同的查询时,发现记录数量变为 11,这种情况称为幻读。
二、MySQL 的隔离等级
MySQL 提供了四种隔离等级,分别是:
-
读未提交(Read Uncommitted):在这个隔离等级下,一个事务可以读取另一个未提交事务修改的数据。这意味着可能会出现脏读、不可重复读和幻读问题。
- 优点:允许事务读取最新的数据,提高了并发性能。
- 缺点:数据的一致性无法得到保证。
- 适用场景:对数据一致性要求不高,且需要高并发读取的场景,例如一些统计报表的生成。
-
读提交(Read Committed):在这个隔离等级下,一个事务只能读取另一个已提交事务修改的数据。这可以避免脏读问题,但可能会出现不可重复读和幻读问题。
- 优点:保证了数据的一致性,避免了脏读。
- 缺点:仍然可能出现不可重复读和幻读问题。
- 适用场景:对数据一致性有一定要求,但可以容忍不可重复读和幻读的场景,例如一些在线查询系统。
-
可重复读(Repeatable Read):在这个隔离等级下,一个事务在执行过程中多次读取同一数据集合的结果是一致的。这可以避免不可重复读问题,但可能会出现幻读问题。
- 优点:保证了数据的一致性,避免了不可重复读问题。
- 缺点:仍然可能出现幻读问题。
- 适用场景:对数据一致性要求较高,且需要多次读取同一数据集合的场景,例如一些财务系统。
-
串行化(Serializable):在这个隔离等级下,事务是串行执行的,一个事务在执行过程中会锁定它所访问的所有数据,直到事务提交。这可以避免脏读、不可重复读和幻读问题,但会极大地降低并发性能。
- 优点:保证了数据的绝对一致性,避免了所有并发问题。
- 缺点:并发性能非常低。
- 适用场景:对数据一致性要求极高,且可以接受低并发性能的场景,例如一些银行核心系统。
例子:
- 若隔离级别是“读未提交”, 则V1的值就是2。这时候事务B虽然还没有提交,但是结果已经被 A看到了。因此,V2、V3也都是2。 若隔离级别是“读提交”,则V1是1,V2的值是2。事务B的更新在提交后才能被A看到。所以, V3的值也是2。
- 若隔离级别是“可重复读”,则V1、V2是1,V3是2。之所以V2还是1,遵循的就是这个要求: 事务在执行期间看到的数据前后必须是一致的。
- 若隔离级别是“串行化”,则在事务B执行“将1改成2”的时候,会被锁住。直到事务A提交后, 事务B才可以继续执行。所以从A的角度看, V1、V2值是1,V3的值是2。
三、设置隔离等级
在 MySQL 中,可以通过以下方式设置隔离等级:
-- 设置隔离等级为读提交
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 开始一个事务
START TRANSACTION;
-- 执行一些 SQL 语句
SELECT * FROM table_name;
-- 提交事务
COMMIT;
可以根据实际需求选择合适的隔离等级。在设置隔离等级时,需要权衡数据一致性和并发性能之间的关系。
隔离等级的选择策略
选择合适的隔离等级需要考虑以下几个因素:
- 数据一致性要求:如果对数据一致性要求非常高,那么应该选择较高的隔离等级,如可重复读或串行化。如果对数据一致性要求不高,可以选择较低的隔离等级,如读未提交或读提交。
- 并发性能要求:较高的隔离等级会降低并发性能,因为事务需要等待其他事务释放锁。如果对并发性能要求较高,可以选择较低的隔离等级。
- 应用场景:不同的应用场景对隔离等级的要求也不同。例如,在线查询系统可能更注重读取数据的及时性,可以选择读提交隔离等级;而财务系统可能更注重数据的一致性,可以选择可重复读隔离等级。
四、事务隔离实现(以 “可重复读” 为例)
- 多版本并发控制(MVCC)
- 每条记录更新时会同时记录一条回滚操作,记录上的最新值可通过回滚操作得到前一个状态的值。例如值从 1 按顺序改成 2、3、4,回滚日志中会记录相应回滚操作,不同时刻启动的事务会有不同的 read - view,同一条记录在系统中可存在多个版本。
- 回滚日志删除时机
- 当系统里没有比该回滚日志更早的 read - view 时,回滚日志会被删除。
长事务风险
- 长事务意味着系统存在很老的事务视图,事务提交前,其可能用到的回滚记录都必须保留,导致大量占用存储空间。在 MySQL 5.5 及以前版本,回滚日志在 ibdata 文件中,长事务提交后文件也不会变小。此外,长事务还占用锁资源,可能拖垮整个库。
五、事务启动方式
- 显式启动事务语句
- 用 begin 或 start transaction 启动事务,配套提交语句是 commit,回滚语句是 rollback。
- set autocommit = 0
- 该命令会关掉线程自动提交,执行一个 select 语句事务就启动,且不会自动提交,事务持续到主动执行 commit 或 rollback 语句或断开连接。部分客户端连接框架默认执行此命令,可能导致意外长事务,建议使用 set autocommit = 1,通过显式语句启动事务。若担心频繁使用事务时 “多一次交互” 问题,可使用 commit work and chain 语法,在 autocommit 为 1 时,用 begin 显式启动事务,执行 commit work and chain 可提交事务并自动启动下一个事务。
长事务查询
可在 information_schema 库的 innodb_trx 表中查询长事务,如查询持续时间超过 60s 的事务:select * from information_schema.innodb_trx where TIME_TO_SEC (timediff (now (),trx_started))>60。
六、总结
MySQL 的隔离性和隔离等级是保证数据一致性和并发性能的重要机制。了解不同的隔离等级以及它们的特点和适用场景,可以帮助我们在实际应用中选择合适的隔离等级,以满足数据一致性和并发性能的需求。同时,在设置隔离等级时,需要根据具体情况进行权衡,以找到最佳的解决方案。