欢迎你们前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~mysql
提示:公众号展现代码会自动折行,建议横屏阅读。数据库
订座在现实生活中是一种很常见的场景,比较常见的有火车票席位选择,电影院席位选择等等。那么如何实现订座功能呢?应用程序可能有不少种不一样的实现方式,固然,确定离不开数据库。这里将介绍一种纯数据库的实现方式。微信
设想咱们有一张座位表以下:并发
CREATE TABLE seats ( seat_no INT PRIMARY KEY, booked ENUM('YES', 'NO') DEFAULT 'NO') ENGINE=InnoDB;
表中有100个席位,从0到99。例如咱们要预约席位2,3,咱们能够先开启事务,锁定席位:mvc
START TRANSACTION;SELECT * FROM seats WHERE seat_no IN (2,3) AND booked = 'NO' FOR UPDATE;
SELECT… FOR UPDATE语句返回结果有以下三种状况:函数
状况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。当结果集不为空时,返回的席位即被锁定成功。ui
SELECT * FROM seats WHERE seat_no IN (2,3) AND booked = 'NO'FOR UPDATE SKIP LOCKED;
NOWAIT的意思是若是碰到被其余事务锁定的席位,不等待并直接返回错误。使用以下NOWAIT语句进行席位锁定,那么返回结果集2和3,要么返回错误。rest
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;
在InnoDB中,实现SKIP LOCKED/NOWAIT具体实现以下:
SKIP LOCKED/NOWAIT能够很是高效地实现订座这个场景,做为InnoDB部分(WL#8919: InnoDB: Implement NOWAIT and SKIP LOCKED)的原做者,我也期待着你们来分享该功能更多的使用场景。
腾讯数据库技术团队对内支持微信红包,彩票、数据银行等集团内部业务,对外为腾讯云提供各类数据库产品,如CDB、CTSDB、CKV、CMongo, 腾讯数据库技术团队专一于加强数据库内核功能,提高数据库性能,保证系统稳定性并解决用户在生产过程当中遇到的问题,并对生产环境中遇到的问题及知识进行分享。
问答
相关阅读
此文已由做者受权腾讯云+社区发布,原文连接:https://cloud.tencent.com/developer/article/1163316?fromSource=waitui
欢迎你们前往腾讯云+社区或关注云加社区微信公众号(QcloudCommunity),第一时间获取更多海量技术实践干货哦~
海量技术实践经验,尽在云加社区!