数据库为什么需要锁机制?有哪些锁机制?

本文深入探讨了数据库锁机制,包括为何需要加锁、常见的锁类型(如行级锁、表级锁、悲观锁和乐观锁),以及它们在并发控制中的应用。通过具体实例解释了各种锁的工作原理和适用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 【为什么要锁】

    数据库是一个多用户使用的共享资源,比如一个用户表t_user,两个浏览器前面的人登录了同个一个账号,把电话号码改了。当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性(脏读,不可重复读,幻读等),可能产生死锁。为了解决这个问题,加锁是一个非常重要的技术,对实现数据库并发控制是一个好的方案。简单说,当一个执行sql语句的事务想要操作表记录之前,先向数据库发出请求,对你访问的记录集加锁,在这个事务释放这个锁之前,其他事务不能对这些数据进行更新操作。

    【有哪些锁】

     锁包括行级锁、表级锁、悲观锁、乐观锁

     行级锁:一种它锁,防止另外事务修改此行;在使用以下语句时,Oracle会自动应用行级锁:INSERT、UPDATE、DELETE、SELECT … FOR UPDATE [OF columns] [WAIT n | NOWAIT];SELECT … FOR UPDATE语句允许用户一次锁定多条记录进行更新.使用commit或者rollback释放锁。MySql的innodb存储引擎默认是行级锁。特点:开锁大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。适合于有大量按索引更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理系统。

 

    表级锁:5种

   行共享 (ROW SHARE) – 禁止排他锁定表,与行排他类似,区别是别的事务还可以在此表上加任何排他锁。(除排他(exclusive)外)
   行排他(ROW EXCLUSIVE) – 禁止使用排他锁和共享锁,其他事务依然可以并发地对相同数据表执行查询,插入,更新,删除操作,或对表内数据行加锁的操作,但不能有其他的排他锁(自身是可以的,没发现有什么用)
   共享锁(SHARE) - 锁定表,对记录只读不写,多个用户可以同时在同一个表上应用此锁,在表没有被任何DML操作时,多个事务都可加锁,但只有在仅一个事务加锁的情况下只有此事务才能对表更新;当表已经被更新或者指定要更新时(select for update),任何事务都不能加此锁了。
   共享行排他(SHARE ROW EXCLUSIVE) – 比共享锁更多的限制,禁止使用共享锁及更高的锁,在表没有被任何DML操作时,只有一个事务可以加锁,可以更新,书上说别的事务可以使用select for update锁定选中的数据行,可是实验后没被验证。
   排他(EXCLUSIVE) – 限制最强的表锁,仅允许其他用户查询该表的行。禁止修改和锁定表

 

   行级锁和表级锁是根据锁的粒度来区分的,行记录,表都是资源,锁是作用在这些资源上的。如果粒度比较小(比如行级锁),可以增加系统的并发量但需要较大的系统开销,会影响到性能,出现死锁,,因为粒度小则操作的锁的数量会增加;如果作用在表上,粒度大,开销小,维护的锁少,不会出现死锁,但是并发是相当昂贵的,因为锁定了整个表就限制了其它事务对这个表中其他记录的访问。

   悲观锁:

   Pessimistic Lock正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守悲观态度,事务每次去操作数据的时候都假设有其他事务会修改需要访问的数据,所以在访问之前都要求上锁,行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能 真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系 统不会修改数据)。 一个典型的倚赖数据库的悲观锁调用: select * from account where name=”Erica” for update 这条sql 语句锁定了account 表中所有符合检索条件(name=”Erica”)的记录。 本次事务提交之前(事务提交时会释放事务过程中的锁),外界无法修改这些记录。

    Hibernate悲欢锁实现:基于数据库锁机制

         Query q=Session.createQuery("select  * from t_profit where amount>10000");

          q.setLockMode("Profit",LockMode.UPGRADE);//Profit是Profit类的别名

         List<Profit> ps=q.list();

       执行的sql:select ....from t_profit where amount>10000 for update.hibernate的悲观锁通过数据库的for update实现。

     LockMode.NONE:无锁机制;

     LockMode.WRITE:insert,update记录时自动获取悲观锁;

     LockMode.READ在读取时自动获取悲观锁;

     LockMode.UPGRADE:利用数据库的for update子句加锁;

     LockMode.UPGRADE_NOWAIT:oracle特定实现,用oracle的for update nowait子句加锁

  乐观锁:

   Optimistic Lock,和悲欢锁相反,事务每次去操作数据之前,都假设其他事务不会修改这些需要访问的数据 ,所以 在访问之前不要求上锁,只是在进行更新修改操作的时候判断一下在访问的期间有没有其他人修改数据 了。它适用于多读的应用类型,冲突真的发生比较少的时候就比较好,这样省去了开销的开销,可以提高吞吐量;但如果是真的经常要发生冲突的,那每次还要去判断进行retry,反倒降低的性能,这个时候悲欢锁比较好。数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。

          它的实现大多是基于数据版本versin记录机制。举个例子:

       1.利润表t_profit中有一个 version字段,当前值为1;而总资产余额字段(balance)为$10000

       2.操作员A读出version=1,从总资产减除2000,10000-2000=8000.

       3.A还没操作结束,此时操作员B也读出version=1,总资产减除5000,10000-5000=5000.

       4.A操作完成,把version加1,修改为2,把总资产减2000后提交更新数据库,更新成功

       5.B操作了,也加version加1,修改为2,把总资产减5000后提交更新数据库,此时发现version已经为2了,如B修改后加1的version一样,不满足乐观锁策略:"提交的版本必有大于记录当前的版本才能执行"。因此B的操作请求被驳回,这样就避免了B就version=1的旧数据修改的结果覆盖了A操作的结果的可能。如没有乐观锁,那A减去2000后剩余8000,但B操作的时候是用10000-5000剩余5000的,如果B的提交成功,总资产余额就是5000,但实际情况应该是8000-5000=3000的。出现总资产表记录和实际支出不一致。

    Hibernate对乐观锁的实现:

      <hibernate-mapping>

        <class name="com.f.TProfit" table="t_profit" optimistic-lock="version"></class>

      </hibernate-mapping>

