当前位置:首页 > 问答 > 正文

数据一致性 并发控制 数据库锁表与解锁:如何保障中心词的数据一致性

数据一致性 | 并发控制 | 数据库锁表与解锁:如何保障中心词的数据一致性

场景引入:当两个用户同时修改同一条数据

想象一下这个场景:

你正在运营一个电商平台,商品库存是核心数据,某热门商品只剩最后一件,用户A和用户B几乎同时点击“购买”,如果没有合适的并发控制,系统可能会这样处理:

  1. 用户A的请求读取库存为1,准备扣减。
  2. 用户B的请求也读取库存为1,准备扣减。
  3. 两个请求都认为库存充足,最终导致超卖——库存显示-1,但实际已无货可发。

这就是典型的数据一致性问题,如何避免?并发控制数据库锁是关键。


数据一致性为什么重要?

数据一致性指的是数据库中的数据在任何时候都保持逻辑正确,不会因为并发操作而出现矛盾。

  • 银行转账:A向B转100元,必须保证A账户扣减和B账户增加同时成功或失败。
  • 订单库存:卖出一件商品,库存必须准确扣减,不能多卖或少卖。

如果缺乏控制,脏读(Dirty Read)不可重复读(Non-repeatable Read)幻读(Phantom Read)等问题就会出现。


并发控制的常见手段

悲观锁(Pessimistic Locking)

核心思想:“先锁住,再操作”,认为冲突很可能发生。

数据一致性 并发控制 数据库锁表与解锁:如何保障中心词的数据一致性

  • 行级锁(Row Lock):锁定某一行数据,其他事务无法修改。

    BEGIN;
    SELECT * FROM products WHERE id = 1 FOR UPDATE; -- 加锁
    UPDATE products SET stock = stock - 1 WHERE id = 1;
    COMMIT;

    适合高竞争场景,但可能降低并发性能。

  • 表级锁(Table Lock):直接锁整张表,简单粗暴,但并发性极差,一般避免使用。

乐观锁(Optimistic Locking)

核心思想:“先操作,冲突了再处理”,认为冲突不常发生。

  • 版本号控制
    每条数据加一个version字段,更新时检查版本是否变化。

    UPDATE products 
    SET stock = stock - 1, version = version + 1 
    WHERE id = 1 AND version = 5; -- 只有版本匹配才更新

    如果返回影响行数为0,说明别人已修改,需重试或提示用户。

  • CAS(Compare-And-Swap):类似版本号,用旧值比对后再更新。

    数据一致性 并发控制 数据库锁表与解锁:如何保障中心词的数据一致性

数据库事务隔离级别

不同隔离级别对一致性的保证不同:

隔离级别 脏读 不可重复读 幻读
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SERIALIZABLE
  • MySQL默认是REPEATABLE READ,能解决大部分问题,但幻读需特殊处理(如间隙锁)。
  • SERIALIZABLE最严格,但性能最差,一般不用。

锁表与解锁:如何避免死锁?

锁的常见类型

  • 共享锁(S锁):多个事务可同时读,但不能写。
  • 排他锁(X锁):一个事务独占,其他事务不能读或写。

死锁的产生与解决

场景

  • 事务A锁了行1,请求行2;
  • 事务B锁了行2,请求行1;
  • 互相等待,形成死锁。

解决方案

  • 超时释放:设置锁等待超时(如innodb_lock_wait_timeout)。
  • 死锁检测:数据库自动检测并回滚代价较小的事务(如MySQL的InnoDB引擎)。
  • 按固定顺序加锁:所有事务按相同顺序访问数据,避免循环等待。

实际应用建议

  1. 尽量缩小锁范围:用行锁代替表锁,减少阻塞。
  2. 避免长事务:长时间持有锁会增加死锁风险。
  3. 合理选择乐观锁/悲观锁
    • 高并发写用悲观锁(如秒杀)。
    • 低冲突场景用乐观锁(如普通订单)。
  4. 监控锁等待:数据库工具(如SHOW ENGINE INNODB STATUS)可查看锁情况。

保障数据一致性的核心是控制并发访问

  • 锁机制(悲观锁)适合强一致性场景,但要注意性能。
  • 乐观锁适合低冲突场景,减少锁开销。
  • 事务隔离级别根据业务需求选择,不是越高越好。

测试是关键!模拟高并发场景(如JMeter压测),确保你的方案真的能扛住真实流量。

发表评论