深入理解悲观锁和乐观锁机制
发布日期:2021-05-14 06:36:37 浏览次数:18 分类:精选文章

本文共 1549 字,大约阅读时间需要 5 分钟。

在探讨悲观锁和乐观锁之前,我们先要理解什么是锁。锁是一种在并发环境下,用于控制多个操作顺序执行以保证数据安全变动的机制。它是一种通用的数据保护手段,而不特指某一技术。悲观锁和乐观锁也是基于这一原理。

锁的选择取决于具体的业务场景。在数据库管理系统(DBMS)中,悲观锁确实会使用数据库提供的锁机制(如行锁、表锁等),但乐观锁并非完全依赖锁机制,而是结合特定机制(如版本号或时间戳)实现。

悲观锁概念

悲观锁顾名思义字符怀很悲观。它假设数据的修改几率较高,因此在获取数据时会立即加锁,排除其他事务的修改可能。这导致其他事务必须等待当前事务完成后才能继续处理。

传统关系型数据库中常用行锁、表锁等都是悲观锁的体现。在Java中,synchronized和ReentrantLock等独占锁也基于悲观锁思想实现。

悲观锁主要包含两种锁类型:共享锁和排他锁。

共享锁(Shared Lock)

又称读锁(Read Lock)或S锁。它允许多个事务同时共享对同一数据的读取权,但不允许修改。这种锁机制主要针对并发读取场景,避免读取过度竞争。

排他锁(Exclusive Lock)

又称写锁(Write Lock)或X锁。它确保在一个事务修改数据前,不允许其他事务进行任何访问(包括读和写)。这最大程度地防止数据不一致,但也导致严重的并发效率下降。

悲观锁的特点是"先取锁再访问",这一保守策略确保了数据的安全性,但同时也付出了较高的性能代价,可能导致死锁和并发次数下降。

乐观锁概念

乐观锁则"乐观"地认为数据的修改不会频繁发生。因此,它允许多个事务同时对数据进行读取和写入操作,只有在数据提交时才检测冲突情况。

乐观锁采用"改而不锁"的机制,通常通过版本号或时间戳实现。以下是版本号机制的常见流程:

  • 事务在读取数据前获取当前版本数。
  • 在修改完成后,将修改后的数据与当前版本数进行对比。
  • 若版本数与原始数据不一致,则说明数据已被修改,禁止写入。
  • 否则,更新数据并增加版本数。
  • 这种机制假设数据冲突的概率较低,适用于读多写少的场景。

    悲观锁和乐观锁的应用

    下面我们以一个蛋糕店的例子,来说明悲观锁和乐观锁的应用场景。

    悲观锁实现

    假设一个蛋糕店存在下列商品数据表:

    id name count
    1 豆乳小蛋糕 1

    如果两个买家A和B同时下单,悲观锁会采取以下方式:

  • A在读取数据时立即加锁,其他B必须等待。
  • A完成购买后将锁释放,B可以继续操作。
  • 此时,如果B想购买同样的蛋糕,可能发现数量为0而放弃购买。

    乐观锁实现

    乐观锁需要在数据表中增加一个版本号字段。假设更新后的数据结构如下:

    id name count version
    1 豆乳小蛋糕 1 0

    在实际购买时,A和B都能先读取version=0的数据。A完成购买后,将version增加到1。B尝试购买时,查看最新数据发现version=1,与原版本不一致,提示用户数据已被修改。

    悲观锁和乐观锁的优缺点

    悲观锁优点

    • 利用数据库本身提供的锁机制,简单可靠。
    • 适用于高并发读写并OLDER场景。
    悲观锁缺点
    • 持续锁定数据会导致并发吞吐量下降。
    • 高 probability of deadlocks 当多个事务试图锁定相同数据时。
    • 锁的颗粒度(如行锁)可能带来额外的锁管理开销。

    乐观锁优点

    • 不需要额外的加锁机制,减少系统开销。
    • 适用于读多写少的场景。
    • 支持更高的并发度。
    乐观锁缺点
    • 需要额外设计机制(如版本号)手动检测冲突,增加开发复杂度。
    • 如果事务确实修改数据,而未正确检测冲突,可能导致数据不一致。

    总结

    在实际应用中,应该根据业务特点选择合适的并发控制机制。悲观锁适合对数据一致性要求极高但并发需求不高的场景,而乐观锁则适用于读多写少的情况。

    上一篇:Mysql学习专栏之八(从删除操作分析Mysql中的表收缩)
    下一篇:Mysql学习专栏之七(浅析Mysql中的binlog)

    发表评论

    最新留言

    做的很好,不错不错
    [***.243.131.199]2025年04月05日 17时45分44秒