MySQL 的 SELECT 默认就是无锁的,但得看隔离级别 开门见山,先说一个核心事实:在 READ COMMITTED 和 REPEATABLE READ 这两个最常用的隔离级别下,MySQL 里一个普通的 SELECT 查询(不带 FOR UPDATE 或 LOCK IN SHARE MODO
开门见山,先说一个核心事实:在 READ COMMITTED 和 REPEATABLE READ 这两个最常用的隔离级别下,MySQL 里一个普通的 SELECT 查询(不带 FOR UPDATE 或 LOCK IN SHARE MODODE 这类后缀),天生就是无锁的。它走的是 MVCC(多版本并发控制)这条“快车道”,不会去加行锁,自然也就不会阻塞其他会话的写操作。这并非什么需要特别配置的“高级功能”,而是 InnoDB 存储引擎基于 undo log 和 read view 机制的默认行为,是它的设计基石。
MySQL默认SELECT无锁,但隔离级别影响行为:RC和RR下走MVCC快照读,SERIALIZABLE则隐式加共享锁;当前读(如FOR UPDATE)会绕过MVCC加锁。

长期稳定更新的攒劲资源: >>>点此立即查看<<<
不过,这里有个常见的“坑”需要警惕。比如你执行一句 SELECT * FROM t WHERE id = 1,看起来人畜无害,对吧?但如果当前会话的隔离级别被设置成了 SERIALIZABLE,那么这条语句会立刻“变脸”,被隐式地转换为 SELECT ... LOCK IN SHARE MODE,从而加上共享锁,导致读写互斥。所以,在讨论“无锁”之前,务必先确认战场环境。
SELECT @@transaction_isolation,这是第一步。READ COMMITTED。为什么呢?因为 REPEATABLE READ 下,如果存在长事务,会拖慢 purge 线程清理旧版本数据的速度,间接影响 MVCC 的整体性能。SELECT 都会等价于加共享锁,MVCC 机制基本被放弃。除非有极其严格的串行化需求,否则一般不建议使用。既然默认无锁,那为什么有时候 SELECT 还是会引发锁等待,甚至把整个表锁住呢?关键在于理解 MVCC 的生效范围:它只对“快照读”有效。一旦你的查询语句触发了“当前读”,就会立刻绕过 MVCC 的温柔乡,直接去读取数据的最新版本,并且该加锁加锁,毫不含糊。
典型的“当前读”场景包括:
SELECT ... FOR UPDATE(加排他锁)、SELECT ... LOCK IN SHARE MODE(加共享锁)。UPDATE t SET x=1 WHERE y=2,在执行时,所有被 WHERE 条件匹配到的记录(注意,是匹配到的所有记录,无论最终是否真的被修改)都会被加上临键锁(Next-Key Lock)。这里有个容易踩的陷阱:一句简单的 SELECT ... WHERE non_unique_col = (在非唯一索引上做等值查询),在 REPEATABLE READ 隔离级别下,就可能触发间隙锁。你的本意可能只是“看看有没有这条数据”,结果却意外地阻塞了其他会话向这个间隙插入新数据的操作。
MySQL 并没有提供一个直接的命令,像魔法水晶球一样告诉你“本次查询用了 MVCC”。但我们可以通过一些组合手段来侧面验证:
SET GLOBAL general_log = ‘ON’;,然后去查看日志文件。如果在 SELECT 语句的日志附近,看到了 lock_mode X(排他锁模式)或 lock_mode S(共享锁模式)这样的字样,那就说明这条查询发起了“当前读”,没有走纯 MVCC 路径。SHOW ENGINE INNODB STATUS\G 命令,重点关注输出结果中的 TRANSACTIONS 部分。观察你的事务持有锁的类型和数量,对比执行查询前后的变化。BEGIN; UPDATE t SET a=1 WHERE id=1;(先不提交)。然后在会话 B 中执行:SELECT * FROM t WHERE id=1;。如果会话 B 立刻返回了修改前的旧值,恭喜,它成功走了 MVCC 快照读。如果会话 B 的执行卡住了,一直在等待,那很可能是因为会话 A 持有的锁,与会话 B 发起的“当前读”请求冲突了。最后,我们来聊聊 MVCC 的“代价”。很多人认为无锁查询是免费的午餐,其实不然。考虑一个典型场景:SELECT * FROM t ORDER BY id LIMIT 1000000, 20。这条语句本身不加锁,但为了给你返回第 1000000 行开始的 20 条数据,MVCC 机制需要为前 1000020 行数据逐一回溯 undo log,判断它们在当前 read view 中的可见性。这个过程的 CPU 和 buffer pool 压力不容小觑。
尤其是在高并发环境下,如果还存在长事务,问题会更严重。老旧的 read view 会阻止 undo log 中被标记删除的旧版本数据被及时清理,导致 innodb_history_list_length 这个指标持续升高,最终拖慢整个数据库实例的性能。
LIMIT offset, N 这种写法。改用游标式分页(或称“seek method”):WHERE id > last_seen_id ORDER BY id LIMIT 20。SHOW GLOBAL STATUS LIKE ‘Innodb_history_list_length’;。如果这个值长期高于 10000,就需要引起警觉,检查是否有长事务未提交。说到底,MVCC 是一种用空间(存储 undo log)、CPU(进行可见性判断)和内存(维护 read view、在 buffer pool 中保留老版本数据页)来换取高并发的技术方案。真正考验功力的,往往不是“如何开启它”,而是“在什么情况下,我们需要意识到它的代价,并做出合理的设计与规避”。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述