title: 数据库——锁
date: 2024-07-06 12:25:15
tags: 数据库
categories: 数据库
cover: /image/T2.jpg
description: 数据库里对锁的应用,锁的一些相关知识
锁
在数据库管理和并发编程中,锁(Locks)是一种重要的同步机制,用于控制多个用户对共享资源的访问,以避免数据的不一致性和冲突。锁的主要目的是确保在给定时间内,只有一个事务(或进程、线程)可以访问特定的数据或资源。
锁的类型
-
共享锁(Shared Locks, S锁):
- 允许多个事务同时读取同一资源,但禁止任何事务写入该资源。也称为读锁。
- 当事务对数据加上S锁后,其他事务可以继续加S锁,但如果其他事务想加排他锁(X锁)则必须等待该事务释放S锁。
-
排他锁(Exclusive Locks, X锁):
- 允许事务独占访问特定资源,即加锁期间既不允许其他事务读取,也不允许写入。也称为写锁。
- 当事务对数据加上X锁后,其他事务不能对其加任何类型的锁,直到该锁被释放。
-
意向锁(Intention Locks):
- 是一种特殊的表级锁,表示事务将来可能对表中的行加锁。意向锁分为意向共享锁(IS锁)和意向排他锁(IX锁)。
- 意向锁的主要目的是表明事务的锁定意向,以提高锁定的效率。例如,在添加行级X锁之前,需要先在表上加IX锁,这样其他事务在尝试对表加S锁或X锁时就可以快速判断是否有冲突。
-
记录锁(Record Locks):
- 锁定数据库表中的一条记录。
-
间隙锁(Gap Locks):
- 锁定一个范围,但不包括记录本身。主要用于防止幻读。
-
临键锁(Next-Key Locks):
- 是记录锁和间隙锁的组合,锁定一个范围并包括记录本身。MySQL的InnoDB存储引擎默认使用临键锁来防止幻读。
锁的特性
- 互斥性:任何时刻,只有一个事务可以持有锁。
- 可见性:锁定的资源对其他事务是不可见的,直到锁被释放。
- 死锁:两个或多个事务在执行过程中,因争夺资源而造成的一种相互等待的现象。数据库管理系统需要检测和解决死锁问题。
锁的管理
- 锁的粒度:决定了锁定资源的大小,可以是数据库、表、页或行。锁的粒度越小,系统的并发性越高,但管理锁的开销也越大。
- 锁的策略:包括悲观锁(Pessimistic Locking)和乐观锁(Optimistic Locking)两种。悲观锁假定最坏的情况,在数据处理前就加锁;乐观锁则假设不会发生并发冲突,只在更新数据时检查是否有冲突。
在数据库设计中,合理地使用锁机制是保证数据一致性和完整性的关键。然而,过度的锁定会导致性能下降,因此需要根据具体的应用场景和需求来选择合适的锁策略和粒度。
事务隔离级别与锁
-
在 读取未提交 隔离级别下,读取数据不需要加 共享锁,这样就不会跟被修改的数据上的 排他锁 冲突;
-
在 读取已提交 隔离级别下,读操作需要加 共享锁,但是在语句执行完以后释放共享锁;
-
在 可重复读 隔离级别下,读操作需要加 共享锁,但是在事务提交之前并不释放共享锁,也就是必须等待事务执行完毕以后才释放共享锁;
-
可串行化 是限制性最强的隔离级别,因为该级别 锁定整个范围的键,并一直持有锁,直到事务完成
死锁与解决
死锁简述
死锁是指两个或多个进程在执行过程中,因争夺资源而造成的一种相互等待的现象,若无外力作用,这些进程都将无法向前推进。具体来说,当一组进程中的每个进程都在等待另一个进程所占有的资源时,这组进程就发生了死锁。
死锁的产生通常源于多个进程对资源的争夺,主要包括以下几点:
- 系统资源不足:系统分配给进程的资源不足以满足所有进程的需求。
- 进程运行推进的顺序不当:进程推进顺序不当也可能导致死锁。
- 资源分配不当:资源分配策略不合理,如资源分配不均匀或过于集中。
死锁产生的必要条件
- 互斥条件:进程在运行中对资源进行排他性使用,即一个资源仅能被一个进程使用,此时其他进程请求资源时,只能等待其释放。
- 请求与保持条件:某进程已经保持了一个资源,但又请求另一个资源,若该资源被其他进程占有,此时请求阻塞,且对已经占有的资源不释放。
- 不可抢占条件:进程获得的资源在未使用完时不可被抢占,只能在进程使用完时自己释放。
- 循环等待条件:发生死锁时,必然存在这样一个循环,一个进程p1等待p2占有的资源,进程p2等待p3占有的资源……进程pn等待p1占有的资源。
死锁的解决办法
-
死锁预防
- 这是一种较简单和直观的事先预防的方法。通过设置某些限制条件,去破坏产生死锁的四个必要条件中的一个或者几个,来预防发生死锁。但这种方法可能会导致系统资源利用率和系统吞吐量降低。
-
死锁避免
- 系统对进程发出的每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源。如果分配后系统可能发生死锁,则不予分配,否则予以分配。这是一种保证系统不进入死锁状态的动态策略。
-
死锁检测和解除
- 先检测:不须事先采取任何限制性措施,也不必检查系统是否已经进入不安全区。允许系统在运行过程中发生死锁,但可通过系统所设置的检测机构,及时地检测出死锁的发生,并精确地确定与死锁有关的进程和资源。检测方法包括定时检测、效率低时检测、进程等待时检测等。
- 然后解除死锁:采取适当措施,从系统中将已发生的死锁清除掉。常用的实施方法是撤销或挂起一些进程,以便回收一些资源,再将这些资源分配给已处于阻塞状态的进程,使之转为就绪状态,以继续运行。
-
其他具体方法
- 按序加锁:确保所有的线程都是按照相同的顺序获得锁,这样可以有效防止循环等待条件的出现。
- 尝试加锁超时:线程在尝试获取锁时设置一个超时时间,如果超时则放弃对该锁的请求,并释放已经占有的锁,然后等待一段时间后重试。
- 锁排序和分组:将锁按照一定的规则进行排序或分组,并规定严格的加锁顺序,以减少死锁的发生。
- 使用无锁数据结构:通过原子操作等无锁机制来实现并发控制,从而避免死锁的发生。
乐观锁和悲观锁
乐观锁
-
核心思想:乐观锁认为数据在多个事务之间很少会发生冲突,因此在读取数据时不对其加锁。当更新数据时,会检查数据是否自上次读取后已被其他事务修改(通常通过版本号或时间戳)。
-
优点:并发性能好,因为读取数据时不加锁。
-
缺点:在高并发情况下,可能会出现较多的更新冲突,需要重试。
悲观锁
-
核心思想:悲观锁认为数据在多个事务之间很容易发生冲突,因此在读取数据时立即加锁,以防止其他事务修改数据。
-
优点:数据一致性好,能够防止数据在并发环境下被多个事务同时修改。
-
缺点:并发性能较低,因为读取数据时就需要加锁,可能阻塞其他事务的访问。
简而言之,乐观锁适合读多写少的场景,而悲观锁适合写多读少的场景。
标签:事务,加锁,数据库,死锁,进程,资源 From: https://blog.csdn.net/longer_net/article/details/140228459