聊聊Mysql锁问题

聊聊Mysql锁问题

  • 一、 Mysql锁概述
  • 二、Myisam锁问题
    • 2. 1 表级锁的锁模式
    • 2.2 如何加表锁
    • 2.3 并发插入
    • 2.4 Myisam的锁调度
  • 三、InnoDB锁问题
    • 3.1 事务及ACID属性
    • 3.2 并发事务处理带来的问题
    • 3.3 事务隔离级别
    • 3.4 获取InnoDB行锁争用情况
    • 3.5 InnoDB行锁模式及加锁方法
    • 3.6 InnoDB行锁实现方式
    • 3.6 间隙锁(Next-key)锁
    • 3.7 InnoDB锁机制设计对恢复和复制的思考
    • 3.8 什么时候使用表锁
    • 3.9 关于死锁

一、 Mysql锁概述

相较于其他数据库而言,Mysql锁机制比较简单,不同的引擎支持不同的锁机制。

  1. Myiasm和Memory存储引擎采用的是表级锁
  2. BDB存储引擎采用的是页面锁,但也支持表级锁
  3. InnoDB存储引擎既支持行级锁,也支持表级锁,默认情况采用行级锁

三种锁的种类归纳如下:

  1. 表级锁:开销小,加锁快,不会出现死锁,锁定粒度大,发生锁冲突的概率最高,并发度最低。
  2. 行级锁:开销大,加锁慢,会出现死锁,锁定粒度最小,发生锁冲突的概率最低,并发度也很高。
  3. 页面锁:开销和加锁时间介于行锁和表锁之间,会出现死锁,锁定粒度位于行锁和表锁中间,并发度一般。

锁适用范围:

  1. 表级锁:更适合查询为主,只有少量按索引条件更新数据的应用,如web应用;
  2. 行级锁:更适合有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用,如OLTP系统;

二、Myisam锁问题

2. 1 表级锁的锁模式

  1. 表共享读锁
  2. 表独占写锁

对Myisam表的读操作,不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写操作。
对Myisam的写操作,会阻塞其他用户对同一表的读和写操作;
Myiasm表的读操作和写操作之间是串行的!

2.2 如何加表锁

lock table table_name read local;
select xx from table_name;
unlock table_name;

上述案例加上local选项,作用是满足myisam表并发插入条件的情况下,允许其他用户在表尾并发插入记录。
在用lock tables给表显式加锁时,必须同时取得所有涉及到表的锁,并且Mysql不支持锁升级。也就是在执行lock tables后,只能访问显式加锁的这些表,不能访问未加锁的表。
如果加的是读锁,那么只能执行查询操作,而不能执行更新操作。
当lock tables时,不仅需要一次锁定用到的所有表,而且,同一个表在SQL语句中出现多少次,就要通过与SQL语句中相同的别名锁定多少次,否则也会出错。

2.3 并发插入

myisam表的读和写是串行的,但在一定条件下,myiasm表也支持查询和插入的并发执行。
myisam存储引擎有一个系统变量concurrnet_insert,专门用以控制其并发插入的行为,其值分别可以为0、1、2.

  1. 当设置为0时,不允许并发插入;
  2. 当设置为1时,如果myisam表中没有空洞(即表的中间没有被删除的行),myisam允许在一个进程读表的同时,另一个进程从表尾插入数据。(默认设置)
  3. 当设置为2时,无论myisam有无空洞,都允许在表尾并发插入记录。

可以利用myisam存储引擎的并发插入特性,来解决应用中对于同一表查询和插入的锁争用。例如,将concurrent_insert系统变量设置为2,总是允许并发插入;同时通过定期在系统空闲时间段执行optimize table语句来整理表空间碎片,收回因删除记录而产生的中间空洞。

2.4 Myisam的锁调度

myisam存储引擎的读锁和写锁是互斥的,读写操作是串行的。那么一个进程请求某个myisam表的读锁,同时另一个进程也i请求同一张表的写锁,此时Mysql怎么处理?
写进程会先获得锁,即使读请求先到锁等待队列,写请求后到,写锁也会插入到读锁之前!这是因为Mysql认为写请求一般比读请求更重要!这也是myisam表不太适合有大量更新操作和查询操作应用的原因。因为大量的更新操作会造成查询操作的阻塞。
当然mysql提供了一些参数可以调节myisam的调度行为:

  1. 通过指定启动参数low-priority-updates,是myisam引擎默认给予读请求以优先的权利。
  2. 通过执行命令set low_priority_updates=1,使该连接发出的请求更新优先级降低/
  3. 通过指定insert、update、delete语句的low_priority属性,降低该语句的优先级。
  4. 通过设置系统参数max_write_lock_count的值,当表锁达到某个值之后,暂时降低写请求的优先级。

