Mysql学习手册(第一部分)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 【5月更文挑战第17天】Mysql学习手册(第一部分)

(一)Mysql查询语法和执行顺序

1.1 查询语法顺序

如下的这些是完整的查询语句的语法编写顺序,这其中除了LIMIT之外,都是很多关系型数据库通用的标准SQL语法:
1.SELECT

  1. FROM
    3.LEFT JOIN
  2. ON
  3. WHERE
  4. GROUP BY
  5. HAVING
  6. ORDER BY
  7. LIMIT --Mysql专有的分页语法

    1.2 查询执行顺序

SELECT
        USER_TABLE.user_name
FROM
        USER_TABLE
LEFT JOIN
        USER_TABLE_ROLE
ON    
        USER_TABLE.user_name=USER_TABLE_ROLE.user_name
WHERE
        bizline_id = 1
GROUP BY
        USER_TABLE.user_name
HAVING
        COUNT(1) > 2
ORDER BY
        USER_TABLE.user_name
LIMIT 0,10

假设将VT表示为虚拟表,MySQL在执行如上的每一步操作时,其实都会产生一个结果集,每个结果集就表示为一个虚拟表VT,下面我们来梳理一下整个执行流程:

(1)FROM:将最近的两张表,进行笛卡尔积 --VT1
(2) ON:将VT1按照他的条件进行筛选--VT2
(3) LEFT JOIN:保留左表的记录(这里不同类型的join处理方式不同,左连接,右连接,全连接,内连接区别,自行查找)--VT3
(4) WHERE:过滤VT3中的记录--VT4
(5) GROUP BY:对VT4中的记录进行分组--VT5
(6) HAVING:对VT5中的数据进行过滤--VT6
(7) SELECT:对VT6中的记录选取指定列--VT7
(8) ORDER BY:对VT7中的记录进行排序--VT8
(9) LIMIT:对排序之后的值进行分页显示--VT9

1.3 WHERE条件顺序

在没有查询优化器介入的情况下,Mysql默认是从左往右执行where子句的;Oracle中则是相反,默认从右向左执行where中的条件的。这种情况下where条件优先级高的部分应该编写尽可能过滤更多数据的条件。
在有索引的条件下,查询优化器会按照索引进行执行计划的优化,具体执行顺序是查询优化器决定的。

(二)Mysql架构介绍

2.1 逻辑架构

MySQL的逻辑架构如下图:
image.png

2.1.1 Connectors

这个部分指的是访问数据库的客户端,不同语言都有自己的实现,比如Java下一般就指JDBC

2.1.2 Management Services & Utilities

系统管理和控制工具

2.1.3 Connection Pool:连接池

连接池是MySQL的通讯层,数据库服务端一般都是使用连接池来管理客户端的连接。该模块负责监听对MySQL Server的各种请求,接受连接请求,转发所有连接请求到线程管理模块。每一个连接上MySQL Server的客户端请求都会被分配一个连接线程为其单独服务。
连接线程的主要工作就是负责MySQL Server与客户端的通信,接收客户端的命令请求,传递server端的结果信息等。
线程管理模块,主要负责管理维护这些连接线程,包括线程的创建,线程的缓存等。

2.1.4 SQL Interface:SQL接口

接收用户的SQL命令,并返回处理结果,如select语句等。

2.1.5 Parser:解析器

验证和解析SQL命令,将SQL语句进行语法和语义的分析,解析成结构化的数据,然后按照不同的操作类型(增、删、改、查)进行分类,然后做出针对性的转法到后续的步骤中,以后SQL语句的传递和处理就是基于这个结构的。SQL语句的语法错误就是在这个模块检查的。

2.1.6 Optimizer:查询优化器

SQL语句在查询之间会使用查询优化器对查询进行优化,产生执行计划。

2.1.7 Cache & BUffer:查询缓存

将客户端提交给MySQL的select请求的返回结果集缓存到内存中,与该query的一个hash值做一个对应。该query所取得数据基表发生任何数据变化,会自动使该query的cache失效。在读多写少的应用系统中,query cache对性能的提高非常的显著的,但是对内存的消耗也是非常大的。
如果查询缓存有命中的查询结果,查询语句就可以直接去查询缓存中取数据,这个缓存机制是由一系列的小缓存组成的。比如表缓存,记录缓存,key缓存,权限缓存等。

