什么是mysql并发访问_mysql并发基础概念解析

来源:这里教程网 时间:2026-02-28 20:37:12 作者:

MySQL 并发访问不是“能不能同时连”,而是“多个连接同时读写同一份数据时,MySQL 怎么不搞错、不丢数据、不卡死”。

核心结论:InnoDB 默认用 MVCC + 行级锁 + 事务隔离级别(可重复读)协同工作,让读不阻塞写、写不阻塞读(大部分情况),但写写之间仍会互斥——这才是你实际编码时真正要盯住的边界。

什么是“并发访问”?别被术语绕晕

它就等于:你写的代码里

SELECT
UPDATE
同时跑在不同线程/请求里,还可能操作同一张表、甚至同一行。

读-读:安全,完全无锁(比如 100 个人查商品库存,没问题) 读-写:InnoDB 用 MVCC 隔离版本,通常不加锁(
SELECT
不会等
UPDATE
提交)
写-写:高危区!两个
UPDATE user SET balance = balance - 100 WHERE id = 1
可能互相覆盖,必须靠锁或事务兜底

为什么“select for update”不是万能解药?

很多人一遇到并发更新就加

SELECT ... FOR UPDATE
,结果发现性能暴跌、死锁频发——因为它本质是“先查再锁”,中间有时间窗口,且锁范围容易失控。

它只在当前事务内生效,不能防止其他事务在你
SELECT
之前就已持有该行锁
如果没走索引,InnoDB 会升级为表锁(
SELECT * FROM user FOR UPDATE
→ 整张表卡住)
嵌套事务或长事务中使用,锁会一直挂着,拖垮整个连接池 正确姿势:确保 WHERE 条件命中索引,并尽量缩短事务生命周期
START TRANSACTION;
-- 必须走主键或唯一索引,否则可能锁全表
SELECT balance FROM account WHERE user_id = 123 FOR UPDATE;
UPDATE account SET balance = balance - 50 WHERE user_id = 123;
COMMIT;

乐观锁怎么写才真“乐观”?

version
字段做乐观锁,不是加个字段就行——它只在“冲突概率低 + 更新逻辑简单”的场景下有效;一旦失败重试频繁,反而比悲观锁更耗资源。

必须在 UPDATE 的 WHERE 子句里校验
version
,漏掉就等于没锁
应用层需捕获影响行数为 0 的情况,并主动重试(不是抛异常就完事) 注意时钟/版本号生成方式:用数据库自增
version
比用
NOW()
更可靠
UPDATE product 
SET stock = stock - 1, version = version + 1 
WHERE id = 456 AND version = 2;

执行后若

ROW_COUNT() == 0
,说明已被别人抢先更新,此时应重新查最新
stock
version
,再试一次。

最容易被忽略的坑:隔离级别不是全局开关

你设了

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED
,不代表所有 SQL 都按这个跑——InnoDB 的 MVCC 行为和锁策略,还取决于语句类型、索引、是否在事务块里。

SELECT
单独执行 → 快照读(不加锁),走 MVCC
SELECT ... FOR UPDATE / LOCK IN SHARE MODE
→ 当前读(加锁),绕过 MVCC
即使在
READ COMMITTED
下,
UPDATE
依然会对匹配行加 X 锁,且锁到事务结束
线上误配成
REPEATABLE READ
后又用
SELECT ... FOR UPDATE
,可能触发间隙锁(Gap Lock),锁住不存在的记录范围,引发隐蔽死锁

真实并发问题从来不在“能不能连”,而在“谁改了什么、什么时候可见、锁住了谁、有没有漏判”。MVCC 是隐形的保护伞,锁是显性的刹车片,而事务边界,是你唯一能亲手划清的防线。

相关推荐