重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
众所周知,innodb是默认行锁,当然也支持表锁。如下是对于行锁的算法进行的一些实验。
创新互联公司专注于北关企业网站建设,响应式网站建设,商城网站定制开发。北关网站建设公司,为北关等地区提供建站服务。全流程定制开发,专业设计,全程项目跟踪,创新互联公司专业和态度为您提供的服务
锁的算法为:我知道是行锁,但是是如何锁的,锁多少数据
假如有个索引是:[1,2,3,7]
record lock 锁的是 1,2,3,7
gap lock 锁的是 (- ,1),(2,3),(3,7),(7,+ )反正锁的就是区间,不是行
next-key lock锁的是 (- ,1],[2,3),[3,7),[ 7,+ )既锁范围也锁行
Innodb锁算法规则如下:
在可重复读隔离级别下,innodb默认使用的是next-key lock算法,当查询的索引是主键或者唯一索引的情况下,才会退化为record lock,在使用next-key lock算法时,不仅仅会锁住范围,还会给范围最后的一个键值加一个gap lock。
其中lockmode中的X锁为左边会话中的锁,因为需要显式的commit之后才会释放锁,第二个S锁,为右边的共享锁,因为主键ID为1的已经被锁住了,所以处于锁等待状态,锁的类型为record lock
使用辅助索引a=8进行操作,这个时候理论应该对主键索引加record lock 则 主键ID=8的被锁,然后辅助索引被加next-key lock 则为:
(7,8] 然后对下一个键值加gap锁,则为:(8,11)
所以目前被锁住的记录为:
1.主键为8的被锁
2.辅助索引8的被锁
3.辅助索引8到11之间的被锁,意味着你这个时候往8到11之间写数据会报错
当使用范围条件进行更新时,此时肯定是需要加X锁的,我是用的也是主键,所以按照理论应该是加的record lock ,但是却加了gap lock,因为插入值为10的阻塞了,查看information 也提示X.GAP
这个有点晕为啥主键变成了next-key lock ,不应该是record lock么?
update20200515
在知乎看到的一个解释:
即,在无论使用主键索引还是非主键索引的时候,请求共享锁或者排他锁,innodb会给范围内的记录加锁,而范围内的间隙也会被加锁,
例如一个表t 的 id为1,2,3,7,10
假如执行如下:
select * from t where id =3 for update
那么这个时候执行
insert into t(id) values(8) 会被阻塞,因为是在请求排他锁时使用了范围,所以[3,10],甚至10以后的任何数据都无法插入。
执行
select * from t where id =3 lock in share mode
insert into t(id) values(8) 会被阻塞,因为是在请求共享锁时使用了范围,所以[3,10],甚至10以后的任何数据都无法插入。
幻读是同一事务下,连续执行两次同样的sql可能导致不同的结果,第二次返回的数据可能导致以前不存在的行。
同时一般会问它和脏读的区别,脏读为读取到其他事务未提交的数据,但是幻读是读取的其他事务已经提交的数据。
reference:
1、在mysql数据库中如何锁定一行数据,保证不被其他的操作影响。
2、从对数据的操作类型分为读锁和写锁。从对数据操作的粒度来分:表锁和行锁。
3、现在我们建立一个表来演示数据库的行锁讲解。
4、行锁基本演示如下图所示。
5、如果两个会话操作的是不同的行,就不会互相阻塞了。
锁是计算机协调多个进程或线程并发访问某一资源的机制,在数据库中,除传统的计算资源(CPU、RAM、I/O)争用外,数据也是一种供许多用户共享的资源,如何保证数据并发访问的一致性,有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素,从这个角度来说,锁对数据库而言是尤其重要,也更加复杂。MySQL中的锁,按照锁的粒度分为:1、全局锁,就锁定数据库中的所有表。2、表级锁,每次操作锁住整张表。3、行级锁,每次操作锁住对应的行数据。
全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML的写语句,DDL语句,已经更新操作的事务提交语句都将阻塞。其典型的使用场景就是做全库的逻辑备份,对所有的表进行锁定,从而获取一致性视图,保证数据的完整性。但是对数据库加全局锁是有弊端的,如在主库上备份,那么在备份期间都不能执行更新,业务会受影响,第二如果是在从库上备份,那么在备份期间从库不能执行主库同步过来的二进制日志,会导致主从延迟。
解决办法是在innodb引擎中,备份时加上--single-transaction参数来完成不加锁的一致性数据备份。
添加全局锁: flush tables with read lock; 解锁 unlock tables。
表级锁,每次操作会锁住整张表.锁定粒度大,发送锁冲突的概率最高,并发读最低,应用在myisam、innodb、BOB等存储引擎中。表级锁分为: 表锁、元数据锁(meta data lock, MDL)和意向锁。
表锁又分为: 表共享读锁 read lock、表独占写锁write lock
语法: 1、加锁 lock tables 表名 ... read/write
2、释放锁 unlock tables 或者关闭客户端连接
注意: 读锁不会阻塞其它客户端的读,但是会阻塞其它客户端的写,写锁既会阻塞其它客户端的读,又会阻塞其它客户端的写。大家可以拿一张表来测试看看。
元数据锁,在加锁过程中是系统自动控制的,无需显示使用,在访问一张表的时候会自动加上,MDL锁主要作用是维护表元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作。为了避免DML和DDL冲突,保证读写的正确性。
在MySQL5.5中引入了MDL,当对一张表进行增删改查的时候,加MDL读锁(共享);当对表结构进行变更操作时,加MDL写锁(排他).
查看元数据锁:
select object_type,object_schema,object_name,lock_type,lock_duration from performance_schema_metadata_locks;
意向锁,为了避免DML在执行时,加的行锁与表锁的冲突,在innodb中引入了意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查。意向锁分为,意向共享锁is由语句select ... lock in share mode添加。意向排他锁ix,由insert,update,delete,select。。。for update 添加。
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.data_lock;
行级锁,每次操作锁住对应的行数据,锁定粒度最小,发生锁冲突的概率最高,并发读最高,应用在innodb存储引擎中。
innodb的数据是基于索引组织的,行锁是通过对索引上的索引项加锁来实现的,而不是对记录加的锁,对于行级锁,主要分为以下三类:
1、行锁或者叫record lock记录锁,锁定单个行记录的锁,防止其他事物对次行进行update和delete操作,在RC,RR隔离级别下都支持。
2、间隙锁Gap lock,锁定索引记录间隙(不含该记录),确保索引记录间隙不变,防止其他事物在这个间隙进行insert操作,产生幻读,在RR隔离级别下都支持。
3、临键锁Next-key-lock,行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap,在RR隔离级别下支持。
innodb实现了以下两种类型的行锁
1、共享锁 S: 允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
2、排他锁 X: 允许获取排他锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排他锁。
insert 语句 排他锁 自动添加的
update语句 排他锁 自动添加
delete 语句 排他锁 自动添加
select 正常查询语句 不加锁 。。。
select 。。。lock in share mode 共享锁 需要手动在select 之后加lock in share mode
select 。。。for update 排他锁 需要手动在select之后添加for update
默认情况下,innodb在repeatable read事务隔离级别运行,innodb使用next-key锁进行搜索和索引扫描,以防止幻读。
间隙锁唯一目的是防止其它事务插入间隙,间隙锁可以共存,一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用的间隙锁。
MySQL8.x 中新增了一个轻量级的备份锁,它允许在 online 备份的时候进行 DML 操作,同时可防止快照不一致。这个锁禁止的操作很少,它禁止的操作包括:
文件的创建、删除、改名
账户的管理
REPAIR TABLE
TRUNCATE TABLE
OPTIMIZE TABLE
备份锁由 lock instance for backup 和 unlock instance 语法组成。使用这些语句需要 BACKUP_ADMIN 权限。
MyISAM和InnoDB存储引擎使用的锁:
封锁粒度小:
由于InnoDB存储引擎支持的是行级别的锁,因此意向锁(因为意向锁是表锁)其实不会阻塞除全表扫以外的任何请求。故表级意向锁与行级锁的兼容性如下所示
参考
参考
行锁的三种算法:
这条语句阻止其他事务插入10和20之间的数字,无论这个数字是否存在。 间隙可以跨越0个,单个或多个索引值。
共享锁:
排他锁:
乐观锁:总是假设最好的情况,每次去拿数据的时候都认为别人不会修改(天真), 操作数据时不会上锁 ,但是 更新时会判断在此期间有没有别的事务更新这个数据,若被更新过,则失败重试 ;适用于读多写少的场景。
乐观锁的实现方式 有:
关闭自动提交后,我们需要手动开启事务。
上述就实现了悲观锁,悲观锁就是悲观主义者,它会认为我们在事务A中操作数据1的时候,一定会有事务B来修改数据1,所以,在第2步我们将数据查询出来后直接加上排它锁(X)锁,防止别的事务来修改事务1,直到我们commit后,才释放了排它锁。