<think>好的,我现在需要回答用户关于数据库为什么需要锁机制,以及有哪些锁机制的问题。首先,我需要回想一下之前用户问过排他锁和读写锁,还有死锁的例子,所以这次的问题应该是在那基础上的扩展。 用户的问题分两部分:为什么需要锁机制,以及有哪些锁机制。我需要先回答为什么需要,再列举不同的锁机制,可能还要简单说明每种机制的作用。 首先,为什么需要锁机制数据库通常会有多个事务同时操作数据,如果不加控制,可能会出现数据不一致的问题。比如两个事务同时修改同一数据,可能导致覆盖或者错误的结果。锁机制的作用就是确保并发操作时的数据一致性,避免脏读、不可重复读、幻读等问题。另外,锁还能保证事务的隔离性,这是ACID里的一个特性。所以,我需要从并发控制、数据一致性、事务隔离性这几个方面来阐述原因。 接下来是锁机制的种类。根据之前的回答,用户已经了解排他锁(X锁)和共享锁(S锁),也就是读写锁。但可能还有其他类型的锁需要介绍,比如表级锁、行级锁、意向锁、乐观锁、悲观锁等。需要区分不同的分类方式,比如按锁的粒度分,按锁的性质分,或者按加锁策略分。 需要注意用户可能对某些术语不太熟悉,比如意向锁是什么,或者乐观锁和悲观锁的区别。可能需要用简单的例子来解释。 另外,用户可能想知道锁机制如何工作,比如行锁和表锁的应用场景,以及它们对性能的影响。比如行级锁粒度小,并发高,但开销大;表级锁反之。意向锁是为了在更高粒度上协调锁,比如事务在加行锁前先加意向锁,避免其他事务加表锁。 还有乐观锁和悲观锁,这两种属于并发控制的策略,乐观锁假设冲突少,通过版本号或时间戳实现;悲观锁则假设冲突多,先加锁再操作。需要解释它们的应用场景,比如乐观锁适合读多写少的情况。 总结来说,我需要结构清晰地分两部分:为什么需要锁,以及锁的种类,每个种类下分点说明,可能用例子帮助理解。同时,要避免过于技术化的术语,用简单的中文表达,保持逻辑连贯。 现在需要检查是否有遗漏的锁类型,比如间隙锁(Gap Lock),或者在MySQL中使用的Next-Key Lock,这些属于行锁的扩展,用于解决幻读问题。不过用户可能不需要太深入的细节,所以可能简要提及即可。 此外,可能需要提到数据库的隔离级别,不同隔离级别下锁的使用方式不同,比如读未提交、读已提交、可重复读、串行化,但这个问题可能更偏向锁机制本身,而不是隔离级别,所以可能不需要展开。 最后,确保回答符合之前的格式要求,使用中文,结构清晰,分点说明,正确使用LaTeX公式(如果有的话),但用户这次的问题可能不需要数学公式,所以重点在文字描述和例子。</think>### **数据库为什么需要锁机制?** 数据库锁机制是**解决并发操作引发数据冲突**的核心工具,主要为了解决以下问题: 1. **保障数据一致性** 当多个事务同时读写数据时,可能引发**脏读**(读到未提交的修改)、**不可重复读**(同一事务内多次读取结果不同)、**幻读**(其他事务插入/删除数据导致结果集变化)等问题。锁机制通过控制并发访问顺序,确保事务的隔离性(ACID中的“I”)。 2. **维护事务隔离级别** 不同隔离级别(如读未提交、读已提交、可重复读、串行化)依赖锁机制实现。例如: - **读已提交**:通过共享锁阻止其他事务修改已读数据。 - **可重复读**:通过长期持有锁或范围锁避免幻读。 3. **防止资源竞争冲突** 并发事务可能因争夺同一数据导致结果错误。例如: - **丢失更新**:两个事务同时修改同一数据,后提交的覆盖前者的结果。 - **写冲突**:未加锁时,事务可能基于过期数据做出错误决策。 --- ### **数据库中的常见锁机制** #### **1. 按锁的互斥性分类** - **共享锁(Shared Lock,S锁)** - **作用**:允许多个事务并发读取同一数据,但阻止其他事务加排他锁。 - **场景**:高频读操作(如 `SELECT`)。 - **示例**: ```sql SELECT * FROM 订单表 WHERE 状态 = '待支付' LOCK IN SHARE MODE; ``` - **排他锁(Exclusive Lock,X锁)** - **作用**:独占数据,禁止其他事务加任何锁(包括共享锁和排他锁)。 - **场景**:数据修改(如 `UPDATE`、`DELETE`)。 - **示例**: ```sql SELECT * FROM 库存表 WHERE 商品ID = 'P100' FOR UPDATE; UPDATE 库存表 SET 数量 = 数量 - 1 WHERE 商品ID = 'P100'; ``` #### **2. 按锁的粒度分类** - **表级锁** - **特点**:直接锁定整张表,实现简单但并发度低。 - **场景**:批量数据操作(如全表更新)。 - **示例**: ```sql LOCK TABLES 用户表 WRITE; -- MySQL表级排他锁 ``` - **行级锁** - **特点**:仅锁定特定行,并发度高但管理开销大。 - **场景**:高并发单行操作(如电商扣减库存)。 - **示例**: ```sql -- InnoDB行级锁(通过索引实现) SELECT * FROM 账户表 WHERE 账户号 = 'A001' FOR UPDATE; ``` - **页级锁/块级锁** - **特点**:锁定数据页(物理存储单元),粒度介于表锁和行锁之间。 - **场景**:较少使用,如某些旧版数据库(如SQL Server)。 #### **3. 按锁的策略分类** - **乐观锁(Optimistic Locking)** - **原理**:假设冲突概率低,通过版本号或时间戳检测数据变更。 - **实现方式**: - **CAS(Compare and Swap)**:更新时校验版本号是否匹配。 - **示例**: ```sql UPDATE 商品表 SET 库存 = 库存 - 1, 版本号 = 版本号 + 1 WHERE 商品ID = 'P100' AND 版本号 = 当前版本; ``` - **适用场景**:读多写少,冲突较少(如评论点赞)。 - **悲观锁(Pessimistic Locking)** - **原理**:假设冲突概率高,预先加锁阻止其他事务访问。 - **实现方式**:通过 `SELECT ... FOR UPDATE` 或 `LOCK IN SHARE MODE` 显式加锁。 - **适用场景**:写操作频繁,冲突较多(如支付扣款)。 #### **4. 按锁的扩展功能分类** - **意向锁(Intention Locks)** - **作用**:快速判断表中是否存在行级锁,避免逐行检查。 - **类型**: - **意向共享锁(IS锁)**:事务计划在表中某些行加共享锁。 - **意向排他锁(IX锁)**:事务计划在表中某些行加排他锁。 - **示例**: 事务A对某行加排他锁前,会先对表加IX锁,其他事务若想加表级锁,需检查IX锁是否冲突。 - **间隙锁(Gap Lock)** - **作用**:锁定索引记录间的“间隙”,防止其他事务插入数据(解决幻读)。 - **场景**:可重复读隔离级别下的范围查询。 - **示例**: ```sql SELECT * FROM 订单表 WHERE 金额 BETWEEN 100 AND 200 FOR UPDATE; -- 锁住金额在100~200之间的所有现有记录及间隙 ``` --- ### **锁机制的核心价值** - **平衡性能与一致性**:通过不同粒度的锁,在并发效率和数据安全间取得平衡。 - **支持复杂事务**:确保事务的原子性、隔离性和持久性(ACID特性)。 **实际案例**: 银行转账场景中,事务A和事务B同时操作账户A和账户B: - 若未加锁,可能导致余额计算错误(如两者同时读取旧值后更新)。 - 通过行级排他锁,强制事务串行执行,保证最终结果正确。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值