欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~

本文由腾讯数据库技术 发表于云+社区专栏

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

第一部分:SKIP LOCKED/NOWAIT订座功能实现

订座在现实生活中是一种很常见的场景,比较常见的有火车票席位选择,电影院席位选择等等。那么如何实现订座功能呢?应用程序可能有很多种不同的实现方式,当然,肯定离不开数据库。这里将介绍一种纯数据库的实现方式。

设想我们有一张座位表如下:

CREATE TABLE seats (seat_no INT PRIMARY KEY,booked ENUM('YES', 'NO') DEFAULT 'NO') ENGINE=InnoDB;
复制代码

表中有100个席位,从0到99。例如我们要预定席位2,3,我们可以先开启事务,锁定席位:

START TRANSACTION;SELECT * FROM seats WHERE seat_no IN (2,3) AND booked = 'NO' FOR UPDATE;
复制代码

SELECT… FOR UPDATE语句返回结果有如下三种情况:

  • 1.返回成功,并且结果集包含2和3,那么说明锁定成功。我们可以之行下一步操作,等待支付完成,并更新席位状态并提交事务,订座完成。 UPDATE seats SET booked = 'YES' WHERE seat_no IN (2,3) COMMIT;
  • 2.返回成功,但结果集为空,或者只包含2或者3,那么说明锁定失败。
  • 3.很长时间不返回直到返回超时。比如席位2或者3已经被另一事务锁定,并且在等待支付完成或者发生其他情况,导致该事务一直未提交(commit)或者回滚(rollback)。返回超时默认需要等待50秒,我们可以通过修改innodb_lock_wait_timeout参数来配置合理的等待时间。超时之后返回的错误如下: ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

情况3对用户来说,意味着卡死,完全不能接受。为什么会发生等待?在InnoDB的锁系统(lock system)中,席位2如果被一个事务上了X(写锁)锁或者IX锁(意向更新锁),那么下一个事务要对席位2上X锁或者IX锁的事务,就要等待。这是由事务本身的特性(ACID)决定的。

那么是否有一种方法避免等待以及后续可能发生的超时呢?MySQL 8.0 提供的新功能SKIP LOCKED/NOWAIT就可以。 SKIP LOCKED的意思是跳过那些已经被其他事务锁定了的席位。使用如下SKIP LOCKED语句进行席位锁定,那么返回的结果集可能为空,2或3,2和3。当结果集不为空时,返回的席位即被锁定成功。

SELECT * FROM seats WHERE seat_no IN (2,3) AND booked = 'NO'FOR UPDATE SKIP LOCKED;
复制代码

NOWAIT的意思是如果碰到被其他事务锁定的席位,不等待并直接返回错误。使用如下NOWAIT语句进行席位锁定,那么返回结果集2和3,要么返回错误。

SELECT * FROM seats WHERE seat_no IN (2,3) AND booked = 'NO'FOR UPDATE NOWAIT;
复制代码

如果返回错误,如下:

ERROR 3572 (HY000): Do not wait for lock.
复制代码

如果成功锁定两个席位,通过如下语句查询锁系统的状态:

SELECT thread_id, object_name, lock_type, lock_mode, lock_data, lock_status FROM performance_schema.data_locks;+-----------+-------------+-----------+-----------+-----------+-------------+| thread_id | object_name | lock_type | lock_mode | lock_data | lock_status |
+-----------+-------------+-----------+-----------+-----------+-------------+|        43 | seats       | TABLE     | IX        | NULL      | GRANTED     |
|        43 | seats       | RECORD    | X         | 2         | WAITING     |
|        42 | seats       | TABLE     | IX        | NULL      | GRANTED     |
|        42 | seats       | RECORD    | X         | 2         | GRANTED     |
|        42 | seats       | RECORD    | X         | 3         | GRANTED     |
+-----------+-------------+-----------+-----------+-----------+-------------+
复制代码

SKIP LOCKED还可以很方便的用来进行随机分配席位。例如我们只需要锁定两个空的席位就可以通过如下语句实现。

SELECT * FROM seats WHERE booked = 'NO' LIMIT 2 FOR UPDATE SKIP LOCKED;
复制代码

SKIP LOCKED/NOWAIT功能只针对行锁(record lock),不包括表锁(table lock),元数据锁(metadata lock/MDL)。因此,带有SKIP LOCKED/NOWAIT的查询语句依然可能会因为表锁或元数据库锁而阻塞。元数据锁是MySQL Server层用来保护数据库对象的并发访问的一致性而创建的,数据库对象不仅包括表,同时包括库,函数,存储过程,触发器,事件等等。表和行锁是InnoDB存储引擎内部为了保证事务的一致性而创建的不同粒度的锁。

