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

8次阅读

MySQL 并发访问核心是读写一致性保障,InnoDB 通过 MVCC+ 行级锁 + 可重复读隔离级别协同实现读不阻塞写、写不阻塞读,但写写互斥;需重点关注写冲突、锁范围、事务生命周期及乐观锁重试机制。

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

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

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

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

它就等于:你写的代码里 SELECTUPDATE 同时跑在不同线程 / 请求里,还可能操作同一张表、甚至同一行。

  • 读 - 读:安全,完全 无锁(比如 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,说明已被别人抢先更新,此时应重新查最新 stockversion,再试一次。

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

你设了 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 是隐形的保护伞,锁是显性的刹车片,而事务边界,是你唯一能亲手划清的防线。

text=ZqhQzanResources