注意:应用中应该尽量避免长时间运行的查询操作,在可能的情况下使用中间表等措施对SQL做一定程度的分解,使得每一步查询都能在较短的时间内完成,从而减少锁冲突。
如果复杂查询不可避免,应尽可能的安排在数据库空闲时间段内执行,比如一些定期统计可以在夜间执行。

三、InnoDB锁问题

InnoDB和Myisam的最大不同点:

  1. 支持事务
  2. 采用了行级锁

3.1 事务及ACID属性

事务是由一组SQL语句组成的逻辑处理单元,事务具备ACID 4个属性。

  1. 原子性:事务是一个原子操作单元,对数据的修改,要么全部执行,要么全部不执行;
  2. 一致性:在事务开始和完成时,数据都必须保持一致状态;
  3. 隔离性:系统提供隔离机制,保证事务在不受外部并发操作影响的独立环境执行;
  4. 持久性:事务完成之后,对于数据的修改是永久性的。

3.2 并发事务处理带来的问题

相对于串行处理来说,并发事务处理能大大增强数据库资源的利用率,提高数据库系统的事务吞吐量,从而可以支持更多的用户。但并发事务也会带来如下几个问题:

  1. 更新丢失:两个事务选择了同一行,最后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题;
  2. 脏读:一个事务正在对一条记录做修改,在这个事务完成并提交前,这奥记录的数据就处于不一致状态,此时另一个事务也来读取同条记录,如果不加控制,第二个事务读取了这些脏数据,并据此做了进一步的处理,就会产生未提交的数据依赖关系;
  3. 不可重复读:一个事务在读取某些数据后的某个时间,再次读取以前的数据,却发现读取的数据发生了改变,或者这些记录已经被删除了;
  4. 幻读:一个事务按相同查询条件重新读取以前检索的数据,却发现其他事务插入了满足其查询条件的新数据。

3.3 事务隔离级别

上面提到的并发事务带来的问题必须由数据库提供一定的事务隔离机制来解决,数据库实现事务隔离的方式,基本上分为两种:

  1. 在读取数据前,对其加锁,防止其他事务对数据进行修改;
  2. 不用加任何所,通过一定机制生成一个数据请求时间点的一致性数据快照,并通过这个快照来提供一定级别(语句级或者事务级)的一致性读取。即MVCC,数据多版本并发控制。

数据库的事务隔离越严格,并发副作用越小,但付出的代价也就越大。事务隔离实质上就是使事务在一定程度上“串行化”进行。

为了解决“隔离”与“并发”的矛盾,ISO/ANSI SQL92定义了4个事务隔离级别。

  1. 未提交读:最低级别,只能保证不读取物理上损坏的数据,会产生脏读、不可重复读、幻读情况。
  2. 已提交读:语句级,不会产生脏读,会产生不可重复读、幻读情况。
  3. 可重复读:事务级,不会产生脏读和不可重复读,会产生幻读情况。
  4. 可序列化:最高级别,事务级,不会产生脏读、不可重复读、幻读情况。

注意:不一定所有的数据库都会完全实现以上4个隔离级别,如ORACLE只提供了已提交读和可序列化两个标准隔离级别;SQL Server除了上述4个隔离级别,还支持一个“快照”的隔离级别;Mysql支持全部4个隔离级别。

3.4 获取InnoDB行锁争用情况

mysql> show status like 'innodb_row_lock%';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| Innodb_row_lock_current_waits | 0     |
| Innodb_row_lock_time          | 0     |
| Innodb_row_lock_time_avg      | 0     |
| Innodb_row_lock_time_max      | 0     |
| Innodb_row_lock_waits         | 0     |
+-------------------------------+-------+
5 rows in set (0.00 sec)

3.5 InnoDB行锁模式及加锁方法

InnnoDB实现了2种类型的行锁:

  1. 共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁;
  2. 排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享锁和排他写锁。

为了允许行锁和表锁并存,实现多粒度机制,InnoDB还有两种内部使用的意向锁,这两种意向锁都是表锁。

  1. 意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁;
  2. 意向排他锁(IX):事务打算给数据行加排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。

如果一个事务请求的锁模式与当前锁兼容,InnoDB就将该请求的锁授予该事务,反之则要等待锁释放。
意向锁是InnoDB自动加的,不需用户干预,对于UPDATE、DELETE、INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于普通的SELECT语句,InnoDB不会加任何锁。

