当前位置: 代码网 > it编程>数据库>Mysql > Mysql锁机制,行锁表锁的使用详解

Mysql锁机制,行锁表锁的使用详解

2025年06月10日 Mysql 我要评论
锁是计算机协调多个线程访问同一个系统资源的机制锁的分类:从数据的操作类型分为:读锁和写锁从数据的操作粒度分为:行锁和表锁表锁偏读,myisam存储引擎,开销小,加锁快,无死锁,锁定粒度大,并发度最低添

锁是计算机协调多个线程访问同一个系统资源的机制

锁的分类:

  • 从数据的操作类型分为:读锁和写锁
  • 从数据的操作粒度分为:行锁和表锁

表锁

偏读,myisam存储引擎,开销小,加锁快,无死锁,锁定粒度大,并发度最低

添加锁

lock table 数据表名 read/write,数据表2 read/write...

-- 比如我要给mylock表加读锁,给book表加写锁
lock table mylock read,book write;

查看那些表被加了锁

show open tables;

第三列in_use字段如果为1则表示加锁了

释放所有表的锁

unlock tables;

执行完该命令后在执行show open tables; 命令就会发现所有数据表的in_use字段都为0了。

加锁对我们的数据操作和系统性能有什么影响

结论:

如果在会话1中给某个数据表加了读锁,其他会话就只能查看该表的数据,会话1不能对当前表进行修改操作,会报错;会话1也不能查询其他未加锁的数据表。而其他会话如果对该数据表进行修改操作会一直阻塞,但不会报错,直到会话1执行unlock tables; 命令将锁释放掉 才会解除阻塞状态进而执行成功修改操作。其他会话还是可以查询其他表的数据。

如果在会话1中给某个数据表加了写锁,会话1能对当前表进行查询修改操作,但不能查询其他未加锁的数据表,其他会话不能对加了写锁的数据表进行查询修改操作,会阻塞住。

简而言之,读锁会阻塞写操作,但不会阻塞读操作;写锁会阻塞其他会话的增删改查操作。

具体步骤如下:

首先是读锁,

首先在当前会话1 对mylock数据表加读锁

lock table mylock read;

然后查看表的当前数据

另一个会话,另一个命令行窗口也能查看mylock数据表内容。

然后在会话1中 查询其他未锁定的表的数据,发现还是报错了

但会话2 还是可以查询其他表的数据,

这个时候在会话1 中对mylock数据表进行修改操作

update mylock set name='aaa' where id=1;

执行就会发现报错了,这是因为加了读锁后不能再对数据表进行修改操作了。

会话2 也如果对mylock表进行修改操作,会一直阻塞住,但不会报错。如果这个时候会话1执行unlock tables;进行解锁,会话2的修改操作就会立刻执行成功。

然后是写锁

在会话1中对mylock表加写锁

lock table mylock write;

会话1中读取自己当前锁的表,发现能读

但是会话2 读取mylock数据表就会出现阻塞,也不会报错 就一直等待,当会话1释放锁后就能读取到数据了。如果在会话1释放锁之前 会话2也能读取到数据,那原因是mysql有缓存,如果之前查询过一次,第二次查询就会从缓存中取数据。

会话1 对mylock表进行修改操作,发现也能成功

但会话2 连查询都不可以 修改操作也肯定不行。

会话1 查询其他未锁的表,会报错

会话2 查询其他表,能正常查询。

分析表的锁定

show status like 'table%';

这里两个状态变量记录mysql内部表级锁定的情况,两个变量分析如下:

  • table_locks_immediate : 产生表级锁定的次数,表示可以立即获取锁的查询次数,每立刻获取锁加1
  • table_locks_waited: 锁的等待情况,每等待一次,值加1,如果该数值很高则表示存在严重的表级锁争用情况

此外:myisam存储引擎读写锁调度是写优先,也就是说它不适合做为写为主的表的存储引擎。因为写锁后,其他线程就不能进行任何操作,会阻塞。

行锁

偏向于innodb存储引擎,开销大、加锁慢、会出现死锁、锁定粒度最小、发生锁冲突的概率最低,并发度最高。

myisam和innodb存储引擎最大的区别是:事务是否支持,表锁与行锁

接下里就是会行锁的理解,首先是建一个数据表,并且为这个数据表的两列都创建了单值索引

create index idx_til_a on text_innodb_lock(a);
create index idx_til_a on text_innodb_lock(b);

然后查询一次数据

首先还是一样开两个会话,mysql事务的隔离级别默认是可重复读。两个会话都想将自动提交关闭set autocommite=0;, 然后会话1对一个表进行修改操作,但没有提交,会话2查询这个表的数据能看到的是会话1修改前的值,然后会话1会话2都提交,会话2再读就写修改后的值了。这是之前事务的知识。

假如这两个会话,会话1先对这个数据表a=1的数据进行了修改,但没有提交,这个时候会话2也对a=1的数据进行修改操作,会话2的修改操作就会阻塞住,但不会报错。然后会话1提交,这时候会话2的修改操作也就解除阻塞了,然后会话2再提交

也就是两个会话 不能对同一行数据进行操作,会话2的更新操作会阻塞,需要等到会话1提交事务才能解除阻塞。

但如果两个会话 不是操作同一行数据则不会阻塞。

索引失效,行锁变表锁

在我们的sql如果导致了索引失效,那么就会把本来的行锁变为表锁

上面的数据表text_innodb_lock中 a字段是数字型,b字段是字符型

首先还是开两个会话,

关闭自动提交set autocommit=0;

会话1执行update tets_innodb_lock set b='4001' where a=4; 对a=4进行修改操作,但还没有提交

然后会话2执行update tets_innodb_lock set b='9001' where a=9; 对a=9进行修改操作,因为不是操作同一行数据,所以不会阻塞。

但如果索引失效,行锁就会变为表锁

会话1 对第三行进行修改操作,但未提交,这里故意将b=‘4000’ 写成了b=4000 使索引失效

然后会话2 对其他行进行修改,就阻塞住了

间隙锁

现在表的数据如下

为了做云计算和大数据分析,数据最好是连续的,可是上面并没有a=2的记录。

这个时候会话1 进行一个范围性的修改操作,回车后,未提交

update test_innodb_lock set b='aaa' where a>1 and a<6;

会话2 进行一个新增操作,插入一个a=2的记录。 这边就会被阻塞住。

然后会话1提交后 会话2的阻塞才会被解除。

当我们使用范围条件而不是相等条件检索数据,innodb会给符合条件的已有数据记录的索引项加锁,对于符合范围查询条件但并不存在的记录就交间隙。

危害就是:它会锁定整个范围内的所有索引键值,即使这个键值不存在,某些场景下会对系统的性能造成伤害。

如何手动锁定一行

首先在命令行输入begin;

然后要锁的哪一行,就先查询那一行,在where条件后面加上for update

这个时候这一行的数据就被锁了 你只需要进行你的操作即可,然后你再提交

在上面的表锁 可以通过show status like 'table%'命令来查看表的一些锁定情况,

当然 行锁也有show status like 'innodb_row_lock%'

这各个参数的意思是:

  • innodb_row_locak_current_waits:当前正在等待锁定的数量
  • innodb_row_locak_time:从系统启动到现在,锁定总时间长度
  • innodb_row_locak_time_avg:每次等待所花的平均时间
  • innodb_row_locak_time_max:从系统启动到现在,等待时间最长的一次时间
  • innodb_row_locak_waits:系统启动后到现在,总共等待的次数

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com