在关系数据库中使用乐观和悲观锁

本文涉及的产品
云数据库 Redis 版,标准版 2GB
推荐场景:
搭建游戏排行榜
云原生内存数据库 Tair,内存型 2GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
简介: 【6月更文挑战第12天】本文探讨了数据库中处理冲突的两种方法:乐观锁和悲观锁。数据库系统如Oracle、PostgreSQL和MySQL的InnoDB引擎利用MVCC实现乐观锁,而SQL Server和MySQL在不同隔离级别下分别采用悲观或乐观锁。

1 简介

乐观锁允许并发更新,仅在提交时检查冲突,适合冲突不频繁的情景;

悲观锁则在编辑时锁定记录,防止并发更新,适用于高冲突环境。

两种锁各有优劣,选择依赖于并发程度、资源开销和事务重试成本。

  • 问题的根本:处理冲突

现在有一座小桥如下,两边都有人需要通过,如何处理他们谁先过的问题?

image.png

  • 冲突避免

在计算机网络中,有两种处理冲突或碰撞的常用方法:

退避重试:检测并重试,这正是以太网的方式
阻止冲突:通过阻止其他并发数据来避免它们,这是 Wi-Fi 的默认方式。

在使用数据库系统,处理冲突实际上也是类似的。

我们可以允许冲突发生,但是我们需要在提交时检测到它,这正是乐观锁定的工作方式。

如果重试的成本很高,我们可以尝试通过锁定来完全避免冲突,这是悲观锁定工作原理。

在数据库操作中,丢失的数据可能发生在在“已提交读取”隔离级别下运行的任何数据库上。

而数据库有两种方式用于锁定数据,乐观锁和悲观锁:

乐观锁 Optimistic Locking,其中记录仅在更改时锁,如提交到数据库的动作
悲观锁 Pessimistic Locking,即记录在编辑时就被锁,从编辑到提交全过程

在两种数据锁模型中,锁在数据更改后都释放, 如已提交数据到数据库。

1 定义

  • 乐观锁

乐观锁模型, 也称为乐观并发控制,是一种并发控制 关系数据库中使用的不使用记录锁的方法。

乐观的 锁允许多个用户尝试更新同一记录,而无需 通知用户其他人也在尝试更新记录。 仅当提交记录时,才会验证记录更改。如果一个 用户成功更新记录,其他用户尝试提交 他们的并发更新会被告知存在冲突。

优势:乐观锁避免了操作持续数据时的锁的开销。如果没有竞争更新, 此模型提供快速更新。

乐观锁使用场景是,当并发记录更新预计不频繁或悲观锁开销很高可以使用。

  • 悲观锁

悲观锁模型, 防止同时更新记录。一旦一个用户开始更新一个记录,一个悲观锁被放在上面。
尝试更新此记录的其他用户将被告知其他用户正在进行更新。

其他用户 必须等到第一个用户完成提交其更改,从而 释放记录锁。只有这样,其他用户才能根据 上一个用户的更改。

悲观锁的优势 模式是它通过预防冲突来避免解决冲突的问题。 更新被序列化了,每个后续更新都从提交的开始 记录来自上一个用户的更改。

悲观锁场景是, 后续更新可以延迟到上一个更新 完成。

这通常意味着更新在较短的时间间隔内发生。

image.png

2 一个数据冲突的例子

例子:

老王读取帐户余额,值为 100元
紧接着,老王的家人 从帐户取出50,余额从 100更改为 50,并且提交。
此时老王还正在查询,想到账户余额还在,就取出60然后退出了,
老王以为最后的余额会是:100-60 = 40元。
但是,由于其他人已更改,老王的更新将使帐户余额为负值。

在大多数场景下乐观和悲观锁都是并发场景的解决方案。根据关系数据库类型和环境的不同,这可能会降低或提高性能。

通常,使用乐观锁时,您会发现需要在某处对值的行做版本控制。

当锁冲突的可能性较低时,乐观锁效果很好。如果有许多进程的相互作用,也就是锁冲突的可能性很高时,悲观锁效果很好。

3 条件和经典使用场景

这两种形式的锁都会导致进程等待数据的正确副本(如果该副本当前正由另一个进程使用)。

对于悲观锁,锁机制来自数据库本身(本机锁对象),而对于乐观锁,锁机制是某种形式的行版本控制,如时间戳,用于检查记录是否“过时”。

4 使用参考条件

但两者都会导致第二个进程挂起,使用时参考的条件:

-是否有足够的 IO/资源来处理行版本控制的形式?否则,您将增加开销。

如果是这样,比如 经常读取数据,业务经常锁数据以进行写入,使用锁将改进读取和写入之间的并发性 (尽管写入仍会阻止写入,但读取将不再阻止写入,反之亦然)

-代码是否容易受到死锁的影响,或者是否遇到锁?

如果没有遇到长锁或很多死锁,那么乐观锁的额外开销不会更大。

-如果数据库很大(或在非常有限的硬件上),并且数据页接近满,具体取决于关系数据库,则可能会导致需要在主要页面拆分整理和数据碎片,因此请务必打开后考虑重新索引。 

