前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >详解Early Lock Release

详解Early Lock Release

作者头像
腾讯数据库技术
发布2019-05-16 15:04:09
1.4K0
发布2019-05-16 15:04:09
举报

提示:公众号展示代码会自动折行,建议横屏阅读

Early Lock Release 的原理


数据库领域存在很多优化措施(例如 group commit),它们很早就被提出来了,ELR 也不例外[1]。在多核处理器的时代,ELR 又被人发掘出来评估对系统性能的影响,例如[2]和[3]评估了包括 ELR 在内的多种性能优化措施。

在数据库中,典型的一个事务 t1 操作流程如下:

    0. begin transaction;

    1. search & lock tuple for update;

    2. update tuple & generate log;

    3. write commit log;

    4. flush logs;

    5. unlock tuples;

    6. commit & notify user;

容易发现,事务在写出并且持久化日志的时候会带来 IO,而 IO 通常而言比较耗时。如果事务中此前的操作都是内存操作的话(在内存数据库或 LSM 结构的系统中更明显),这些 IO 的相对耗时就会显得更大。IO 虽然可以做成异步的,但是在 IO 结束之前锁都仍然会被持有,从而会阻塞其他的并发事务。如果可以把第 5 步释放锁的操作提前,放到第 4 步刷出日志之前,则可以让并发操作同一记录(试图获得锁)的事务 t2 提前开始执行,从而可以增加系统的吞吐量。显然,第 6 步的 commit 操作不能提前,仍然必须等到日志持久化完成之后才能通知用户提交成功,因此用户事务的响应时间不会缩短。

unlock 提前会带来什么副作用吗?此时 t2 可以读取并修改 tuple 的内容,从数据依赖的角度看,t2 依赖了 t1。万一 t1 在提交的过程中(flush log 等操作)失败了,导致事务回滚,t2 也必须回滚(可以称之为:级联回滚)。更进一步,t2 此前读到的 t1 的数据实际上是脏数据(读到了未提交的数据,并且还在其上做了更新)。如果业务逻辑本来就是要读并且更新(例如在电商秒杀业务中,做减库存这样的热点行更新操作),脏读并不会造成实质上的不良后果。如果脏读并写了一条记录到另一个持久化的系统(例如消息队列中),则可能会带来业务上的副作用。

导致提交失败最可能的原因是写日志失败,例如日志盘满或坏了。随着云计算的流行,网络盘的使用非常普遍,写盘失败的概率可以认为是相对增加了。如果把数据库存在可拔插盘上(例如移动盘),拔插移动盘也容易触发写盘失败。如果日志文件设计成只有一个并确保总是按照 LSN 从小到大的顺序写出,在 t1 的日志写盘失败的时候,t2 必定也会失败。只不过在内存中事务已经提交了,对某些系统而言,要做回滚操作可能会很复杂或难以实现。最简单的策略反而是让系统自动 crash,系统重启的时候自然会去走崩溃恢复的逻辑。不过,提交失败的具体原因比较多,可能是盘满了、也可能是盘坏了等,需要针对性的处理,虽然要处理得很完善不那么容易[4]、[5]。

MySQL 作为最流行的开源数据库,自然不会放过这样一个经典的优化。下面就让我们来看一看它做了哪些尝试。

Server 层面的 ELR

Facebook 曾经在 MySQL 的 Server 层做过一个 ELR 的实现[6],来改进高并发场景下的热点行更新的系统性能。不过这个实现在被 port 到 MariaDB 后发现存在致命的缺陷[7],最后导致代码被回滚掉了。这个改进虽然失败了,但是其中的思考和经验教训8对我们理解 MySQL 以及 ELR 还是很有价值的。

众所周知,MySQL 在支持 binlog 的时候,事务提交要走一个内部的分布式事务(XA),执行 prepare()、写 binlog、commit() 三个步骤。这个改进试图把锁的释放提前到 prepare() 阶段,从而提高性能。按照前面一节的描述,实现时需要保证事务 t1、t2 等有依赖关系的事务之间的提交顺序,也需要保证在异常恢复时它们之间的顺序。

在事务 t1 提交失败时,mysqld 可以自杀。但是在异常恢复时,对于 prepared 但是没有 committed 事务,InnoDB 层是按照事务 ID 从大到小的顺序恢复的。而事务 ID 的分配并不等价于事务之间的依赖关系(事务 ID 可以在事务开始或首次进行写操作时分配),因此恢复的时候,事务的先后顺序可能会被搞反,从而导致数据错乱。如果不修改系统来记录事务的依赖关系,并因此修改对应的事务恢复逻辑,这个 Bug 就无法回避。考虑到修改的代价以及数据正确比性能更重要,这最终导致了 patch 被回滚。

InnoDB 层面的 ELR

下面以 MySQL 5.7.20 为例,分析一下 InnoDB 中事务提交时的基本调用流程。事务提交的主要函数为 trx0trx.cc 中的 trx_commit() 函数,它调用了 trx_commit_low(),后者又调用了 trx_commit_in_memory(),它们之间是一个线性关系,比较直接。

代码语言:javascript
复制
trx_commit() -> trx_commit_low() -> trx_commit_in_memory()

而 trx_commit_in_memory() 会先调用 lock_trx_release_locks(),再调用 trx_flush_log_if_needed()。(注,在 trx->flush_log_later 为 true 时,写日志的时机则还要往后一些。)

代码语言:javascript
复制
trx_commit_in_memory() -> lock_trx_release_locks(), trx_flush_log_if_need()

