一、MySQL锁
锁是计算机协调多个进程或线程并发访问某一资源的机制。 在数据库中,除传统的计算资源(如CPU、RAM、I/O等)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一 个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。
很多人在开发过程中,应该注意到很多锁的问题,在数据库中,有时候我们没有给SQL语句加锁,这些SQL语句也正常的运行,那是因为MySQL自动帮我们加了隐式锁,在某些其他特殊情况,可能会需要我们自己主动加锁。
1、锁分类
在MySQL中,锁机制比较简单,并且不同的存储引擎支持不同的锁机制。
- MyISAM 存储引擎采用表锁。
- BDB 存储引擎采用页面锁,也支持表锁。
- InnoDB 存储引擎即支持表锁,也支持行锁,默认使用行锁。
从性能上分为:悲观锁 和 乐观锁。
从数据操作类型上分为:读锁 和 写锁。(都属于悲观锁)
读锁(共享锁):针对同一份数据,多个读操作可以同时进行并且不会相互影响
写锁(排他锁):在写操作完成之前,会阻塞其他写锁和读锁。
从数据操作粒度上分为:表锁 和 行锁。
1.1 表锁
每次操作锁住整张表,开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
- 手动增加表锁:lock table 表名称 read(write),表名称2 read(write);
- 查看表锁:show open tables;
- 删除表锁:unlock tables;
1)手动增加表读锁
表锁语句
当前session和其他session都可以读该表
查询结果
当前session中插入或者更新锁定的表都会报错,其他session插入或更新则会等待
更新结果
2)手动增加表写锁
查询结果
更新结果
3)查看表锁情况
查看表锁情况
4)删除表锁
删除表锁
结论:MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行增删改操作前,会自动给涉及的表加写锁。表锁中读锁会阻塞写,但是不会阻塞读;写锁则会阻塞读和写。
1.2 行锁
每次操作锁住一行数据。开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度最高。
上面说了,MyISAM 只支持表锁,InnoDB可以支持表锁和行锁,但是,InnoDB只有通过索引条件检索数据才使用行级锁,否则,InnoDB也会走表锁。所以说:InnoDB的行锁是基于表索引的。
同时,InnoDB 与 MyISAM 引擎的不同之处还有,InnoDB支持事务
。
1、行锁支持事务
在并发事务中,可能会带来如下问题:
- 脏读:事务A读取到了事务B已经修改但尚未提交的数据,还在这个数据基础上做了操作。(针对未提交数据)
- 不可重复读:事务A读取到了事务B已经提交的修改数据。(针对其他提交前后,读取数据本身的对比)
- 幻读:事务A读取到了事务B提交的新增数据。(针对其他提交前后,读取数据条数的对比)
- 更新丢失:当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题–最后的更新覆盖了由其他事务所做的更新。
1) 事务的隔离级别
脏读”、“不可重复读”和“幻读”,其实都是数据库读一致性问题,必须由数 据库提供一定的事务隔离机制来解决。
事务的隔离级别就是通过锁的机制来实现,锁的应用最终导致不同事务的隔离级别,只不过隐藏了加锁细节,事务的隔离级别有4种:
数据库的事务隔离越严格,并发副作用越小,但付出的代价也就越大,因为事务隔 离实质上就是使事务在一定程度上“串行化”进行,这显然与“并发”是矛盾的。
mysql默认事务隔离级别为:可重复读(REPEATABLE-READ)
查看mysql事务隔离级别:
5.7版本:
show variables like 'tx_isolation';
8.0版本:
show variables like 'transaction_isolation';
2、行锁分析
通过检查InnoDB_row_lock状态变量来分析系统上的行锁的争夺情况
show status like '%Innodb_row_lock%'
image.png
对各个状态量的说明如下:
Innodb_row_lock_current_waits: 当前正在等待锁定的数量
Innodb_row_lock_time: 从系统启动到现在锁定总时间长度
Innodb_row_lock_time_avg: 每次等待所花平均时间
Innodb_row_lock_time_max: 从系统启动到现在等待最长的一次所花时间
Innodb_row_lock_waits: 系统启动后到现在总共等待的次数
优化建议:
- 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁
- 合理设计索引,尽量缩小锁的范围尽可能减少检索条件范围,避免间隙锁
- 尽量控制事务大小,减少锁定资源量和时间长度,涉及事务加锁的sql尽量放在事务最后执行
- 尽可能低级别事务隔离
网友评论