另外,SKIP LOCKED/NOWAIT还可以配合FOR SHARE使用,并且可以与单表绑定。例如:

SELECT seat_noFROM seats JOIN seat_rows USING ( row_no )WHERE seat_no IN (2,3) AND seat_rows.row_no IN (12)AND booked = 'NO'FOR UPDATE OF seats SKIP LOCKEDFOR SHARE OF seat_rows NOWAIT;
复制代码

第二部分:SKIP LOCKED/NOWAIT在InnoDB中的代码实现

在InnoDB中,实现SKIP LOCKED/NOWAIT具体实现如下:

  • 1.增加新的查询模式 enum select_mode { SELECT_ORDINARY = 0, /* default behaviour / SELECT_SKIP_LOCKED, / skip the row if row is locked / SELECT_NO_WAIT / return immediately if row is locked */ };
  • 2.在查询开始前,设置查询模式 ha_innobase::store_lock(): /* Set select mode for SKIP LOCKED / NO_WAIT */ switch (lock_type) { case TL_READ_SHARED_SKIP_LOCKED: case TL_WRITE_SKIP_LOCKED: m_prebuilt->select_mode = SELECT_SKIP_LOCKED; break; case TL_READ_SHARED_NO_WAIT: case TL_WRITE_NO_WAIT: m_prebuilt->select_mode = SELECT_NO_WAIT; break; default: m_prebuilt->select_mode = SELECT_ORDINARY; break; }
  • 3.上锁函数中,如果记录已被锁定,针对对不同查询模式进行相应处理: lock_rec_lock_slow(): if (wait_for != NULL) { switch (sel_mode) { case SELECT_SKIP_LOCKED: err = DB_SKIP_LOCKED; break; case SELECT_NO_WAIT: err = DB_LOCK_NOWAIT; break;
  • 4.查询中对上锁结果进行处理: row_search_mvcc(): case DB_SKIP_LOCKED: goto next_rec; 对DB_LOCK_NOWAIT的处理则是回滚当前语句(statement),见函数row_mysql_handle_errors()。
  • 5.二级索引(secondary index)的处理 在InnoDB中,对表中记录的锁定分两种情况。第一种是查询使用是聚集索引(cluster index),那么直接对聚集索引的记录上锁;第二中是查询使用的是二级索引,那么首先对二级索引的记录上锁,然后根据二级索引的记录,找到对应的聚集索引记录进行上锁。 所以,对于第一部分订座的席位表中,如果存在二级索引,对于锁定表中一条记录而言,最终锁定成功与否,还是以锁定聚集索引记录为准。

SKIP LOCKED/NOWAIT可以非常高效地实现订座这个场景,作为InnoDB部分(WL#8919: InnoDB: Implement NOWAIT and SKIP LOCKED)的原作者,我也期待着大家来分享该功能更多的使用场景。

参考链接:

  • 1.MySQL 8.0.1: Using SKIP LOCKED and NOWAIT to handle hot rows
  • 2.WL#3597: Implement NOWAIT and SKIP LOCKED
  • 3.WL#8919: InnoDB: Implement NOWAIT and SKIP LOCKED
  • 4.WL#6657: PERFORMANCE_SCHEMA, DATA LOCKS

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

问答

更新MySQL主键

相关阅读

Linux调度原理介绍和应用(前篇)

如何备份你的MySQL数据库

MySQL 8.0 版本功能变更介绍

此文已由作者授权腾讯云+社区发布,原文链接:https://cloud.tencent.com/developer/article/1163316?fromSource=waitui

欢迎大家前往腾讯云+社区或关注云加社区微信公众号(QcloudCommunity),第一时间获取更多海量技术实践干货哦~

海量技术实践经验,尽在云加社区!

如何在数据库中高效实现订座功能?相关推荐

  1. word中高效实现参考文献标注功能--自动更新--以word 2016为例

    word小白一个,平常都是用本办法解决的,写这篇文档的目的纯粹是为了以后方便自己查阅.我这里用的是word2016版本 灵活创建参考文献 首先光标选中要插入参考文献的地方,选中工具栏的引用,如图所示 ...

  2. MySQL的position值是什么_MySQL数据库中系统函数POSITION功能简介

    摘要: 下文讲述MySQL数据库中系统函数POSITION的功能说明,如下所示: 系统函数POSITION功能: 用于查找一个字符串在另一个字符串中第一次出现的位置 当无法在原始字符串中查找到子字符串 ...

  3. repeat mysql翻译_MySQL数据库中系统函数repeat有哪些功能呢?

    摘要: 下文讲述MySQL数据库中系统函数repeat的功能说明,如下所示: 系统函数repeat的功能: repeat函数的功能: 重复前面字符串指定次数 repeat语法: repeat(str, ...

  4. android 数据库 事务,Android数据库中事务操作方法之银行转账示例

    Android数据库中事务操作方法之银行转账示例 发布时间:2020-09-14 18:19:43 来源:脚本之家 阅读:79 作者:Qi_Yuan 本文实例讲述了Android数据库中事务操作方法之 ...

  5. Oracle数据库中stragg()函数,在瀚高数据库中如何替换使用?

    瀚高数据库 目录 环境 文档用途 详细信息 环境 系统平台:Microsoft Windows (64-bit) 10 版本:6.0 文档用途 用于解决怎么在瀚高数据库中实现和Oracle数据库中st ...

  6. 在SQL Server 2005数据库中进行错误捕捉

    在SQL Server数据库中,如果执行Transact-SQL时出现了错误,我们可以使用两种捕捉错误的方法解决此问题,一种是在客户端代码(如c#.delphi等)中使用类似try...catch的语 ...

  7. mysql substring用法_MySQL数据库中系统函数SUBSTRING功能简介

    摘要: 下文讲述MySQL数据库中系统函数SUBSTRING的功能说明,如下所示: 系统函数SUBSTRING的功能: SUBSTRING函数的功能: 用于从字符串的指定位置开始截取指定长度的字符串 ...

  8. imac mysql导入sql_iPhone 12 和全新 iMac 出现在数据库中

    相信大家对 EEC,也就是 Eurasian Economic Commission(欧亚经济委员会) 已经很熟悉了,最近几年,苹果所有未发布的新品都会提前出现在这个数据库中,所有使用加密功能的设备, ...

  9. 实现对mysql增删改查_Java语言实现对MySql数据库中数据的增删改查操作的代码

    简单说操作的步骤: 1.连接数据库 2.将SQL语句发送到数据库 3.执行SQL语句 这里举个例子: 在一个数据库中有个students表,表中有学号(Id),姓名(Name),性别(Sex),地址( ...

最新文章

  1. 【项目展示】一个有点难度的猜数字小游戏(Java编写)
  2. mysql.info文件是什么_info 数据库
  3. 5-spark学习笔记-spark集群应用与监控
  4. java动态打印_JFreeChart学习(三)——动态打印java内存使用情况
  5. leetcode27:移除元素(暴力+双指针)
  6. Cassandra,MongoDB,CouchDB,Redis,Riak,HBase比较
  7. python标准库对象导入语句_Python标准库之Sys模块使用详解
  8. 子主题function php,php – 带有依赖项的子主题
  9. 值得学习的100个网站推广方法。新站推广必备
  10. poj 1389 Area of Simple Polygons 线段树扫面线,和1151一样的嘛
  11. php中获取不到当前元素,PHP 中数组获取不到元素
  12. Linux TCP/IP 网络工具对比:net-tools 和 iproute2
  13. 用pcdet跑deecamp20_liadr3-1
  14. 让电脑自动开机、关机以及取消开机密码
  15. 大学生学剪辑蒙太奇技巧怎么用?
  16. zotero文献管理|chartero 插件 绝对是有一款让你离不开的插件,可视化你的文献阅读记录,提取PDF图片方便阅读
  17. Docker容器化技术教程,24小时快速入门
  18. 银行数字化转型导师坚鹏:基于招商银行案例研究的银行APP运营
  19. AWS云上基础服务体验课程
  20. kernel启动流程-start_kernel的执行_8.cpio initrd解包

热门文章

  1. SAP RETAIL商品主数据Basic Data视图里几个让人莫名惊诧的字段
  2. AI 崛起的第九个年头,还有哪些大有可为的地方?
  3. 如何利用AI语义分析,做产品需求分析(1)
  4. 精度 vs 效率:模型越小,精度就一定越低吗?
  5. 报告:AI技术正有效缩小亚洲医疗发展的差距
  6. 人工神经网络中为什么ReLu要好过于tanh和sigmoid function?
  7. 心得丨在人工智能领域, 开发人员需要什么技能?
  8. 干货丨2017年AI与深度学习要点大全
  9. 2021年14项世界互联网领先科技成果发布
  10. 浙江发布数字化改革标准化体系建设方案,将于2025年底建成