2.1.8 Pluggable Storage Engines

存储引擎接口:MySQL在底层的数据存储访问和上层的数据处理逻辑之间定义了一个抽象层,这个模块实际上就是一个抽象类,它将各个数据存储操作抽象化,实现其底层数据存储引擎的插件式管理。这是MySQL与其他数据库相比的一个非常有特色的地方。

2.2 存储引擎的介绍

MySQL是一张表对应一个存储引擎的实现,不同的表可以用不同的存储引擎,自从MySQL5.5版本之后,默认的存储引擎从MyISAM变为了InnoDB。MySQL还有其他的存储引擎,不过这两种是目前比较流行的存储引擎。
存储引擎是数据库中比较底层的模块,如果把MySQL的架构再简化一下可以分为三层,接入的通讯层(负责连接池管理等),中间的server层(SQL语句的解析,命令执行,优化缓存等)和最下面的存储引擎层,SQL的操作一部分是在server层处理,一部分是在存储引擎层处理。
查看存储引擎的命令:show engines

2.3 MyISAM和InnoDB的区别

1、MyISAM不支持事务,InnoDB是事务类型的存储引擎,当我们的表需要用到事务支持的时候,那肯定是不能选择MyISAM了。

2、MyISAM只支持表级锁,BDB支持页级锁和表级锁默认为页级锁,而InnoDB支持行级锁和表级锁默认为行级锁

表级锁:直接锁定整张表,在锁定期间,其他进程无法对该表进行写操作,如果设置的是写锁,那么其他进程读也不允许

MyISAM是表级锁定的存储引擎,它不会出现死锁问题

对于write,表锁定原理如下:

如果表上没有锁,在其上面放置一个写锁,否则,把锁定请求放在写锁队列中。

对于read,表锁定原理如下 :

如果表上没有写锁定,那么把一个读锁放在其上面,否则把锁请求放在读锁定队列中

当一个锁定被释放时,表可被写锁定队列中的线程得到,然后才是读锁定队列中的线程。这意味着,如果你在一个表上有许多更新,那么你的SELECT语句将等到所有的写锁定线程执行完。

行级锁:只对指定的行进行锁定,其他进程还是可以对表中的其他行进行操作的。

行级锁是Mysql粒度最小的一种锁,它能大大的减少数据库操作的冲突,但是粒度越小实现成本也越大。

行级锁可能会导致“死锁”,那到底是怎么导致的呢,分析原因:Mysql行级锁并不是直接锁记录,而是锁索引。索引分为主键索引和非主键索引两种,如果一条sql语句操作了主键索引,那么Mysql就会锁定这个主键索引,如果sql语句操作的是非主键索引,那么Mysql会先锁定这个非主键索引,再去锁定主键索引。

在UPDATE 和 DELETE操作时Mysql不仅会锁定所有WHERE 条件扫描过得索引,还会锁定相邻的键值。

“死锁”举例分析:

表Test:(ID,STATE,TIME) 主键索引:ID 非主键索引:STATE

当执行"UPDATE STATE =1011 WHERE STATE=1000" 语句的时候会锁定STATE索引,由于STATE 是非主键索引,所以Mysql还会去请求锁定ID索引

当另一个SQL语句与语句1几乎同时执行时:“UPDATE STATE=1010 WHERE ID=1” 对于语句2 Mysql会先锁定ID索引,由于语句2操作了STATE字段,所以Mysql还会请求锁定STATE索引。这时。彼此锁定着对方需要的索引,又都在等待对方释放锁定。所以出现了"死锁"的情况。

行级锁的优点:

有许多线程访问不同的行时,只存在少量的冲突。

回滚时只有少量的更改

可以长时间锁定单一的行

行级锁缺点:

相对于页级锁和表级锁来说占用了更多的内存

当表的大部分行在使用时,比页级锁和表级锁慢,因为你必须获得更多的锁

当在大部分数据上经常使用GROUP BY操作,肯定会比表级锁和页级锁慢。

页级锁:表级锁速度快,但是冲突多;行级锁速度慢,但冲突少;页级锁就是他俩折中的,一次锁定相邻的一组记录。

3、MyISAM引擎不支持外键,InnoDB支持外键

4、MyISAM引擎的表在大量高并发的读写下会经常出现表损坏的情况

我们以前做的项目就遇到这个问题,表的INSERT 和 UPDATE操作很频繁,原来用的MyISAM引擎,导致表隔三差五就损坏,后来更换成了InnoDB引擎。

其他容易导致表损坏原因:

服务器突然断电导致数据文件损坏,强制关机(mysqld未关闭情况下)导致表损坏

mysqld进程在写入操作的时候被杀掉

磁盘故障

表损坏常见症状:

查询表不能返回数据或返回部分数据

打开表失败: Can’t open file: ‘×××.MYI’ (errno: 145) 。

Error: Table 'p' is marked as crashed and should be repaired 。

Incorrect key file for table: '...'. Try to repair it

Mysql表的恢复:

对于MyISAM表的恢复:

可以使用Mysql自带的myisamchk工具: myisamchk -r tablename 或者 myisamchk -o tablename(比前面的更保险) 对表进行修复

5、对于count()查询来说MyISAM更有优势

因为MyISAM存储了表中的行数记录,执行SELECT COUNT() 的时候可以直接获取到结果,而InnoDB需要扫描全部数据后得到结果。

但是注意一点:对于带有WHERE 条件的 SELECT COUNT()语句两种引擎的表执行过程是一样的,都需要扫描全部数据后得到结果

6、 InnoDB是为处理巨大数据量时的最大性能设计,它的CPU效率可能是任何其它基于磁盘的关系数据库引擎所不能匹敌的。

7、MyISAM支持全文索引(FULLTEXT),InnoDB不支持

8、MyISAM引擎的表的查询、更新、插入的效率要比InnoDB高

总结一下,在写多读少的应用中还是Innodb插入性能更稳定,在并发情况下也能基本,如果是对读取速度要求比较快的应用还是选MyISAM。

2.4 Mysql的日志文件

linux操作系统环境下Mysql的日志相关文件都在/var/lib/mysql目录中,可以通过命令查看当前数据库中的日志使用信息:

MySQL>show variables like 'log_%';

2.4.1 错误日志(error log)

记录了运行过程中遇到的所有严重的错误信息,以及每次启动和关闭的详细信息。
其中log-error定义是否启动错误日志的功能和错误日志的存储位置,以直接定义为文件路径,也可以为ON|OFF;log_error_verbosity定义是否将警告信息也写入到错误日志中,三个可选值分别对应:1错误信息;2错误信息和告警信息;3错误信息、告警信息和通知信息。不过,从MySQL5.7.2版本开始,就不再推荐使用log-warning参数了,在8.0.3版本这个参数开始被移除。

2.4.2 二进制日志(bin log)

二进制日志默认是关闭的,通过配置:log-bin=/var/lib/mysql/mysql-bin进行开启。其中mysql-bin是binlog日志文件的basename,binlog日志文件的名称为mysql-bin-000001.log。
binlog记录了数据库所有的ddl语句和dml语句,但是不包括select语句的内容,语句以事件的形式保存,描述了数据变更的顺序,binlog还包括了更新语句的执行时间信息,binlog主要作用是用于恢复数据,实现主从复制。binlog对于灾难恢复和备份恢复至关重要,生产环境必须开启。
此外,如果是DDL语句,则直接记录到binlog日志中,如果是dml语句则需要通过事务提交后才会记录到binlog日志中。

2.4.3 通用查询日志(general query log)

通用查询日志默认关闭,需通过设置:general_log=ON开启。
通用查询日志会记录用户的所有操作,包含增删改查等信息,在高并发环境下会产生大量的信息导致不必要的磁盘IO,从而影响MySQL的性能,一般只在调试数据库的环境下才开启。

2.4.4 慢查询日志 (slow query log)

慢查询日志默认关闭,需通过设置:slow_query_log=ON开启。
记录执行时间超过long_query_time秒的所有查询,便于收集查询时间比较长的SQL。

2.4.5 事务日志(redo/undo log)

