常见的数据库隔离级别以及企业里常用的是什么方案

基础知识:

MySQL 默认隔离级别 RR(可重复读),依靠MVCC 多版本并发控制避免普通幻读;如果是当前读(select ... for update 锁查询),依靠间隙锁 + 临键锁彻底解决幻读。只有最高隔离级别:串行化 (Serializable) 完全杜绝所有幻读。

  • 脏读
    • 事务 B:更新 money=500,未 commit
    • 事务 A:查询读到 money=500(读到未提交数据)
    • 事务 B:执行 rollback 回滚,数据变回 1000
    • 问题:A 拿到的 500 是脏数据,完全错误
  • 不可重复读(已有行的值被修改)
    • 事务 A:第一次查询 money=1000
    • 事务 B:更新 money=600,执行 commit 提交
    • 事务 A:再次查询,money=600
    • 问题:A 同一个事务,两次读同一行结果不一样,不可重复读取
  • 幻读(新增 / 删除了行)
    • 事务 A:select * from account where money < 2000,查到 1 条数据
    • 事务 B:插入一条 money=800 的新数据,commit
    • 事务 A:相同条件再次查询,查到 2 条数据
    • 问题:A 明明没操作,凭空多出一条数据,称为幻行

四种隔离级别:

1.读未提交(存在脏读、不可重复读、幻读)

2.读已提交(存在不可重复读、幻读)

3.可重复读(存在幻读)

4.串行化(三个都不存在)

mysql innodb默认可重复读,但是大厂为了提升并发性能,常设计为读已提交,由业务层都低处理重复读、幻读问题

MySQL RC 关闭间隙锁,只有记录锁,并发高,但无法靠数据库隔离级别规避上面不可重复读和幻读,全部交给业务层兜底,下面分通用方案 + 完整订单扣库存实战案例。

业务层通用兜底方案(大厂主流):

1.悲观锁方案(for update)

RC 下 select ... for update 加行排他锁,锁定当前行,其他事务阻塞更新/删除,规避不可重复读;

缺点:并发高容易锁等待,适合库存、订单扣减这类短事务。

2.乐观锁(版本号 version / 时间戳)

无锁,靠版本号控制更新,更新时校验版本不一致直接失败重试,互联网最常用。

3.唯一约束防幻读插入

幻读根源是并发插入,给关键字段加唯一索引,重复插入直接抛唯一冲突,捕获异常重试。

4.分布式锁(Redis/Zookeeper)

全局锁住资源,同一商品/订单同一时间只允许一个事务操作,彻底杜绝并发读写冲突。

5.最终一致性兜底(消息队列+对账)

业务操作+事务消息,定时对账补偿,即使并发出现数据不一致,后台定时任务修复。

6.避免长事务

RC 冲突概率和事务时长正相关,所有更新逻辑尽量缩小事务范围。

乐观锁示例:

  1. 查询当前商品库存、版本号
    sql
    SELECT stock, version FROM product_stock WHERE product_id = 1001;
  2. 判断库存是否充足,不足直接返回失败;
  3. 执行扣减,带上版本号做条件更新:
    sql
    UPDATE product_stock
    SET stock = stock - 1, version = version + 1
    WHERE product_id = 1001 AND version = #{old_version};
  4. 判断更新影响行数:
  • row > 0:扣减成功,提交事务;
  • row = 0:说明中间被其他事务修改(不可重复读导致版本过期),回滚,重试 2~3 次。
    解决了什么
  • 不可重复读:版本号校验拦截中间更新;
  • 库存超卖:多并发只会有一个事务更新成功;
  • 幻读插入: product_id 唯一索引,不会重复创建库存行。

数据库唯一索引

插入防幻读

悲观锁示例:

sql

BEGIN;

-- 加行排他锁,锁定该行,其他事务阻塞读写

SELECT stock FROM product_stock WHERE product_id = 1001 FOR UPDATE;

if (stock < 1) {

ROLLBACK;

return "库存不足";

}

UPDATE product_stock SET stock = stock - 1 WHERE product_id = 1001;

COMMIT;

加锁后同一商品同一时间只能一个事务操作,不会出现两次读取库存不一致(不可重复读)。

分布式锁示例:

单纯数据库锁扛不住秒杀流量,Redis分布式锁前置拦截:

java

// 1. 先抢商品分布式锁

String lockKey = "lock:stock:1001";

boolean lock = redisTemplate.tryLock(lockKey, 3, TimeUnit.SECONDS);

if (!lock) return "抢购拥挤,请重试";

try {

// 2. 数据库RC事务扣库存(乐观锁)

boolean success = deductStockWithVersion(1001);

if (!success) throw new Exception("库存变动,重试");

} finally {

// 释放锁

redisTemplate.unlock(lockKey);

}

全局锁保证同一商品同时只有一个业务流程操作,从源头避免并发读写冲突,彻底屏蔽不可重复读、幻读。

无论乐观锁/分布式锁,网络、DB超时都可能出现不一致,大厂必加定时对账任务:

  1. 订单表已支付订单汇总扣减库存总量;
  2. 和商品实时库存做差值校验;
  3. 差值不为0则自动补发库存/生成异常工单人工处理;
    这是最终兜底,隔离级别带来的一致性问题全部可以靠对账抹平。
相关推荐
Light6010 分钟前
四款云端 Agent 的安全边界画在三个地方
大数据·数据库·安全·个人agent·安全边界·提示注入·grok bot
IvorySQL15 分钟前
PostgreSQL 日报| pg_vectorsearch 开源向量搜索发布(10 月 14 日)
数据库·人工智能·postgresql·开源·区块链
m0_5873830020 分钟前
碰碰好评卡功能落地全流程实战指南 附核心逻辑与常见问题解决方案
java·前端·数据库·后端·struts·需求分析
Y38153266224 分钟前
SerpBase + RAG 实战:用搜索 API 给知识库接上实时数据,回答不再过期
java·服务器·数据库
骑着蜗牛撵大象32724 分钟前
SQL 编辑器不是高级记事本:KStudio 与 KSQL Developer 里的建表与增删改查手法
数据库·sql·mysql·编辑器·crud·执行计划
Access开发易登软件29 分钟前
我全新开发了一套 Access 通用 Excel 导入工具,现在开源了
数据库·microsoft·excel·mfc·vba·access·access开发
笃行35037 分钟前
SQL编辑器的隐藏技能:KingbaseES 配套 KSQL Developer 使用笔记
数据库
努力进修1 小时前
时序数据长期承载难题:从压缩、批量写入到数据组织优化
java·开发语言·数据库
CaptainT1 小时前
先把例子跑起来:MySQL 环境与本文的示例数据
数据库·后端·mysql
Dovis(誓平步青云)1 小时前
总用电下降了,为什么家庭能源看板仍然提示异常
java·开发语言·数据库·人工智能·算法·电脑·能源