事务在内存中提交的时候,按照 WAL 的原则应该是先持久化日志,再释放锁。但是这里却是先释放锁,再刷日志。在 lock0lock.cc, lock_trx_release_locks() 函数中有一段有趣的注释:

代码语言:javascript
复制
 /* The following assignment makes the transaction committed in memory
    and makes its changes to data visible to other transactions.
    NOTE that there is a small discrepancy from the strict formal
    visibility rules here: a human user of the database can see
    modifications made by another transaction T even before the necessary
    log segment has been flushed to the disk. If the database happens to
    crash before the flush, the user has seen modifications from T which
    will never be a committed transaction. However, any transaction T2
    which sees the modifications of the committing transaction T, and
    which also itself makes modifications to the database, will get an lsn
    larger than the committing transaction T. In the case where the log
    flush fails, and T never gets committed, also T2 will never get
    committed. */
 /*--------------------------------------*/
    trx->state = TRX_STATE_COMMITTED_IN_MEMORY;
 /*--------------------------------------*/

注释说的就是我们在前面提到的脏读问题。那么 InnoDB 是怎么处理日志提交失败(这里只看写出日志失败)的呢?调用序列如下:

代码语言:javascript
复制
trx_flush_log_if_needed() -> trx_flush_log_if_needed_low() -> log_write_up_to() 
    -> log_write_flush_to_disk_low() -> fil_flush() -> os_file_fsync_posix()

可以看到, fil_flush() 最后会走到 os_file_fsync_posix()(这里以 Linux 为例)。最后这个函数是 fsync 的包装,而如果 fsync 失败了(例如遇到 EIO 等错误),它会 sleep,然后重试。而由于 fsync 本身实现的问题[5],重试后的 fsync 会成功返回。所以,写日志失败并不会立即 kill 掉 mysqld,因此可能会导致数据错误。后来的 MySQL 版本也已经修复了这个问题。

ELR 对性能的影响

从原理上看,ELR 可以改进系统的性能。参考文献[2]、[3]则给出了具体的测试验证。Facebook 也是因为性能才尝试在 Server 层做出改进。如果仅测试 InnoDB,它本身的实现也对性能有改善。可是在 MySQL 中因为对 binlog 的支持引入了内部的 XA 机制,将这些优化的效果给掩盖掉了。

References

代码语言:javascript
复制
1 Implementation Techniques for Main Memory Database Systems. https://15721.courses.cs.cmu.edu/spring2016/papers/p1-dewitt.pdf
2 Aether: A Scalable Approach to Logging. http://www.cs.albany.edu/~jhh/courses/readings/johnson.vldb10.logging.pdf
3 Improving OLTP concurrency through Early Lock Release. https://infoscience.epfl.ch//record/152158/files/ELR-single-column.pdf
4 PostgreSQL's fsync() surprise. https://lwn.net/Articles/752063/
5 PostgreSQL's handling of fsync() errors is unsafe and risks data loss at least on XFS. https://www.postgresql.org/message-id/flat/CAMsr%2BYHh%2B5Oq4xziwwoEfhoTZgr07vdGG%2Bhu%3D1adXx59aTeaoQ%40mail.gmail.com#CAMsr+YHh+5Oq4xziwwoEfhoTZgr07vdGG+hu=1adXx59aTeaoQ@mail.gmail.com
6 Implement release of row locks in InnoDB during prepare() phase. http://worklog.askmonty.org/worklog/Server-Sprint/?tid=163
7 –innodb-release-locks-early=1 breaks InnoDB crash recovery. https://bugs.launchpad.net/maria/+bug/798213
8 Tale of a Bug. https://kristiannielsen.livejournal.com/15893.html

腾讯数据库技术团队对内支持微信红包,彩票、数据银行等集团内部业务,对外为腾讯云提供各种数据库产品,如CDB、CTSDB、CKV、CMongo, 腾讯数据库技术团队专注于增强数据库内核功能,提升数据库性能,保证系统稳定性并解决用户在生产过程中遇到的问题,并对生产环境中遇到的问题及知识进行分享。

var first_sceen__time = (+new Date());if ("" == 1 && document.getElementById('js_content')) { document.getElementById('js_content').addEventListener("selectstart",function(e){ e.preventDefault(); }); } (function(){ if (navigator.userAgent.indexOf("WindowsWechat") != -1){ var link = document.createElement('link'); var head = document.getElementsByTagName('head')[0]; link.rel = 'stylesheet'; link.type = 'text/css'; link.href = "//res.wx.qq.com/mmbizwap/zh_CN/htmledition/style/page/appmsg_new/winwx45ba31.css"; head.appendChild(link); } })();

腾讯数据库技术

赞赏

长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

阅读

分享 在看

已同步到看一看

取消 发送

我知道了

朋友会在“发现-看一看”看到你“在看”的内容

确定

已同步到看一看写下你的想法

最多200字,当前共字 发送

已发送

朋友将在看一看看到

确定

写下你的想法...

取消

发布到看一看

确定

最多200字,当前共字

发送中

微信扫一扫 关注该公众号

微信扫一扫 使用小程序

即将打开""小程序

取消 打开

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-05-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 腾讯数据库技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 提示:公众号展示代码会自动折行,建议横屏阅读
  • Server 层面的 ELR
  • InnoDB 层面的 ELR
  • ELR 对性能的影响
  • References
    • 朋友会在“发现-看一看”看到你“在看”的内容
      • 朋友将在看一看看到
        • 发布到看一看
        相关产品与服务
        云数据库 SQL Server
        腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档