事务日志是InnoDB特有的日志,也叫redo日志。
文件名为“ib_logfile0”和“ib_logfile1”,默认存放在表空间所在的目录中。还有一个日志文件叫undo日志,默认存储在ib_data目录下。

2.4.6 中继日志(relay log)

主从复制中产生的日志,主要作用是为了从机可以从中继日志中获取到主机同步过来的SQL语句,然后执行到从机中。

(三)Mysql的索引

3.1 索引的结构

3.1.1 索引的存储结构

索引是帮助数据库高效获取数据的数据结构,就如同一本书的目录,可以加快查询数据库的速度。索引存储在磁盘上的文件中,可能存储在存储在单独的索引文件中,也可能和数据一起存储在数据文件中。

索引是在存储引擎中实现的,不同的存储引擎可以使用不同的索引结构。MyISAM和InnoDB存储引擎只支持BTREE索引。MEMORY/HEAP存储引擎支持HASH和BTREE索引。

3.1.2 B树和B+树的简介

数据库如果采用树结构作为索引,一般都会选择用B树或其衍生出来的B+树和B-树,目的是为了解决数据变化造成的树的结点平衡问题(二叉树就有这种问题)。B树节点变化是向根节点方向分裂或者合并,所以B树就是设计为一种多叉平衡查找树。
image.png

每一个节点占用一个磁盘块,通常磁盘块的大小是磁盘页大小的整数倍一致(16K)
B树和B+树最大的区别在于非叶子节点是否存储数据,B树的叶子和非叶子节点都会存储数据,而B+树只有叶子节点存储数据,而且存储的数据都是在一行上,这些数据都有指针指向,有序。
image.png

3.1.3 主键索引和辅助索引

MySQL的每一张表都会有一个主键索引(每张表必须有一个主键),除了主键索引之外,其他索引就是辅助索引,主键索引地位唯一并且特殊。

3.1.4 非聚集索引

非聚集索引的叶子节点只存储数据行(数据文件中)的指针,简单来说就是数据和索引不在一起,就是非聚集索引。主键索引和辅助索引的节点数据存储指针的值。
MyIsam存储引擎就使用的是非聚集索引,在物理实现上主键索引和辅助索引没有什么区别。查询数据的逻辑都是先根据在索引文件中通过索引查询到数据的指针,再通过指针在数据文件里查询数据。
Linux系统下/var/lib/mysql目录下放置表的文件:
.frm文件:主要存放与表相关的数据信息,主要包括表结构的定义信息
.myd文件:主要用来存储表数据信息,MyIsam用。
.myi文件:主要用来存储表数据文件中任何索引的数据树,MyIsam用。

3.1.5 聚集索引

聚集索引(和主键索引一体)的叶子节点会存储数据行,也就是说数据和索引在一起,辅助索引只会存储主键值。如果建表的时候没有定义主键,MySQL会从其他唯一索引中挑选一个作为主键索引,如果没有定义唯一索引,MySQL就会生成一个隐含字段作为主键,类型为长整型。
聚集索引这种实现方式是的按主键的搜索十分高效,但是辅助索引搜索需要检测两遍索引:首先检索辅助索引得到主键,再用主键到主索引中获得记录。这也是为什么不建议使用过长的字段作为主索引的原因,过长的主索引会令辅助索引变得过大。同时,主索引建议使用长整型,在查找比较的时候效率高。

3.2 组合索引

考虑到索引的使用率,尽量使用组合索引(多个字段建立),如下为建立组合索引的示例:

ALTER TABLE 'table_name' ADD INDEX index_name('a','b','c')

相当于建立了a,a+b,a+b+c三个索引。
创建组合索引应该将最常用做限制条件的列(使用频率最高的列)放到最左面,依次递减,组合索引最左字段用in可以用到索引。

3.3 使用索引注意事项

3.3.1 需要建立索引的情况

  • 主键会自动建立唯一索引
  • 频繁作为查询条件的字段应该创建索引
  • 多表关联查询的时候,关联字段应该创建索引
  • 查询中排序的字段,应该创建索引
  • 查询中统计或者分组的字段,应该创建索引

    3.3.2 不适合建立索引的情况

  • 表记录太少
  • 经常进行增删改操作的表
  • 频繁更新的字段
  • where条件中是使用频率不高的字段

    3.3.3 创建索引其它要注意的地方

  • 尽量创建组合索引,效果上相当于按照最左匹配原则创建了多组索引,实际上并没有真正创建其他索引。
  • 经常查询的列可以覆盖索引,如此可以直接通过索引拿到数据。
  • like ‘%aa%’和like‘%aa’不会使用索引,但是like'aa%'可以使用索引
  • is null、is not null 不会使用索引

    (四)Mysql执行计划

    4.1 EXPLAIN命令

    mysql提供了一个EXPLAIN命令,可以对select语句进行分析,并输出select执行的详细信息,以供开发人员针对性优化。EXPLAIN命令用法十分简单,在select语句前加上explain就可以了,可以在任何能够执行查询操作的地方使用,比如命令行或者客户端工具:
    image.png

4.2 执行计划解析

接下来对执行计划输出的列做一下自我介绍。

4.2.1 id