-- 共享锁
select ** from table_name where ... lock in share mode;-- 排他锁
select * from table_name where ... for update;

3.6 InnoDB行锁实现方式

InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点mysql和oracle不同,oracle是通过在数据块中对相应的数据行加锁来实现的。
InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则InnoDB将使用表锁!

  1. 在不通过索引条件查询的时候,InnoDB使用的是表锁,而不是行锁。
  2. mysql的行锁是针对索引家的锁,而不是针对记录加的锁,如果使用相同的索引键,还是会出现锁冲突的。
  3. 当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行;
  4. 即使条件中使用了索引字段,但是否使用索引来检索数据是由mysql判断不同执行计划的代价来决定的。

3.6 间隙锁(Next-key)锁

当我们用范围条件而不是相等条件检索数据,并请求共享锁或排他锁的时候,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙”,InnoDB会对这个间隙加上锁。
使用间隙锁的目的:

  1. 防止幻读,以满足隔离级别的要求;
  2. 满足其恢复和复制的需要

3.7 InnoDB锁机制设计对恢复和复制的思考

Mysql通过binlog记录执行成功的insert、update、delete等更新数据的sql语句,并由此实现Mysql数据库的恢复和主从复制。
Mysql恢复机制有以下特点:

  1. 恢复是基于SQL语句级别的,也就是执行BINLOG中的SQL语句,Oracle是基于数据文件块的;
  2. Mysql的binlog是按照事务提交的先后顺序记录的,恢复也是按照这个顺序执行,Oracle是按照系统更新号SCN来恢复数据的。

从上边两点可知,mysql恢复机制要求,在一个事务未提交之前,其他并发事务不能插入满足其锁定条件的任何记录,也就是不允许出现幻读,实际上就是要求事务要串行化。这也是很多情况下,InnoDB要用到间隙锁的原因。比如在用范围条件更新记录时,不论在读已提交还是可重复读隔离级别下,InnoDB都要使用间隙锁。

3.8 什么时候使用表锁

  1. 事务需要更新大部分数据或全部数据,表又比较大的情况下;
  2. 事务涉及多个表,比较复杂,很可能引起死锁,造成大量事务回滚;

注意:

  1. lock tables虽然可以给InnoDB加表级锁,但表锁不是由InnoDB存储引擎管理的,而是由上一层Mysql Server负责的,仅当autocommit=0、innodb_table_locks=1(默认设置)时,InnoDB层才能知道Mysql加的表锁,Mysql Server才能感知到InnoDB加的行锁。这种情况下,InnoDB才能自动识别涉及表级锁的死锁。否则,InnoDB将无法自动检测并处理这种死锁。
  2. 在用lock tables对InnoDB表加锁时候,要将autocommit设为0,否则Mysql不会给表加锁;事务结束前,不要用unlock tables释放表锁,因为会隐式提交事务;commit或rollback并不能释放用lock tables加的表锁,必须用unlock tables。
set autocommit=0;
lock table table_name1 write, table_name2 read, ...;
commit;
unlock tables;

3.9 关于死锁

Myisam表锁是deadlock free的,但在 InnoDB 中,除单个 SQL 组成的事务外,锁是逐步获得的,这就决定了在 InnoDB 中发生死锁是可能的。
两个事务都需要获得对方持有的排他锁才能继续完成事务,这种循环锁等待就是典型的死锁。
发生死锁后,InnoDB一般都能自动检测到,并使一个事务释放锁并回退;另一个事务获得锁,继续完成事务。
但在涉及外部锁或者表锁的时候,InnoDB并不能完全自动检测到死锁,这需要通过设置锁等待超时参数InnoDB_lock_wait_timeout来解决。这个参数不仅用来解决死锁问题,还可以针对并发较高的情况下,如果大量事务因无法立即获得所需的锁而挂起,会占用大量计算机资源,造成严重性能问题,甚至拖垮数据库。我们通过设置合适的锁等待超时阈值,可以避免这种情况发生。
通常情况下,死锁都是由于应用设计问题,通过调整业务流程、数据库对象设计、事务设计、以及访问数据库的SQL语句,绝大部分死锁都可以避免。

  1. 在应用中如果程会并发读取多个表,应尽量约定以相同的顺序访问表,这个可以大大降低死锁的机会。
  2. 在程序以批量方式处理的数据,如果事先对数据排序,保证每个线程按固定的顺序来处理记录,也可以大大降低出现死锁的可能。
  3. 在事务中,如果要更新记录,应该直接申请足够级别的锁,即排他锁,而不应先申请共享锁,更新时再申请排他锁,因为当用户申请排他锁时,其他事务可能又已经获得了相同记录的共享锁,从而造成锁冲突,甚至死锁。
  4. 在可重复读隔离级别下,如果两个线程同时对相同记录使用select … for update加排他锁,在没有符合该条件记录情况下,两个线程都会加锁成功。程序发现记录尚不存在,就试图插入一条数据,如果两个线程都这么干,就会出现死锁。这种情况下将隔离级别改成read commited,就可以避免问题。
  5. 在读已提交隔离级别下,如果两个线程都执行select … for update,判断是否存在符合条件的记录,如果没有就插入记录,此时,只有一个线程能插入成功,另一个线程会出现锁等待,当1个线程提交完成后,第2个线程会因主键冲突而出错,但是虽然这个线程出错了,也会获得一个排他锁!此时,如果有第3个线程来申请排他锁,也会出现锁。

