Mysql进阶
Mysql体系结构
大体来说,MySQL 可以分为 Server 层和存储引擎层两部分。
Server层
主要包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能,以及所有的内
置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。
其中Server层又可以划分以下:
|-------------------------------------------------------------------------------------------------------------------------------------------|
| 连接层 :主要负责连接处理、身份验证、安全性等,一般 C/S 架构都会有这一层。 核心服务层 :主要有查询缓存、分析器、优化器、执行器等,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。 |
存储引擎层
存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引
擎。现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始成为了默认存储引擎。也就是说如果我们在
create table时不指定表的存储引擎类型,默认会给你设置存储引擎为InnoDB。
mysql系统服务之外又包含数据存储层
数据存储层:文件系统存储数据,存放数据文件、日志文件等,完成与存储引擎的交互。
Server层的不同部件作用
解析器
|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| SQL 词法分析: 词法分析是编译过程的第一步,其主要任务是接收输入的字符序列,然后将它们分解成一系列具有独立含义的单元, 这些单元被称为Token(标记或者词法单元)。 在MySQL(或者任何其他数据库语言)中,当你提交一个SQL语句时,词法分析器会被用来进行初步的解析工作。 例如,一个SQL查询SELECT * FROM users被词法分析器拆分为了以下四个Token:SELECT, *, FROM, users。 不同的Token对应不同的元素。比如可以代表关键字(如SELECT, FROM),标识符(如表名或列名), 运算符(如* 或者 =),或者常量(比如数字或字符串)等。 词法分析器通常会利用有限状态自动机或者正则表达式来完成这个任务。词法分析的结果会被传递到下一步, 即语法分析阶段,用于进一步检查SQL语句的合法性,构建所称的解析树(parse tree)或者语法树(syntax tree)。 语法分析: 语法分析,也称为解析,是编译过程中紧接着词法分析之后的阶段。在此阶段中,根据语言的语法规则, 解析器将从词法分析阶段获取的 Tokens 组合成语句和表达式。这个过程通常会创建出一种称为抽象语法树(Abstract Syntax Tree, AST)的数据结构。 例如,对于一个简单的SQL查询,譬如"SELECT * FROM users",语法分析的结果可能是一个表达式, 包含一个"SELECT"操作,一个"FROM"操作以及目标表名"users"。 如果输入的Tokens序列不能被解析成一个符合语法规则的AST,那么就会抛出一个语法错误,告诉用户输入的语句在语法上有错误。 例如,如果你提交了一个查询 "SELECT FROM WHERE",这样的语句就会因为在语法上错误而被解析失败。 在完成语法分析后,AST将被传递给下一阶段,可能是语义分析阶段,或者直接被传到优化器进行进一步处理。 语义分析: 语义分析是编译过程中的一个重要阶段,它主要检查语法树是否具有意义,即符合语言特定规则的逻辑。 这涵盖了一系列工作,包括类型检查、上下文关联检查等。 在MySQL中,语义分析的一部分工作可能会包括: 检查表和列的存在性 :解析器要确认查询引用的所有表和列是否都在数据库中存在。 名称解析 :解析可能出现的名称冲突,比如,当一个表的名称和库中的另一个表名称重复时。 类型检查 :对查询所涉及的操作进行类型检查,如,确保放在等号两边的数据类型一致,不能将一个字符串和一个数值进行比较等。 函数和过程的存在及其参数校验 :如果查询中使用了某个函数或者存储过程,解析器要确认函数/过程存在,且传入的参数数量要正确,类型要与函数/过程的定义相符。 语义分析完成后,解析器就会产生一棵注释过的语法树,接下来还会根据需要进行诸如查询优化等更多工作。如果在语义分析阶段遇到错误,将返回错误信息,比如"未知的表或字段"或"类型不匹配"等。 |
优化器
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| SQL 在MySQL中,优化器(Optimizer)的主要任务是决定如何执行SQL查询以达到最快的速度以及使用最少的资源。 优化器需要选择数据存取策略(如何访问数据和以何种顺序访问数据)和连接策略(对多个表或查询进行优化的连接方式)。 以下是优化器主要做的一些工作: 1. 索引选择:优化器评估是否使用索引以及选择何种索引以获取数据。它会基于索引的统计信息以及查询的具体情况进行决策。 2. 连接类型和连接顺序:对于处理涉及多个表的查询,优化器需要确定表的连接顺序以及如何连接这些表。例如,它可能选择嵌套循环连接,hash连接,或者排序合并连接等。 3. 查询重写:有些情况下,优化器可能会重写查询。例如,它可以简化一些没有必要的操作,或者将查询分解为一组较小的查询。 4. 生成执行计划:优化器最终的产出是一个执行计划,这个执行计划包含了如何取数据,连接方式等信息,以便查询执行引擎根据这个计划来实际地执行查询。 整个这个过程基于的是数据的统计信息,包括数据的分布、表的大小、索引的选择等。优化器的目标是尽可能保证在处理大型数据库时,查询仍然能够高效运行。 |
执行器
|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| SQL 在MySQL中,执行器,也称执行引擎,是用来按优化器提供的执行计划实际执行SQL查询的。 优化器的输出为一个执行计划,这个计划包含了用于执行查询的详细步骤。执行器按照这些步骤来读取或写入数据。 以下是执行器通常要执行的一些操作: 1. 打开表:对需要查询的表进行打开操作。 2. 检查权限:在执行查询前检查是否有对应的权限进行此项操作。 3. 获取并返回数据:按照执行计划从表中查询数据,然后将数据返回给客户端。对于像`SELECT`这样的查询,数据会被返回给用户;对于像`UPDATE`或者`INSERT`这样的查询,数据会被修改或新增。 4. 处理错误:如果在执行过程中遇到错误,如数据冲突或权限不足等问题,执行器会停止并返回一个错误信息。 执行器的工作并不包括复杂的决策过程,因为所有需要决策的步骤已经由优化器完成。 执行器只负责按照拿到的执行计划简单、直接的执行即可。 分工明确的执行器和优化器,使得MySQL能更好的灵活性和效能,以处理各种查询,并满足不同的性能需求。 |
一条语句在Mysql底层执行过程
Buffer pool
在MySQL中,Buffer Pool是InnoDB存储引擎用于缓存数据和索引的区域。对于MySQL的InnoDB存储引擎来说,所有的读操作(查询)和写操作(增删改)都会首先被应用在Buffer Pool中,这是一个在内存里的缓存区域。其主要目标是减少对硬盘的读写操作,因为硬盘I/O是数据库操作的瓶颈之一。
当进行数据查询时,InnoDB存储引擎会首先在Buffer Pool中查找数据。如果在Buffer Pool中找到了数据(被称为缓存命中),那么查询结果就会直接被返回;如果没有找到(被称为缓存未命中),那么数据会从磁盘被加载到Buffer Pool中,然后再返回结果。
对于数据的修改操作,InnoDB先在Buffer Pool中修改数据,然后在修改数据的同时,将修改操作写入到redo log(重做日志)里。这时候该操作就算完成了,然后在适当的时机(比如Buffer Pool不够用,或者系统空闲的时候),再将Buffer Pool中的数据刷回到磁盘。
Buffer Pool的大小是可以配置的,通常我们希望将其设定为能够在物理内存中持有尽可能多的常用数据的大小。在64位的系统中,Buffer Pool的大小可以配置得非常大,只受限于服务器的物理内存大小。
另外,MySQL通过使用一种称为LRU(Least Recently Used)的算法来管理Buffer Pool中的数据。当Buffer Pool满了,需要为新的数据提供空间时,最近最少使用的页(也就是在最久的一段时间内都未被访问的页)会被清理出Pool。
总的来说,Buffer Pool是InnoDB提高读操作性能的重要机制之一。通过提供内存级别的数据访问,可以显著减少更慢的硬盘读写操作。
|------------------------------------------------------------------------------------------------------------------------|
| MySQL数据库的增删改查操作是直接作用在Buffer Pool上的。但需要注意的是,当Buffer Pool中的空间不足,或MySQL实例关闭,或操作系统重启等情况时,Buffer Pool中的数据会被刷写回磁盘,以确保数据的持久性。 |
查询语句
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| SQL 在MySQL中,查询缓存是一种对之前运行的SELECT查询语句及其结果进行缓存的功能。当有一个新的查询请求到来, 且该查询完全匹配查询缓存中的某个查询,MySQL会直接返回该缓存的查询结果,而跳过后面的词法分析、语法分析、优化器和执行器等步骤。 查询缓存属于MySQL的连接器部分。当客户端连接到MySQL服务器并发送请求时,连接器首先检查这个请求是否完全匹配查询缓存中的某个查询。如果匹配,连接器就直接返回缓存的结果。 然而,需要注意的是,在部分高并发场景和大规模的读写混合负载中,查询缓存可能带来性能下降,因为每次数据修改,相关的查询缓存都需要被清理。 并且,在MySQL 8.0及更高版本中,查询缓存已被彻底移除。这些情况下,查询性能的优化更依赖于其他技巧和机制,例如索引优化,使用Buffer Pool等。 |
更新操作
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| update更新操作,具体步骤如下: 1:加载数据库存储在磁盘的数据记录到缓存池 2:将旧数据记录到undo日志 3:更新的数据记录刷新缓存池数据 4:将新数据记录写到redo 日志 5:redo日志顺序写磁盘保存 开始准备提交事务 6:bin log日志记录操作日志 7: 写入commit标记到redo log 提交事务 8: 异步刷新缓存池更新记录到磁盘 |
Mysql事务
事务主要用于处理操作量大、复杂度高的数据。事务是保证数据一致性的重要机制,并且能在数据库错误甚至系统崩溃时保护数据不受损害。
ACID特性
1.原子性(Atomicity)
事务被视为一个不可分割的最小工作单元,整个事务中的所有操作,要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作。
2. 一致性(Consistency
事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致性与业务规则有关,比如一个人转账给另一个人,那么这个过程中两个人的总金额是不变的。
3. 隔离性(Isolation)
多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
4. 持久性(Durability)
已被提交的事务对数据库的修改是永久的,该修改即使在数据库系统崩溃后也将保持。
不同地方的使用事务操作的语法可能会有些许不同,但基本的SQL指令包括START TRANSACTION(或BEGIN)、COMMIT和ROLLBACK。
如何保证ACID
保证原子性:
|----------------------------------------------------------------------------------------------------------------------------------------------------------|
| InnoDB存储引擎通过"Undo日志"来保证事务的原子性。当一个事务开始时,InnoDB会引入一个新的Undo日志记录,其中记录了该事务涉及的数据的原始状态。如果事务在执行过程中出现错误,或者用户手动回滚该事务,InnoDB就会使用Undo日志,将发生变动的数据恢复到原始状态,从而达到回滚事务的目的。 |
如:
delete一条数据的时候,就会记录这条数据的曾经的信息,回滚的时候,insert这条旧数据
update一条数据的时候,就会记录之前的旧值,回滚的时候,根据旧值执行update操作
insert一条数据的时候,就会这条记录的主键,回滚的时候,根据主键执行delete操作
保证一致性:
|-----------------------------------------------------------------|
| InnoDB存储引擎通过redo log保证一致性,它用于记录对数据库执行的修改操作,如果系统出现崩溃,重做日志将用于恢复系统 |
保证隔离性:
在MySQL中,InnoDB存储引擎利用多版本并发控制(MVCC,Multiversion Concurrency Control)和锁定系统来实现事务的隔离性。
-
多版本并发控制(MVCC):这是一种策略,使得每个用户可看到一个数据库的快照,而不是当前正在被其他用户更改的数据内容。这个快照是由执行读操作的事务的开始时间来定义的,因此该读取操作(或者整个事务)会看到在开始执行记录读取操作时存在的数据的布局。
-
锁定机制:InnoDB还实现了一个强大的锁定机制以确保数据在并发情况下的一致性。 锁定机制包括共享锁(Shared locks)、排他锁(Exclusive locks)和意向锁(Intention locks)。锁定级别可以是行级和表级。行级锁为最小的锁定级别,它使得多个事务能够同时处理同一表中的不同行,从而达到很高的并发性。表级锁在整个表上锁定,适合全表操作(如删除表的所有行)。
-
四大隔离级别:InnoDB根据SQL标准定义了四种不同的隔离级别:未提交读(READ UNCOMMITTED)、已提交读(READ COMMITTED)、可重复读(REPEATABLE READ)和串行化(SERIALIZABLE)。这四个级别通过控制在同一时间是否可以访问某一资源、是否阻塞以及何时解锁,实现了不同程度的隔离。
因此,所谓的隔离性,就是使并发的事务互不干扰,每个事务都感觉不到系统中其他事务的存在,尽管它们可能正在对相同的数据进行操作。
|------------------------------------------------------------------------------------------------------|
| 在事务隔离级别为读已提交(Read Commited)时,一个事务能够读到另一个事务已经提交的数据,是不满足隔离性的。但是当事务隔离级别为可重复读(Repeateable Read)中,是满足隔离性的 |
保证持久性:
在MySQL的InnoDB存储引擎中,事务的持久性(Durability)是通过其日志系统来实现的。该系统包含两种日志类型:重做日志(Redo Logs)和Undo日志。
-
重做日志(Redo Logs):重做日志是InnoDB在提交事务前用来确保持久性的一种手段。重做日志是一种物理日志,记录的是对于页的物理修改。当一个事务开始时,InnoDB会首先在重做日志中记录下该事务要执行的所有修改操作(这个动作称为日志预写,write-ahead logging, WAL)。只有当所有的重做日志都已经被成功写入磁盘,事务才算提交成功。因此,即使在事务提交后立即发生系统崩溜,数据库系统也能通过重做日志恢复到最后的一致性状态。
-
Undo日志:Undo日志主要用于在事务失败或者被中断时回滚(撤销)已经进行的修改,确保数据库的一致性。由于Undo日志能够记录数据的原始状态,因此在事务提交前如果发生错误,MySQL也能够通过Undo日志将数据恢复到事务开始前的状态。
此外,为了确保数据的持久性,在日志写入磁盘时,InnoDB会使用一种称为"刷盘"(flush)的操作,确保日志真实地写入磁盘而非留在缓存中。如果数据仅存储在缓存中,系统崩溃可能会导致数据丢失。
因此,通过InnoDB存储引擎的日志系统以及刷盘操作, MySQL能够有效地保证事务持久性,确保一旦事务提交,其数据的改变就是永久的,即使在系统崩溃的情况下也能恢复到最后的一致性状态。
隔离机制/不同隔离级别带来问题如何解决
"脏读"、"不可重复读"和"幻读",其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决。
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 更新丢失(Lost Update)或脏写 "脏写"指的是一个事务修改了数据,但还未提交,另一个事务又对这些未提交的修改进行了覆盖。这就是脏写(Dirty Write) 脏写的主要问题是,它可能会破坏数据的完整性,给恢复和并发控制带来困难。为了避免脏写,关系型数据库管理系统(RDBMS)通常会使用写锁(Write Lock):在一个事务对数据进行修改(写操作)时,其他的任何事务都不能修改这个数据,必须等待直到第一个事务提交,这样就避免了脏写。 脏读(Dirty Reads) 所谓的"脏读"(Dirty Read),是指在数据库进行并发访问时,一个事务(事务A)读取了另一个正在执行且尚未提交的事务(事务B)中的数据。如果事务B由于某些原因回滚了,那么事务A读取到的数据将是无效的。 一句话:事务A读取到了事务B已经修改但尚未提交的数据,还在这个数据基础上做了操作。此时,如果B事务回滚,A读取的数据无效,不符合一致性要求。 举个例子,假设一个银行转账操作,事务A是你向朋友转账100元,转账操作是一个事务,包含两个步骤,先从你的账户扣款,后给朋友加款。假设在这两个步骤之间,你的账户已经被扣款100元,但是朋友的账户还未加款,这时候事务B读取了你朋友的账号数据,这就是脏读,因为事务B读取了尚未提交的事务数据。 为了避免脏读,数据库会提供多级别的隔离机制,如"可重复读"或"串行化"等级别,在这些隔离级别下,数据库会锁定正在使用的数据,防止其他事务在数据提交前进行访问。 不可重复读(Non-Repeatable Read)是指在一个事务内,多次读取同一数据,在这个事务还未结束时,其它事务也在访问该同一数据,那么,在第一个事务中,多次读取同一数据时,可能会得到不一样的结果。这种在一个事务内数据的非重复现象,就被称为"不可重复读"。 举一个简单例子,比如事务A第一次读取了一条记录,得到的结果是100,而在A事务再次读取这条记录之前,B事务对这条记录进行了修改,将值改为了200,那么当事务A再次读取这条记录的时候,就会发现值已经从100变为200,这种现象就被称为不可重复读。 不可重复读是由于多事务竞争同一资源时,因为读写操作的先后次序不同,读取的数据结果就有可能不同。避免"不可重复读"的一种常用方式是通过行级锁实现读写互斥,即一个事务在修改数据前先锁定,其他事务不能对这个数据进行修改操作,也不能读取,直至该事务完成,这种级别的隔离级别在MySQL中被称为可重复读。 幻读(Phantom Read)是指在一个事务内部,执行了两次相同的查询,但由于另一个并发事务的插入或删除操作,第二次查询结果多了或少了记录,这就是所谓的"幻读"。 假设我们有一个操作,需要从一个包含100条记录的表中选出字段A为3的记录,此时另外一个事务插入了一条新记录,其中字段A也为3,那么,原本的事务中再执行相同的查询操作,就会发现有多了一条原本不存在的记录,这就是一次典型的"幻读"。 幻读和不可重复读的区别在于,不可重复读关注的是已有数据的修改,而幻读则是关注新的记录的插入或已有记录的删除。解决幻读的办法通常是使用后台机制,比如MySQL的InnoDB存储引擎会在可重复读隔离级别下使用间隙锁或临键锁阻止其它事务插入"幻影"行。 |
MySQL 锁
排它锁
排它锁(Exclusive Locks,也被称为写锁):当一个事务对数据进行修改(如 UPDATE,DELETE)操作时,会对数据加上排它锁。其他事务不能对此行数据进行读取或者修改操作。也就是说,如果一个事务持有了排它锁,在此锁释放前,其他任何事务都无法获取这个数据行的任何锁。
|-------------------------------------------------------------------------------------------------------------------------------------------------|
| SELECT ... FOR UPDATE:这条语句对查询返回的所有行添加排他锁(写锁)。当你希望对查询的数据进行更新操作,并且希望确保这些数据在读取和修改之间不会被其他事务更改时,可以使用这个语句。在加了排他锁的行上,其他的事务不能加共享读锁或者排他写锁,即不能读取也不能修改这些行。 |
共享锁
共享锁(Shared Locks,也被称为读锁):当一个事务需要读取一条数据,并且不希望这条数据在读操作过程中被修改,那么可以对这条数据加上共享锁。一条数据上如果有共享锁,其他事务可以继续对这条数据加共享锁,进行读取操作,但是不能加排它锁,进行数据修改
|-----------------------------------------------------------------------------------------------------------------------------------------------------|
| SELECT ... LOCK IN SHARE MODE:这条语句在查询的同时给符合条件的行添加共享锁(读锁)。对选定的行进行了加锁,其他会话可以读取这些行,但是除非持有共享模式锁,否则不能对它们进行写操作(更新、删除、修改)。这个锁模式的目的主要是为了保护某个查询下的数据不被其他事务更改。 |
意向锁
在MySQL的InnoDB存储引擎中,意向锁(Intention Locks)是为了在表级上解决多粒度锁定问题(也就是同一时间在已经存在行锁或表锁的情况下允许其他事务在表级别上添加锁)而引入的一种锁。
意向锁是InnoDB自动加的,用户不需要直接操作。它们见于InnoDB存储引擎的内部,并且是在行锁之前申请的。它们有两种类型:意向共享锁(Intention Shared Lock)和意向排他锁(Intention Exclusive Lock)。这两种意向锁都是表级别的锁。
-
意向共享锁(IS):表明事务接下来将在该表的行上请求共享锁(读锁)。存有意向共享锁的表上可以执行查询操作。
-
意向排他锁(IX):表明事务接下来将在该表的行上请求排他锁(写锁)。存有意向排他锁的表上可以进行插入、更新、删除操作。
意向锁的设计可以让数据库在请求行级锁之前通过检查意向锁来了解是否有冲突,这样就可以大大降低锁冲突的可能性,提高多事务处理时的效率。再者,意向锁的存在也可以避免出现死锁的情况。
乐观锁
乐观锁(Optimistic Locking)并不是通过真正的锁来实现,而是在数据读取的时候,并不加任何锁;而在更新数据的时候,才会判断这段时间内别的事务是否也对这个数据进行了更新。如果别的事务有更新,那么就回滚当前事务,否则就提交事务。所以,乐观锁适用于读多写少的应用场景,能够最大程度地提高并发性能。
使用乐观锁的方式,可以避免读写、写读互斥的情况,具体如下:
-
乐观锁实现读写不互斥:当一个事务在读取数据的时候,由于不加锁,所以并不会阻止其他事务进行写操作。此时尽管写操作可能会引起数据的变化,但是由于一致性读的保障,当前事务还是能读取到开始读取操作时的数据快照。读操作结束后,如果进行写操作,则需要判断在此期间是否有其他写操作影响了这个数据。如果有,则回滚事务,否则提交事务。
-
乐观锁实现写读不互斥:当一个事务在写数据的时候,由于不会锁定数据,所以并不会阻止其他事务进行读操作。其他事务可以继续读取数据的旧版本。然后当前事务需要在写入时判断在此期间是否有其他写操作影响了这个数据。如果有,则回滚事务;否则,提交事务。
所以,通过乐观锁,我们可以在一定程度上解决读写和写读的阻塞问题。但是这也意味着在高并发的情况下,乐观锁就可能会导致大量的事务回滚,从而影响性能。
互斥原理
- 读读不互斥:这个是因为读操作如果使用的是共享锁(Shared Lock),在持有共享锁的情况下,是允许其他事务继续对这条数据加共享锁,进行读取操作的。也就是说,同时对同一条数据进行读操作并不会互相影响。
- 写写互斥:如果一个事务在进行写操作(如UPDATE,DELETE)时,会对这条数据加上排它锁(Exclusive Lock)。在持有排它锁的情况下,其他任何事务都无法获取这个数据行的任何锁,也就实现了写写互斥。
- 读写互斥:如果一个事务对某条数据加上了共享锁,进行读取操作,那么其他事务不能对此数据进行写操作(即不能加排它锁),实现了读写互斥。
- 写读互斥:同理,如果一个事务正在对某条数据进行写操作(也即持有排它锁),那么其他事务在此锁释放前,无法对该数据进行读操作(无法加共享锁),实现了写读互斥。
|-----------------------------------------------------------------------------------------------------------------------------------------|
| 1、对MyISAM表的读操作(加读锁) ,不会阻塞其他进程对同一表的读请求,但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其它进程的写操作。 2、对MylSAM表的写操作(加写锁) ,会阻塞其他进程对同一表的读和写操作,只有当写锁释放后,才会执行其它进程的读写操作 |
表锁/行锁
-
表锁(Table Locks):表级锁是MySQL中最基本的锁类型,也是开销最小的锁类型。表级锁有两种:读锁(共享锁)和写锁(排他锁)。读锁在同一时间允许多个事务读取同一资源,不对数据进行修改,而写锁在任一时刻只允许一个事务进行写入或修改操作,并在此期间,不允许其他事务对数据进行操作,确保数据的正确性。
-
行锁(Row Locks):行级锁是MySQL中最细粒度的锁,它针对数据表中的行进行加锁。行级锁可以最大程度地支持并发处理(同时也带来了最高的开销)。行级锁只在存储引擎层实现,MyISAM和MEMORY存储引擎的行锁只是一个功能声明而未实现,但InnoDB存储引擎实现了行级锁。行级锁有两种模式:共享(S)锁和排他(X)锁。
这两种锁类型各有其使用场景,表锁适合查询密集型的场景,而行锁则更适合大量更新、插入操作的应用场景。
间隙锁和临键锁
在MySQL中,间隙锁(Gap Locks)和临键锁(Next-Key Locks)都是InnoDB存储引擎提供的行级锁的一种扩展,主要用于解决隔离级别为可重复读和串行化时的幻读问题。
间隙锁(Gap Locks):顾名思义,间隙锁是锁定一个范围,但并不包含这个范围内的任何具体的行,而只是防止新行插入到这个范围中。实现方式是锁定索引的记录之间的间隙。由于间隙锁和行级锁可以共存,因此InnoDB可以有效地避免幻读(Phantom Read)现象。
临键锁(Next-Key Locks):临键锁是InnoDB在行级锁和间隙锁基础上的一种组合,是InnoDB默认的行级锁。一个临键锁实际上锁定了索引记录及其之间的间隙。因此,临键锁可以同时防止同一资源的并发修改(即解决脏写问题)和并发插入的记录导致的幻读(即解决幻读问题)。
这两种锁主要是在InnoDB存储引擎的可重复读(REPEATABLE READ)和串行化(SERIALIZABLE)两个事务隔离级别中使用。这两种锁的存在,使得InnoDB可以实现精确度高和效率高的锁定策略。
死锁
大多数情况mysql可以自动检测死锁并回滚产生死锁的那个事务,但是有些情况mysql没法自动检测死锁
锁优化建议
● 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁
● 合理设计索引,尽量缩小锁的范围
● 尽可能减少检索条件范围,避免间隙锁
● 尽量控制事务大小,减少锁定资源量和时间长度,涉及事务加锁的sql尽量放在事务最后执行
● 尽可能低级别事务隔离
个人公众号
|-------------------------------|---------------------------------------------------------------------------------------------------|
| 对于刚毕业的应届生或者忙于找工作的求职者, 可以一起沟通哦 | |