暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

为什么不要使用长事务?

码上记录 2021-06-21
3364

长事务意味着系统里存在很老的事务视图。由于这些事务随时可能访问数据库里的任何数据,所以这个事务提交之前,数据库里它可能用到的回滚记录都必须保留,这就会导致占用大量的存储空间。


在MySQL5.5以及之前的版本,回滚日志是和数据字典一起放在ibdata文件里的。即使长事务最终提交,回滚段被清理,文件也不会变小。有可能出现数据不大,但是回滚段很大的情况。


除了对回滚段的影响,长事务还占用锁资源,也可能拖垮整个库。


那么怎么避免使用长事务呢?


我们先来看一下MySQL事务启动的方式。


1 显示的启动事务,启动语句是 begin 或 start transaction,提交语句是commit,回滚语句是rollback


2 set autocommit=0  这个命令会将线程的自动提交关掉。这意味着如果你只执行一个select 语句,这个事务就启动了,而且不会自动提交。这个事务会持续存在直到你主动执行commit或rollback语句,或者断开连接。


有的客户端连接框架会默认连接成功后先执行一个set  autocommit=0的命令。这样做的好处是每个事务开始时都不需要主动执行一次begin,减少了语句的交互次数。但同时也导致接下来的查询都在事务中,如果是长连接,就导致了意外的长事务。


想要避免长事务,可以使用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


    此外从应用开发和数据库两个角度还可以采用下面这些措施来避免长事务:


    从应用开发角度来看:

    1 通过MySQL的general_log来确认数据库连接框架设置的autocommit是几,能否改为1

    2 是否有不必要的只读事务,能否去掉

    3 根据业务的具体情况,设置 MAX_EXECUTION_TIME来控制单个语句执行的最长时间,避免单个语句意外执行太长时间


    从数据库来看:

    1 监控 information_schema.Innodb_trx 表,设置长事务阈值,超过就报警 或者 kill

    2 必要时使用Percona 的 pt-kill 来杀掉长事务

    3 测试阶段输出所有的general_log,分析日志行为提前发现问题

    如果使用的是 MySQL 5.6 或者更新的版本,把 innodb_undo_tablespaces 设置成 2(或更大的值)。这样设置后,如果出现大事务导致回滚段过大,清理起来更方便。


    参考资料:MySQL实战45讲

    文章转载自码上记录,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

    评论