5 经典场景:

许多关系数据库系统使用乐观锁定来提供ACID保证。Oracle,PostgreSQL和InnoDB MySQL引擎使用MVCC(多版本并发控制),它们基于乐观锁定。

比如在 MVCC 中使用了乐观锁,因此读取器不会阻止编写器,而编写器也不会阻止读取器,从而允许发生冲突。但是,在提交时,事务引擎会检测到冲突,并回滚冲突的事务。

另外在 SQL Server 中,数据库在读取“可重复读”或“可序列化隔离”级别的记录时会自动获取共享锁,因为 SQL Server 默认使用 2PL(两阶段锁定)算法。

而MySQL在使用可序列化隔离级别时默认使用悲观锁定,而对于其他不太严格的隔离级别,则使用乐观锁定。

6 小结

乐观锁方面,我们通常需要在两个同样令人不满意的选项之间进行选择。
一个是“成本更高的正确性”,这种成本与事件的可能性也许不成比例。
另一种选择是“部分正确”,过度简化的实现。

但是,悲观锁在最基本的形式中,有一个非常简单的实现。执行的成本将恢复为与事件发生的可能性成比例。

悲观和乐观锁都是有用的技术。当重试事务的成本非常高或争用太大以至于如果使用乐观锁,许多事务最终会回滚时,悲观锁定是合适的。

另一方面,乐观锁甚至可以跨多个数据库事务工作,它不依赖于锁定的物理记录。

总的来说,单个任务而言没有锁总是比有锁快,乐观锁的使用率较高。

目录
相关文章
|
4月前
|
算法 关系型数据库 MySQL
【MySQL 解析】数据库的乐观锁和悲观锁实现原理
【1月更文挑战第11天】【MySQL 解析】数据库的乐观锁和悲观锁实现原理
|
4月前
|
数据库
什么是数据库悲观锁和乐观锁
什么是数据库悲观锁和乐观锁
34 0
|
3月前
|
Java 数据库连接 API
解锁你的数据库:JPA和Hibernate的乐观锁与悲观锁
本文由木头左介绍JPA和Hibernate中的乐观锁与悲观锁。乐观锁假设无冲突,通过`@Version`注解防止并发更新,适用于更新不频繁、处理冲突成本高、数据一致性要求不高的场景。悲观锁假设有冲突,利用`@Lock`注解实现加锁,适用于并发更新频繁、处理冲突成本低、数据一致性要求高的情况。选择哪种锁取决于具体需求。
解锁你的数据库:JPA和Hibernate的乐观锁与悲观锁
|
4月前
|
算法 关系型数据库 MySQL
数据库的乐观锁和悲观锁是什么?
乐观锁对应于生活中乐观的人总是想着事情往好的方向发展,悲观锁对应于生活中悲观的人总是想着事情往坏的方向发展。这两种人各有优缺点,不能不以场景而定说一种人好于另外一种人。
|
10月前
|
算法 Java 数据库
数据库系列课程(19)-乐观锁与悲观锁的优缺点
数据库系列课程(19)-乐观锁与悲观锁的优缺点
64 0
|
SQL 关系型数据库 MySQL
数据库并发控制、悲观锁(Pessimistic Lock)、乐观锁(Optimistic Lock)、排他锁(Exclusivelocks X锁)、共享锁(Shared Lock S锁)
数据库并发控制、悲观锁(Pessimistic Lock)、乐观锁(Optimistic Lock)、排他锁(Exclusivelocks X锁)、共享锁(Shared Lock S锁)
数据库并发控制、悲观锁(Pessimistic Lock)、乐观锁(Optimistic Lock)、排他锁(Exclusivelocks X锁)、共享锁(Shared Lock S锁)
|
算法 关系型数据库 MySQL
软件测试mysql面试题:数据库的乐观锁和悲观锁是什么?怎么实现的?
软件测试mysql面试题:数据库的乐观锁和悲观锁是什么?怎么实现的?
151 0
|
关系型数据库 MySQL 数据库
树莓派开发笔记(十七):树莓派4B+上Qt多用户连接操作Mysql数据库同步(单条数据悲观锁)
安装了mysq数据库,最终时为了实现在一个树莓派上实现多用户多进程操作的同步问题,避免数据并发出现一些错误,本篇安装了远程服务并且讲述了使用Qt进行悲观锁for update操作,命令行进行同步查询的示例。
树莓派开发笔记(十七):树莓派4B+上Qt多用户连接操作Mysql数据库同步(单条数据悲观锁)
|
关系型数据库 MySQL 数据库
深入了解和探索数据库的悲观锁和乐观锁
 在数据库的锁机制中介绍过,数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。
1618 0
|
Java 数据库
使用数据库悲观锁实现不可重入的分布式锁
一、前言 在同一个jvm进程中时,可以使用JUC提供的一些锁来解决多个线程竞争同一个共享资源时候的线程安全问题,但是当多个不同机器上的不同jvm进程共同竞争同一个共享资源时候,juc包的锁就无能无力了,这时候就需要分布式锁了。
11064 0