通过上边的设计和SQL优化措施,可以大大减少死锁,但死锁很难完全避免,在程序设计上总是捕获并处理死锁是一个良好的编程习惯。
如果出现死锁,可以使用show engine innodb status\G;命令来确定最后一个死锁产生的原因,返回结果中包含死锁相关事务的详细信息。

引发死锁的SQL语句
事务已经获得的锁
正在等待什么锁
被回滚的事务等

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/11145.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sql-行转列(转置)

- 行转列的常规做法是,group bysum(if())【或count(if())】 例题: 已知 yearmonthamount199111.1199121.2199131.3199141.4199212.1199222.2199232.3199242.4 查成这样一个结果 yearm1m2m3m419911.11.21.31.419922.12.22.32.4 解答: us…

串转并74hC595控制流水灯

#include <reg51.h> //重新定义系统数据类型 typedef unsigned int u16; typedef unsigned char u8; //宏定义数据端口 #define LEDDZ_COL_PORT P0 #define SER_AP_PORT P1 //定义数据存储端口 sbit SERP3^4; sbit SRCLKP3^6; sbit RCLKP3^5; //依次闪烁…

WPS表格:对比少于1万的两列数据

当我们需要对于A、B两列乱序的数据&#xff0c;找出A列中某一项B列有没有&#xff0c;或者找出B列中的某一项A列有没有&#xff0c;都可以先将这两列数据放入WPS表格中&#xff1a; 1.选中C列的第一行的单元格&#xff0c;在函数区输入函数 如果我们以A为基准&#xff0c;找A中…

SQL语句优化技巧

目录 1、sql语句规范 2、sql语句优化 1、sql语句规范 MySQL在Linux系统下数据库名&#xff0c;表名&#xff0c;存储过程名&#xff0c;函数名称&#xff0c;触发器名称等区分大小写&#xff0c;列名不区分大小写&#xff0c;原因是这些操作系统下文件名称区分大小写。 MySQL…

19、案例实战:上亿请求轻松应对,老年代垃圾回收参数调整技巧大公开

19.1、前文回顾 在上一篇文章中,我们已经向大家介绍了一个日活跃用户百万级别,处理请求量上亿的电商系统案例。我们选择了这个中型电商系统在大促期间的瞬时高峰下单场景,作为我们的JVM优化分析的场景。通过预测,我们得出在大促高峰期,每台机器每秒需要处理300个订单请求…

蓝桥杯-线性动态规划问题背包问题进阶策略详解-

题目&#xff1a;蓝桥云课-青蛙吃虫 解题代码&#xff1a; #include <iostream> #include<cstring> #include<algorithm> using namespace std;const int N106;int f[N][N]; int a[N]; int t,l,r,k,n;int main() {cin>>t;while(t--){scanf("%d%…

云计算第十二课

安装虚拟机 第一步新建虚拟机 选择自定义安装 下一步 选择稍后安装操作系统 选择系统类型和版本 选择虚拟机文件路径&#xff08;建议每台虚拟机单独存放并且路径不要有中文&#xff09;点击下一步 选择bios下一步 选择虚拟机处理器内核数量 默认硬盘或者自行调大硬盘 选择虚…

win10建立共享文件夹和ipad共享文件

win10端设置 查看自己的局域网IP 在任意地方新建一个文件夹 打开文件夹的属性,点到共享的地方 点击高级共享 然后点击应用,确认 再回到之前哪个地方,点击共享 把Everyone的权限改为读取/写入 最后点击共享就欧克了 失败的可能原因 ipad端设置 然后回出现一个要输入用户名和…

MySQL查询篇-模式匹配

