| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 大数据 -> 我的sql没问题为什么还是这么慢|MySQL加锁规则 -> 正文阅读 |
|
[大数据]我的sql没问题为什么还是这么慢|MySQL加锁规则 |
前言前阵子参与了字节跳动后端青训营,其中大项目编写涉及到数据持久化一般选择使用? 这么一看,仿佛即使是实际开发也与你此前听闻的一些? 在?某次不够规范的?小组开发过程中,开发成员选择测试程序的方式比较原始,大家共享一个测试数据库,各自使用测试账号进行接口的测试,这就意味着数据库中的记录在某一时刻有可能被多个事务访问,甚至在其他人测试的同时,某张数据表的结构被另一位同学修改。 多事务并发访问,反映到开发者这边,就是查询接口?有时?速度很慢。如果你是直接使用数据库管理工具操作数据库表数据/结构,对应的就是? 当然导致数据库访问速度变慢的原因有很多:?sql语句编写不规范、数据库服务器的性能差、网络状况不佳等?,但是本文所侧重的点在于探究? 相信在完成本文的阅读之后,你会明白上面的场景的发生,可能是? MySQL的锁有哪几种全局锁
此时整个数据库处于只读状态,所有数据记录的更新、数据库/表结构的改动提交都会被阻塞,这可以用于全库的数据备份。 表级锁表锁 表锁可以通过以下显式命令实现对一个表添加读/写锁: 如果A线程为t1表添加了读锁,为t2表添加了写锁。则其他线程将只能读t1,写t1被阻塞;读/写t2都会被阻塞。而A线程在执行? 元数据锁(metadata lock) MDL锁不需要显式使用,在访问一个表的时候会被自动加上,并且当事务完成提交时释放。当对一个表数据做CRUD操作的时候,自动加MDL读锁;当对该表结构作出改动的时候,自动加MDL写锁。
这里展示一个多线程并发操作同一个数据表的案例: 这里? 或许此时你已经对于为什么多人调试程序时数据库访问不时出现卡顿有了一些自己的想法,当然这只是锁机制的冰山一角。 行级锁通过上面的讲解,我们明白了,所谓的读写锁并不是单指一个锁叫读锁/写锁,而是指?不同粒度的锁有读锁和写锁两种状态?,允许的并发程度也有所不同。行级锁也是如此(针对记录行的锁,锁粒度进一步缩小),行锁的存在也使得事务并发访问数据库的性能进一步的提高,并且依旧有读写锁之分,下面介绍。 但区别于全局锁和表级锁,?MySQL行锁?是由各个存储引擎自己实现的,并不是所有的存储引擎都支持行锁(MyISAM不支持),?由于现在MySQL用户大多选择使用InnoDB存储引擎,所以本文将以InnoDB引擎为默认选择?。 两阶段锁协议在InnoDB事务当中,行锁在需要的时候添加,并且直到事务提交才释放(锁的添加和释放分两个阶段进行),举个例子: 事务A(线程A)在提交之前,占有id=1这条行记录的写锁,事务B(线程B)修改同一行的操作将被阻塞。 死锁与检测死锁原本是操作系统当中的概念,意思是多个线程都在等待其他线程释放自己需要的资源,使得这些线程陷入无限制的等待。 在这个例子当中,线程A的事务和线程B的事物分别占有? InnoDB存储引擎默认?开启了死锁检测?,每个新来的被阻塞的线程,都会主动判断是否是自己的加入导致死锁(检测逻辑就是判断自己需要的行资源是否被别的线程的事务占有),时间复杂度O(n),一旦检测到,则回滚当前线程的事务,确保其他线程可以得到执行。 这里你会发现,如果同时有多个线程修改同一条记录,一旦并发度很高,则需要消耗O(n^2)时间去完成死锁检测,就会消耗大量CPU资源在死锁检测上,而使得数据库IO的性能下降。 此时你是否又对我最初给出的小组开发时访问数据库慢的场景有了自己的思考,其实在高QPS情况下,发生死锁检测的概率是大大高于小组开发场景的 因此控制热点记录的并发访问数量,是提升数据库IO性能的重要前提。 多版本并发控制(MVCC)上面讲述了?InnoDB?的update操作会占用行记录的写锁,那么你自然而然就想到,?select查询操作是否就占用了行记录的读锁呢?不完全正确?,这就不得不提及?MySQL的InnoDB引擎的用于控制事务隔离级别的多版本并发控制机制?。 简言之就是每条行记录值的变化是由一个链式的结构组织的,存放在? 而InnoDB存储引擎默认的事务隔离级别是可重复读(Read Repeatable),简单来说:就是当事务A启动期间,普通的select查询将无法访问到其他事务在此期间对表记录的改动。 关于多版本并发控制(MVCC)这里我没有过多深入讲解,详情给出我的另一篇文章:?一文搞懂MySQL事务的隔离性如何实现|MVCC - 掘金 快照读 对于普通的查询操作,你大致了解InnoDB引擎管理的表的行记录变更是链式组织的,那么每一条记录就相当于一个个的快照,因此普通的select查询操作被称为快照读,会读取到自己可见的最近一个版本(但不一定是最新版本),快照读并不加锁(?也就是没有获取读锁?)。 至于具体读到哪个版本的快照,在上面链接给出的文章中有详细讲解。 当前读 这里给出了两种不同的当前读方式,当前读可以读取到? 上面讲解死锁检测的时候我用?更新语句?获得了行记录的?写锁?,而这里,通过增加? 间隙锁间隙锁的出现解决了幻读问题,那么先简述一下幻读的概念,以及幻读有什么问题。 幻读概述
幻读的问题这里用一张表t的操作来描述幻读带来的问题。 以下的分析建立在?没有间隙锁?的情况下(只是为了分析所作的假设):
这里的核心问题就在于:即使所有扫描到的行记录都加上了锁,依旧无法阻止新记录的插入(因为要插入的记录不可能提前锁定),要避免幻读,就需要将记录之间的间隙锁定——间隙锁。 Gap Lock间隙锁在可重复读隔离级别下才有效,所以本文的描述都是基于RR级别(InnoDB存储引擎事务默认隔离级别),这里给出?间隙锁?配合?行锁?工作的一些规则:
小结本文概述了MySQL锁机制的工作情况,明确了锁有读/写之分。以及给出了一些会触发表锁、行锁的案例,同时,InnoDB存储引擎为了解决幻读问题,引入了间隙锁,用于锁定索引之间的间隙,防止当前读的出错。 还记得文章开头我抛出的实际开发案例吗,相信通过这篇文章的讲解,你对于多事务并发操作数据库时数据库访问性能下降的原因,已经有了不少自己的思考。 ? ? |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 13:36:49- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |