什么是数据库锁?
在数据库中,锁是一种协调多个进程或线程并发访问某一资源的机制。
有哪些锁?
- 按锁的粒度划分,可分为行级锁、表级锁、页级锁。(mysql支持)
- 按锁级别划分,可分为共享锁(读锁)、排他锁(写锁)、意向锁
- 按使用方式划分,可分为乐观锁、悲观锁
- 按加锁方式划分,可分为隐式锁(自动锁)、显式锁
- 按操作划分,可分为DML锁、DDL锁
行级锁
行级锁可以最大程度地支持并发处理(同时也带来了最大的锁开销),即并发最高。
支持引擎:InnoDB
特点:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发最高。
行级锁分为行共享锁和行排他锁。# 共享锁(S):SELECT ... LOCK IN SHARE MODE; -- 查出的记录行都会被锁住
# 排他锁(X):SELECT ... FOR UPDATE; -- 查出的记录行都会被锁住
表级锁
表锁是MySQL中最基本的锁策略,并且是开销最小的策略。也是MySQL中锁定粒度最大的。它会锁定整张表。
特点:开销小,加锁快;不会出现死锁;锁定粒度大,发出锁冲突的概率最高,并发最低。
支持引擎:MyISAM、MEMORY、InNoDB
分类:表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)
#上锁:lock table 表名 read(write),表名 read(write),.....;
#解锁:unlock tables; -- UNLOCK TABLES释放被当前会话持有的任何锁
页级锁
页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。
特点:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
共享锁
共享锁(share lock)又叫读锁,如果事务对A加上共享锁,则其他事务只能对A再加共享锁,不能加其他锁。共享锁的事务只能读数据,不能写数据。
排他锁
排他锁(exclusive lock)又叫写锁,如果事务对A加上排它锁,则其他事务都不能对A加任何类型的锁。获准排他锁的事务既能读数据,又能写数据。
意向锁
意向锁的存在是为了协调行锁和表锁的关系,支持多粒度(表锁与行锁)的锁并存。
意向锁是表级锁,指示事务稍后需要哪种类型的锁(共享或独占)才能用于表中的行。
意向锁分为意向共享锁和意向排他锁。
意向共享锁(IS锁):事务在请求S锁前,要先获得IS锁
意向排他锁(IX锁):事务在请求X锁前,要先获得IX锁
意向锁是InnoDB自动加的,不需用户干预。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于普通SELECT语句,InnoDB不会加任何锁;
意向锁理解:
在mysql中有表锁,读锁锁表,会阻塞其他事务修改表数据。写锁锁表,会阻塞其他事务读和写。
- Innodb引擎支持行锁。行锁分为行共享锁和行排他锁。其中,共享锁,一个事务对一行的共享只读锁;排它锁,一个事务对一行的排他读写锁。
- 这两中类型的锁共存的问题考虑这个例子:事务A锁住了表中的一行,让这一行只能读,不能写。之后,事务B申请整个表的写锁。如果事务B申请成功,那么理论上它就能修改表中的任意一行,这与A持有的行锁是冲突的。数据库需要避免这种冲突,就是说要让B的申请被阻塞,直到A释放了行锁。
数据库要怎么判断这个冲突呢?
- step1:判断表是否已被其他事务用表锁锁表
- step2:判断表中的每一行是否已被行锁锁住。
注意step2,这样的判断方法效率实在不高,因为需要遍历整个表。于是就有了意向锁。在意向锁存在的情况下,事务A必须先申请表的意向共享锁,成功后再申请一行的行锁。 在意向锁存在的情况下,上面的判断可以改成
- step1:不变
- step2:发现表上有意向共享锁,说明表中有些行被共享行锁锁住了,因此,事务B申请表的写锁会被阻塞。
注意:申请意向锁的动作是数据库完成的。就是说,事务A申请一行的行锁的时候,数据库会自动先开始申请表的意向锁。
共享锁、排他锁、意向锁的共存逻辑关系
隐式锁
会根据隔离级别在需要的时候自动加锁,并且所有的锁都是在同一时刻被释放,这被称为隐式锁,或称为自动锁。
显式锁
使用特定语句进行显示锁定叫显式锁。
# 共享锁(S):SELECT ... LOCK IN SHARE MODE;
# 排他锁(X) :SELECT ... FOR UPDATE;
乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段。
无论是悲观锁还是乐观锁,都是人们定义出来的概念,可以认为是一种思想。
乐观锁
乐观并发控制(又名“乐观锁”,Optimistic Concurrency Control,缩写“OCC”)。它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回滚。
实现方式
在数据库中,乐观锁的实现有两种方式:
- 使用版本号实现
每一行数据多一个字段version,每次更新数据对应版本号+1, 原理:读出数据,将版本号一同读出,之后更新,版本号+1,提交数据版本号大于数据库当前版本号,则予以更新,否则认为是过期数据,重新读取数据
- 使用时间戳实现
每一行数据多一个字段time 原理:读出数据,将时间戳一同读出,之后更新,提交数据时间戳等于数据库当前时间戳,则予以更新,否则认为是过期数据,重新读取数据
悲观锁
当我们要对一个数据库中的一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行加锁以防止并发。这种借助数据库锁机制在修改数据之前先锁定,再修改的方式被称之为悲观并发控制(又名“悲观锁”,Pessimistic Concurrency Control,缩写“PCC”)。
实现方式
在数据库中,悲观锁的流程如下:
在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)。 如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。 具体响应方式由开发者根据实际需要决定。 如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。 其间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常。
ps:行锁、表锁、读锁、写锁都是在操作之前先上排他锁。
DML锁
DML锁,也称为数据锁,保证被多个用户并发防伪的数据的完整性。DML语句自动获得行锁和表锁。
DDL锁
当正在进行的DDL操作对对象执行或引用时,ddl(data dictionary lock)锁保护schema对象的定义。在DDL操作期间,只有被修改或引用的单独的schema对象被锁定,数据库从不锁定整个数据字典。
Oracle数据库自动为需要它的任何DDL事务自动获取DDL锁。用户不能显式请求DDL锁。
DDL锁也分为共享DDL锁和排他DDL锁。
排它DDL锁:Exclusive DDL Lock防止其他会话获得DDL或DML锁。
共享DDL锁:Share DDL Locks防止对有冲突的DDL操作的破坏性干扰,但允许相似DDL操作的数据并发。
附参考资料:
第十篇:数据库锁机制
16、数据库锁机制