对于复杂的查询sql语句,MySQL会进行多此的查询操作,每一次都会有一个执行计划,每个查询都会分配一个唯一标识符,表示查询中操作表的顺序,有三种情况:

  • id相同:执行顺序由上到下
  • id不同:如果是子查询,id号会自增,id越大,优先级越高,id相同和不同的同时存在
  • id列为null的就表示这是一个结果集,不需要使用它来进行查询。

    4.2.2 select_type

    查询类型,用于区别普通查询、联合查询(union、union all)、子查询等复杂查询。
  • simple:普通查询,非union查询或者不包含子查询的简单select查询。
  • primary(主查询):一个需要union或者含有子查询的select,位于最外层的单位查询select_type即为primary,只有一个。
  • subquery:表示一次子查询
  • union:union连接的查询,第一个查询是dervied派生表,除了第一个表外,第二个以后的表select_type都是union。
  • deriver:from子句中出现的子查询,也叫做派生表,其他数据库中可能叫做内联视图或者嵌套select。
  • union result:包含union结果集,在union和union all语句中,因为它不需要参与查询,所以id字段为null。

    4.2.3 table

    显示的查询表名,如果查询使用了别名,那么这里显示的是别名。如果不涉及到对数据库表的操作,这里显示为null。如果显示为<derived N>就表示这个临时表,后边的N就是执行计划中的id,表示结果来自于这个查询产生。
    如果是尖括号括起来的<union M,N>,与<derived N>类似,也是一个临时表,表示这个结果来自于union查询的id为M,N的结果集。

    4.2.4 type

    这列非常重要,能告诉我们这此查询能否用到索引以及用到的程度。
    出现的类型从好到差排序:system>const>eq_ref>ref>fulltext>ref_or_null>unique_subquery>index_subquery>range>index_merge>index>all。
    除了all以外,其他的type都可以用到索引,除了index_merge之外,其他的type只会用到一个索引,如下是对出现的常用类型进行简单的介绍:
  • system:表中只有一行数据或者是空表,且只能用于myisam和memory表。如果是innodb引擎表,type列这个情况下通常都是all或者index。
  • const:使用唯一索引或者主键,返回记录一定1行记录的等值where条件。
  • eq_ref:出现在要连接多个表的查询计划中,驱动表只返回一行数据,且这行数据是第二个表的主键或者唯一索引,且必须为not null。如果唯一索引和主键是多列时,只有所有列都用作比较时才会出现eq_ref。
  • 不像eq_ref那样要求连接顺序,也没有主键和唯一索引的要求,只要使用相等条件检索时就可能出现。常见于用辅助索引的等值查找。
  • fulltext:全文索引检索,主要用于text类型的字段,全文索引的优先级很高,若全文索引和普通索引同时存在,MySQL不管代价,优先使用全文索引。
  • ref_or_null:与ref方法类似,只是增加了null值得比较
  • unique_subquery:where中得in形式的子查询,子查询返回不重复的唯一值。
  • index_subquery:用于in形式的子查询使用到了辅助索引或者in常数列表,子查询可能返回重复值,可以使用索引将子查询去重。
  • rang:索引范围扫描,常见于使用is null、between、in、like等运算符的查询中。
  • index_merge:表示查询用来两个以上的索引,最后取交集或者并集,常见的and,or的条件使用了不用的索引。
  • index:索引全表扫描,把索引从头到位扫一遍,常见于使用索引列就可以处理不需要读取数据文件的查询,可以使用索引排序或者分组查询。
  • all:全表扫描数据文件,效率最差,要避免这种情况的出现。

    4.2.5 possible_key

    此次查询中可能选用的索引,一个或者多个

    4.2.6 key

    查询中真正用到的索引,select_type为index_merge时,这里可能会出现两个以上的索引,其他的select_merge时,这里只会出现一个

    4.2.7 key_len

    这里表示的是用于处理查询的索引的长度,只计算where条件用到的索引长度,而非排序和分组用到的索引。

    4.2.8 ref

  • 如果是使用的常数等值查询,这里会显示const。

  • 如果是连接查询,被驱动表的执行计划这里会显示驱动表的关联字段
  • 如果是条件使用了表达式或者函数,或者条件列发生了内部隐式转换,这里可能显示func。

    4.2.9 rows

    执行计划估算扫描行数,不是精确值(innodb不是精确值,myisam是精确值,主要原因是innodb里面使用了mvcc并发机制)

    4.2.10 filtered

    这个字段表示存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量比例,单位是百分比,不是具体记录数。越接近100%表示server层的处理越少,也就说明查询能够更多利用存储层的过滤,效率越高。

    4.2.11 extra

    这个列显示一些额外信息,比如在select部分使用了distinct,这个列就会显示distinct;no tables used:不带from字句的查询或者From dual查询。using index:查询时直接通过索引就可以获取查询的数据,使用到了覆盖索引(convering index)避免访问表的数据行,效率好。如果同时出现了using where,说明索引被用来执行查找索引值,如果没有同时出现using where则表明索引用来读取数据而非执行查找动作。

    (五)MySQL的锁

    5.1 锁的介绍

    对于数据库来说,如果存在同一时刻对同一条数据有多个读和写操作,就会产生数据不一致的问题。锁机制的目的就是进行并发控制,解决数据不一致问题。
    MySQL数据库由于其自身架构的特点,存在多种数据存储引擎,每种存储引擎所针对的应用场景特点都不太一样,为了满足各自特定的应用场景的需求,每种存储引擎的锁定机制都是为各自所面对的特定场景而优化设计,所以各存储引擎的锁定机制也有较大的差别,这里只介绍最常用的InnoDB存储引擎的锁。

    5.2 锁的分类

    按照锁的粒度来分:行级锁和表级锁
    按照锁的功能来分:共享锁(读锁)和排他锁(写锁)
    从实现上来分:悲观锁和乐观锁
    如上三种类别可以组合。
    MySQL的InnoDB和MyISAM存储引擎最大的区别就是:InnoDB对事务的支持、行锁的支持。

    5.2.1 行锁

    行级锁定的最大优点就是锁定对象的颗粒度很小,也是目前各大数据库管理软件所实现的锁定颗粒度最胆小的,所以发生锁定资源争用的概率也最小,提升了系统的并发能力。
    同时因为锁定资源的颗粒度小,所以每次获取锁和释放锁需要做的事情也更多,带来的消耗也更打了,此外,行级锁定也是最容易发生死锁。

    5.2.2 表锁

    表级别的锁定是MySQL各存储引擎中最大颗粒度的锁定机制。该锁定机制实现的逻辑非常简单,带来的系统负面影响最小,所以获取和释放锁的速度最快。由于表级锁一次会将整个表锁定,所以可以很好的避免死锁的问题。
    锁定颗粒度大所带来的负面影响是出现锁定资源争用的概率也最高,并发度低。
    MySQL的表级锁定有两种模式:表共享读锁(Table Read Lock)和表独占写锁(Table Write Lock),表锁是server层实现的。

    5.3 InnoDB存储引擎的锁机制

    5.3.1 InnoDB锁的基本概念

    5.3.1.1 InnoDB锁分类

    共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
    排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。
    意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。
    意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。

    5.3.1.1 意向锁的作用

    在MySQL中有表锁,例如:
    LOCK TABLE my_tabl_name READ;用读锁锁表,会阻塞其他事务修改表数据。
    LOCK TABLE my_tabl_name WRITE;用写锁锁表,会阻塞其他事务读和写。
    InnoDB引擎又支持行锁,行锁分为共享锁和排它锁。考虑如下这两种类型的锁共存的问题,比如:
    事务A用共享锁锁住了表中的一行,该行只能读,不能写。然后事务B申请了整个表的写锁。如果事务B申请成功,那么理论上它就能修改表中的任意一行,这与A持有的行锁是冲突的。数据库需要避免这种冲突,就是说要让B的申请被阻塞,直到A释放了行锁。
    那么数据库是如何判断这个冲突的呢?step1:判断表是否已被其他事务用表锁表,step2:判断表中的每一行是否已被行锁锁住。这种判断方法效率太低,因为需要遍历整个表。意向锁就是为了解决这种情况,在意向锁存在的情况下,事务A必须先申请表的意向共享锁,成功后再申请一行的行锁。在意向锁存在的情况下,上面的判断可以改成step1:不变,step2:发现表上有意向共享锁,说明表中有些行被共享锁锁住了,因此事务B申请表的写锁会被阻塞。
    注意:申请意向锁的动作是数据库完成的,就是说事务A申请一行的行锁的时候,数据库会自动先开始申请表的意向锁,不需要程序显示用代码来申请。

    5.3.2 InnoDB锁基本逻辑

    1)共享锁和排他锁都是行锁,意向锁都是表锁,应用中我们只会使用到共享锁和排他锁,意向锁是MySQL内部使用的,无需用户干预。
    2)对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁,普通的select语句,InnoDB不会加任何锁,事务可以通过以下语句显示给记录集加共享锁和排他锁“

  • 共享锁(S):SELECT * FROM table_name WHERE...LOCK IN SHARE MODE

  • 排他锁(X):SELECT * FROM table_name WHERE...FOR UPDATE。
    3)InnoDB行锁是通过给索引上的索引项加锁来实现的,因此只有通过索引条件检索数据,InnoDB才使用行级锁,否则InnoDB将使用表锁。
    4)InnoDB的行级锁定同样分为两种类型,共享锁和排他锁,而在锁定机制的实现过程中为了让行级锁定和表级锁定共存,InnoDB也同样使用了意向锁(表级锁定)的概念,也就有了意向共享锁和意向排他锁这两种。
    5)InnoDB的锁定是通过在指向数据记录的第一个索引键之前和最后一个索引键之后的空域空间上标记锁定信息而实现的。InnoDB的这种锁定实现方式被称为”NEXT-KEY locking“(间隙锁,GAP锁),因为query执行过程通过范围查找的话,他会锁定整个范围内所有的索引键值,即使这个键值并不存在。间隙锁是MySQL特有的,能够解决幻读问题。

    5.3.3 行级锁状态查看

    使用如下命令查看:show status like'innodb_row_lock%';
  • Innodb_row_lock_current_waits:当前正在等待锁定的数量
  • Innodb_row_lock_time:从系统启动到现在锁定总时间长度
  • Innodb_row_lock_time_avg:每次等待所花费的平均时长
  • Innodb_row_lock_time_max:从系统启动到现在等待最长一次所花的时间。
  • Innodb_row_lock_waits:系统启动后到现在总共等待的次数。
    如果出现等待次数很高,而且每次等待时长也不小的时候就需要分析系统的SQL执行情况了。

    5.4 其他相关数据库概念

    5.4.1 Isolation Level

    隔离级别是RDBMS的一个关键特性,总共定义了4种隔离级别:
    Read Uncommited:可以读取未提交记录。此隔离级别不会使用,忽略。
    Read Commited(RC):快照读忽略,对于当前读,RC隔离级别保证对读取到的记录加锁(记录锁),存在幻读现象。
    Repeatable Read(RR):快照读忽略,对于当前读,RR隔离级别保证对读取到的记录加锁(记录锁),防止了脏读和不可重复读,但是可能会出现幻读。
    Serializable:从MVCC并发控制退化为基于锁的并发控制。不区别快照读和当前读,所有的读操作均为当前读,读加读锁(S锁),写加写锁(x锁)。Serializable隔离级别下,读写冲突,因此并发度急剧下降,一般不建议使用。

    5.4.2 MVCC

    InnoDB存储引擎实现了多版本的并发控制--MVCC。与MVCC相对的是基于锁的并发控制--Lock-Based Concurrency Control。
    MVCC最大特点是读不加锁,读写不冲突。在读多写少的OLTP应用中极大的增加了系统的并发性能。现阶段几乎所有的RDBMS,都支持MVCC。
    在MVCC并发控制中,读操作可以分为两类:快照读(snapshot read)与当前读(current read);
    快照读:读取的是记录的可见版本(有可能是历史版本),不用加锁。简单的select操作属于快照读,不加锁。
    当前读:读取的是记录的最新版本,并且当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录。特殊的读操作,插入/更新/删除操作,属于当前读,需要加锁。

    5.4.3 2PL:Two-Phase Locking

    2PL(二阶段锁):顾名思义,锁分为两个阶段,加锁阶段和解锁阶段,并且保证加锁阶段和解锁阶段不相交。加锁阶段:只加锁,不放锁。解锁阶段:只放锁不加锁。
    定理:若所有事务均遵循两段锁协议,则这些事务的所有交叉调度都是可串行化的。对于遵循两段协议的事务,其交叉并发操作的执行结果一定是正确的。
    值得注意的是:遵循两段锁协议的事务有可能发生死锁。