文章目录 多个字符模糊查询确定长度单个字符模糊查询转义下查询 like 和 %&#xff0c;_&#xff0c;# 多个字符模糊查询 select * from table where version like %abc% -- 查询version列里含abc的数据 select * from table where version like %abc -- 查询version列里abc结…

【2024新版】龙年新版ui周易测算网站H5源码/在线起名网站源码/运势测算网站系统源码

>>>功能说明&#xff1a; 1、系统配置&#xff1a;系统基本配置、测算价格配置、在线预约配置、系统信息配置、代理分成配置、推广积分配置、VIP价格配置、账号管理 2、推广管理&#xff1a;我的信息、推广链接、订单管理、体现管理 3、付费应用&#xff0c;订单管…

如何学好Django?

Django作为一个高级的Python Web框架&#xff0c;已经成为了许多Web开发者的首选工具。无论是初学者还是有一定经验的开发者&#xff0c;都可以通过Django快速构建功能强大的Web应用。那么&#xff0c;如何学好Django呢&#xff1f;本文将给出一些建议&#xff0c;帮助你更好地…

SQL语言:完整性约束

完整性约束 数据完整性是指存储在数据库中的数据要能正确反映实际情况&#xff0c;规定输入的数据不能是无效值、错误值 或者乱码等。 一、非空约束&#xff1a; 非空约束关键字&#xff1a; not null 1、非空约束的创建 create table teacher( t_id int not null, -- 为教…

[数据集][目标检测]电力场景安全帽检测数据集VOC+YOLO格式295张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;295 标注数量(xml文件个数)&#xff1a;295 标注数量(txt文件个数)&#xff1a;295 标注类别…

【OceanBase 系列】—— 什么是冻结和转储

文章出处&#xff1a;OceanBase分布式数据库-海量数据 笔笔算数 本文主要介绍什么是冻结和转储。 适用版本 OceanBase 数据库所有版本。 冻结 冻结是指将 Active MEMTable 转化为 Frozen MEMTable 的过程。 冻结的触发方式 手动触发&#xff1a;alter system minor freeze…

机器学习---朴素贝叶斯

朴素贝叶斯是一种用于分类和预测任务的算法&#xff0c;他的原理是基于贝叶斯定理。其中朴素的意思是假设各特征之间相互独立。这个实验我是用的老师课后作业的题目预测某天是否会打网球&#xff0c;假设每个特征独立。 目录 贝叶斯公式&#xff1a; 训练集&#xff1a; 处理…

最新版★重大升级★神点云连锁餐饮V2独立版点餐系统★公众号/h5/小程序前后端全套源码

提醒&#xff1a; 市场上流通很多老版本代码&#xff0c;一大堆问题且无法保证售后的源码&#xff0c;请各位买家一定要睁大眼睛&#xff0c;以防上当受骗&#xff01;&#xff01;&#xff01;本系统源码全是经本人亲自测试与修复的完好版本&#xff0c;且本人用此版本源码已…

Java | Leetcode Java题解之第86题分隔链表

题目&#xff1a; 题解&#xff1a; class Solution {public ListNode partition(ListNode head, int x) {ListNode small new ListNode(0);ListNode smallHead small;ListNode large new ListNode(0);ListNode largeHead large;while (head ! null) {if (head.val < x…

第 1 天_二分查找【算法基础】

第 1 天_二分查找 前言34. 在排序数组中查找元素的第一个和最后一个位置题解官方33. 搜索旋转排序数组题解官方74. 搜索二维矩阵 前言 这是陈旧已久的草稿2021-11-09 19:33:44 当时在学习数据结构&#xff0c;然后再LeetCode上找了一个算法基础。 但是后来又没做了。 现在20…

使用LLaMA Factory来训练智谱ChatGLM3-6B模型

使用LLaMA Factory来训练智谱ChatGLM3-6B模型时&#xff0c;以下是一个训练过程&#xff1a; 1. 环境搭建 a. 安装Python和Anaconda 下载并安装适合你操作系统的Python版本&#xff08;推荐Python 3.10或更高版本&#xff09;。安装Anaconda&#xff0c;以管理Python环境和依…

【STM32+k210项目】基于AI技术智能语音台灯的设计(完整工程资料源码)

视频演示 基于AI技术智能语音台灯的设计 前言&#xff1a; 随着社会的快速发展&#xff0c;人们对家用电器智能化程度的要求越来越高。不管是对于学生人群还是对于工作加班者&#xff0c;台灯是每家每户必不可少的工具&#xff0c;长期处于光线太强或者过弱的环境中学习和一系列…