默认使用MySQL 5.5后的InnoDB引擎。
ACID的I - “隔离性”。当db上有多事务同时执行时,可能出现如下问题:
为解决上述问题,隔离级别诞生了。级别越高,性能越低。因此要根据业务折中选择。
假设表T中仅一列,一行值1:
create table T(c int) engine=InnoDB;
insert into T(c) values(1);
事务 A | 事务 B |
---|---|
启动事务,查询得到值 1 | 启动事务 |
查询得到值 1 | |
将 1改成 2 | |
查询得到值 V1 | |
提交事务B | |
查询得到值 V2 | |
提交事务A | |
查询得到值 V3 |
下面具体来看不同隔离级别的事务A返回结果,即V1、V2、V3是什么。
实现上,db里会创建一个视图,访问时以视图的逻辑结果为准。
若想更改,可将启动参数transaction-isolation的值set成READ-COMMITTED。
可用show variables来查看当前值。
show variables like 'transaction_isolation'
比如银行账户表。一个表存每月月底的余额,一个表存账单明细。
事务启动时的视图可认为是静态的,不受其他事务更新影响。
MySQL的每条记录在更新时都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可得到前一个状态的值。
假设一个值从1被按顺序改成2、3、4
当前值4,但在查询这记录时,不同时刻启动事务有不同read-view。 在视图A、B、C,该记录的值分别是1、2、4,同一记录在系统中可存在多版本,即多版本并发控制(MVCC)。
对read-view A,要得到1,就必须将当前值依次执行图中所有的回滚操作得到。 即使现在有另外一个事务正在将4改成5,这个事务跟read-view A、B、C对应的事务是不会冲突的。
不需要时才删除。 即系统会自己判断,当没有事务再用到这些undo log,undo log就会被删除。
当系统里没有比该undo log更早的read-view时。
长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问db里的任何数据,所以该事务提交之前,db里它可能用到的回滚记录都必须保留,导致大量占存储。
在MySQL 5.5及以前,undo log是跟数据字典一起放在ibdata文件,即使长事务最终提交,回滚段被清理,文件也不会变小。
除了对回滚段影响,长事务还占用锁资源,可能拖慢全库。
开发同学并不是有意长事务,通常误用。其实MySQL的事务启动方式有以下几种:
begin 或 start transaction。配套的
set autocommit=0
将该线程的自动提交关闭。如果你只执行一个select,事务就启动了,且不会自动提交。 该事务会持续存在,直到主动执行commit 或 rollback,或断开连接。
有些客户端连接框架会默认连接成功后先执行
set autocommit=0
导致接下来的查询都在事务中,若是长连接,就导致意外的长事务。 因此建议总用
set autocommit=1
显式启动事务。
频繁事务的业务,第二种方式每个事务在开始时都不需要主动执行一次 “begin”,减少了语句交互次数。如果你也有这个顾虑,建议使用commit work and chain。
autocommit为1时,用begin显式启动的事务,若执行commit,则提交事务。 若执行 commit work and chain,则是提交事务并自动启动下个事务,省去执行begin语句的开销。从程序开发的角度也能明确知道每个语句是否处于事务。
information_schema库的innodb_trx表中查询长事务
select * from information_schema.innodb_trx where
TIME_TO_SEC(timediff(now(),trx_started))>60
好处是“最长恢复时间”更短。
系统的对应指标是RTO(恢复目标时间)。 当然这个是有成本的,因为更频繁全量备份需要消耗更多存储空间,所以这个RTO是成本换来的,需要根据业务评估。
确认是否使用
set autocommit=0
确认可在测试环境中,把MySQL的general_log开启,随便跑个业务逻辑,通过general_log确认。 一般框架如果设置该值,也会提供参数来控制,目标就是把它改成1。
确认是否有不必要的只读事务。有些框架不管什么语句先begin/commit框。有些是业务并没有这需要,但也把好几个select语句放到事务。这种只读事务可以去掉。
业务连接数据库时,根据业务预估,通过SET MAX_EXECUTION_TIME命令,控制每个语句执行最长时间,避免单语句意外执行太长时间。
监控 information_schema.Innodb_trx表,设置长事务阈值,超过就报警/或者kill。
Percona的pt-kill这个工具不错,推荐。
在业务功能测试阶段要求输出所有的general_log,分析日志行为提前发现问题。 使用的MySQL 5.6或更新版本,把innodb_undo_tablespaces设置成2或更大值。如果真的出现大事务导致回滚段过大,这样设置后清理起来更方便。