Mysql锁
既然说到了Mysql锁,那么什么是Mysql锁呢?本质上来说,锁是一种协调多个进程或者多个线程对某一资源的访问的机制,Mysql通过锁实现了事务的隔离级别;通俗来说就是当应用访问某一资源时,会对当前资源加锁,防止其他请求来访问该资源,导致产生问题,待当前应用对这一资源访问结束后再释放锁供其他请求获取该资源。
Mysql锁的分类
Mysql锁主要从以下几个方面来分类
从性能上
Mysql锁可以分为悲观锁和乐观锁:
悲观锁:就是说不相信在整个数据处理过程中其他应用不会修改数据库中的数据,一旦获取资源就立刻加锁,在整个数据处理过程中都将相应数据锁定,其他应用无法修改数据。读数据需要加锁,此时不能对这些数据进行修改操作;修改数据也需要加锁,此时不能对这些数据进行读取操作。
乐观锁:就是说为数据增加一个版本标识,查询数据时会将版本号标识一起读出来,在更新数据时,令版本号加1,提交数据时与数据库记录版本对比,如果提交版本号大于数据库中版本号则修改,否则不能修改。
从操作类型上
Mysql锁分为读锁和写锁:
读锁:也叫做共享锁,同一份数据可以加多个读锁。
写锁:也叫做排他锁,如果当前数据的写锁未释放,则不能再加写锁或读锁。
从锁的粒度上
Mysql锁分为表锁、行锁和页面锁:
表锁:就是整个表加锁,开销小,加锁快,一般不会死锁,锁粒度比较大,发生冲突概率性高。
行锁:就是在数据行上加锁,开销比较大,加锁慢,可能会死锁,锁粒度最小,发生冲突概率小,并发高。
页面锁:就是页面级别加锁,开销介于表锁和行锁之间,可能会死锁,锁粒度介于表锁和行锁之间。
从锁的粒度上还有两种锁:间隙锁和临键锁,这两种锁遇到的不到,这里就不再详细阐述了,有兴趣的小伙伴可以自行搜索相关文章介绍。
死锁
既然上面说到了可能会死锁,那么死锁是怎么产生的呢?什么情况下会出现死锁?如何避免死锁呢?
死锁的四个条件
产生死锁需要四个必要条件:互斥条件、不可剥夺条件、请求保持条件、循环等待条件。
互斥条件:就是说某个资源只能被以及各进程或者线程占用,其他进程或线程请求该资源必须等待。
不可剥夺条件:就是说某个资源在该进程或线程使用完之前,不能强行被剥夺,只能由其自行释放。
请求保持条件:有一个进程或者线程已经获得了一个资源,现在要请求其他资源,但请求的其他资源被其他进程占用,此时请求被阻塞,并且不会释放自己的资源。
循环等待条件:有A、B、C三个进程或者线程同时占用自己的资源同时又想获取别的资源
图中A占用自己资源同时请求B的资源,B占用自己资源同时请求C资源,C占用自己资源同时请求A资源,这样就行程了循环等待。
死锁的验证
开启客户端A、B连接Mysql数据库,只需要通过其中一个客户端A创建数据库,数据表即可
create database mytest;
查看数据库创建成功与否
show databases;
选中当前数据库,创建数据表sys_user并插入数据
use mytest;CREATETABLE `sys_user` ( `user_id` bigint(20)NOTNULL AUTO_INCREMENT COMMENT '用户ID', `dept_id` bigint(20) DEFAULT NULL COMMENT '部门ID', `login_name` varchar(30)NOTNULL COMMENT '登录账号', `user_name` varchar(30) DEFAULT '' COMMENT '用户昵称', `user_type` varchar(2) DEFAULT '00' COMMENT '用户类型(00系统用户 01注册用户)', `email` varchar(50) DEFAULT '' COMMENT '用户邮箱', `phonenumber` varchar(11) DEFAULT '' COMMENT '手机号码', `sex` char(1) DEFAULT '0' COMMENT '用户性别(0男 1女 2未知)', `avatar` varchar(100) DEFAULT '' COMMENT '头像路径', `password` varchar(50) DEFAULT '' COMMENT '密码', `salt` varchar(20) DEFAULT '' COMMENT '盐加密', `status` char(1) DEFAULT '0' COMMENT '帐号状态(0正常 1停用)', `del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', `login_ip` varchar(50) DEFAULT '' COMMENT '最后登陆IP', `login_date` datetime DEFAULT NULL COMMENT '最后登陆时间', `create_by` varchar(64) DEFAULT '' COMMENT '创建者', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `update_by` varchar(64) DEFAULT '' COMMENT '更新者', `update_time` datetime DEFAULT NULL COMMENT '更新时间', `remark` varchar(500) DEFAULT NULL COMMENT '备注', PRIMARY KEY (`user_id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户信息表';INSERTINTO `sys_user` VALUES('1','103','admin','若依','00','ry@163.com','15888888888','1','','29c67a30398638269fe600f73a054934','111111','0','0','127.0.0.1','2020-11-24 13:32:43','admin','2018-03-16 11:33:00','ry','2020-11-24 13:32:15','管理员');INSERTINTO `sys_user` VALUES('2','105','ry','若依','00','ry@qq.com','15666666666','1','','8e6d98b90472783cc73c17047ddccf36','222222','0','0','127.0.0.1','2018-03-16 11:33:00','admin','2018-03-16 11:33:00','ry','2018-03-16 11:33:00','测试员');
在当前客户端A设置事务隔离级别可重复读,开启事务,为user_id=1数据添加排他锁
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;START TRANSACTION;SELECT*FROM sys_user WHERE user_id=1 FOR UPDATE;
命令行执行结果
同样在客户端B对user_id=2进行相同操作,执行结果如图
之后在终端A为user_id=2添加排他锁,线程卡住,返回错误信息
错误信息
ERROR 1205(HY000): Lock wait timeout exceeded; try restarting transaction
此时在终端B为user_id=1添加排他锁,返回错误信息
错误信息内容
1213- Deadlock found when trying to get lock; try restarting transaction
通过命令查看Mysql死锁信息
SHOW ENGINE INNODB STATUS;
结果如图
或者可以通过命令查看锁信息
select*from information_schema.innodb_trx;
查询结果如图
死锁的避免
1.尽量让数据检索都通过索引完成,避免无效索引导致行锁升级成了表锁;
2.合理设计索引,尽量缩小锁的范围;
3.尽量控制事务大小,减少一次事务锁定的资源数量,缩短资源锁定时间;
4.尽量减少查询条件范围;
5.尽可能使用低级别的事务隔离机制。
总结
以上就是Mysql锁相关内容,希望对大家有所帮助。