相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
2月前
|
分布式计算 关系型数据库 MySQL
大数据-88 Spark 集群 案例学习 Spark Scala 案例 SuperWordCount 计算结果数据写入MySQL
大数据-88 Spark 集群 案例学习 Spark Scala 案例 SuperWordCount 计算结果数据写入MySQL
56 3
|
2天前
|
SQL 存储 关系型数据库
【MySQL基础篇】全面学习总结SQL语法、DataGrip安装教程
本文详细介绍了MySQL中的SQL语法,包括数据定义(DDL)、数据操作(DML)、数据查询(DQL)和数据控制(DCL)四个主要部分。内容涵盖了创建、修改和删除数据库、表以及表字段的操作,以及通过图形化工具DataGrip进行数据库管理和查询。此外,还讲解了数据的增、删、改、查操作,以及查询语句的条件、聚合函数、分组、排序和分页等知识点。
【MySQL基础篇】全面学习总结SQL语法、DataGrip安装教程
|
2月前
|
关系型数据库 MySQL Java
Django学习二:配置mysql,创建model实例,自动创建数据库表,对mysql数据库表已经创建好的进行直接操作和实验。
这篇文章是关于如何使用Django框架配置MySQL数据库,创建模型实例,并自动或手动创建数据库表,以及对这些表进行操作的详细教程。
95 0
Django学习二:配置mysql,创建model实例,自动创建数据库表,对mysql数据库表已经创建好的进行直接操作和实验。
|
2月前
|
Java 关系型数据库 MySQL
springboot学习五:springboot整合Mybatis 连接 mysql数据库
这篇文章是关于如何使用Spring Boot整合MyBatis来连接MySQL数据库,并进行基本的增删改查操作的教程。
251 0
springboot学习五:springboot整合Mybatis 连接 mysql数据库
|
2月前
|
Java 关系型数据库 MySQL
springboot学习四:springboot链接mysql数据库,使用JdbcTemplate 操作mysql
这篇文章是关于如何使用Spring Boot框架通过JdbcTemplate操作MySQL数据库的教程。
93 0
springboot学习四:springboot链接mysql数据库,使用JdbcTemplate 操作mysql
|
2月前
|
关系型数据库 MySQL 数据库
mysql关系型数据库的学习
mysql关系型数据库的学习
23 0
|
2月前
|
Kubernetes 关系型数据库 MySQL
k8s学习--利用helm部署应用mysql,加深helm的理解
k8s学习--利用helm部署应用mysql,加深helm的理解
310 0
|
3月前
|
SQL 关系型数据库 MySQL
学习MySQL操作的有效方法
学习MySQL操作的有效方法
51 3
|
3月前
|
SQL 关系型数据库 MySQL
如何学习 MySQL?
如何学习 MySQL?
42 3
|
4月前
|
SQL 关系型数据库 MySQL
学习mysql基础操作
【8月更文挑战第20天】学习mysql基础操作
43 1