文章目录

  • 前言
  • 一、MySQL的目录结构
    • 1.1、认识目录文件
    • 1.2、配置文件设置
      • windows平台下设置
      • linux环境下设置
  • 二、MySQL的系统架构
    • 2.1、MySQL系统的逻辑架构:
    • 2.2、MySQL系统架构(包含每个部分介绍)
    • 2.3、MySQL的查询过程
  • 三、学习I/O原理以及数据库选型
    • 3.1、学习计算机硬盘原理
    • 3.2、Mysql数据库的数据结构选型
      • 为什么选择B树数据结构?
      • B-树
      • B+树(InnoDB与MyISAM底层使用)
      • 实践(感受索引的力量)
    • 3.3、为什么使用B+树
  • 四、MySQL索引
    • 4.1、索引的基本知识
    • 4.2、回表与索引覆盖
      • 介绍回表与索引覆盖
      • 引出聚簇索引与非聚簇索引(重要)
    • 4.3、索引的分类与创建
      • 4.3.1、聚簇索引与非聚簇索引(InnoDB与MyISAM不同的索引表)
      • 4.3.2、主键索引
      • 4.3.3、普通索引(常规索引,normal)
      • 4.3.4、唯一索引(unique)
      • 4.3.5、全文索引(fulltext,不常用)
      • 4.3.6、空间索引(spatial,不常用)
      • 4.3.7、复合索引(联合索引,重点!!!)
        • 认识与创建复合索引
        • 实例介绍复合索引的最左匹配原则(重要)
        • 最左匹配原则及优化器
      • 4.3.8、哈希索引(Hash,不常用也重要)
        • 认识与创建哈希索引
        • 使用哈希来解决BTREE索引较大的字段
    • 4.4、使用索引的问题
      • 4.4.1、索引究竟何时使用?
      • 4.4.2、索引不会包含有NULL值的列
      • 4.4.3、短索引使用
      • 4.4.4、排序(order by)的索引问题
      • 4.4.5、MySQL索引失效的几种情况
    • explain关键字
      • 1、id字段(三种情况)
      • 2、select_type
      • 3、type字段(重要)
        • 11种访问类型说明
        • 小总结
        • 全表扫描注意事项
      • 4、table
      • 5、partitions(分区)
      • 6、possible_keys
      • 7、key(重要)
      • 8、key_len
      • 9、ref
      • 10、rows(重要)
      • 11、filtered
      • 12、Extra(重要)
      • 总结
  • 五、MySQL事务
    • 5.1、事务介绍
    • 5.2、事务的四大特征
    • 5.3、事务并发问题描述(4个)
    • 5.4、事务的特性—隔离性
      • 5.4.1、查看与设置隔离级别
      • 5.4.2、4种隔离级别
      • 5.4.3、示例演示隔离级别解决的问题
        • 解决不可重复读
  • 六、MySQL的锁机制
    • 介绍锁的概念
    • 6.1、InnoDB行锁
      • ①记录锁(Record Lock)
        • (1)、name字段无索引(上表锁)
        • (2)、name字段带索引(上行锁)
        • 总结
      • ②间隙锁(GAP Lock)
        • 介绍说明
        • 实际案例
      • ③临键锁(记录锁与间隙锁组合,next-key lock)
      • 总结
    • 6.2、表锁(含读、写锁)
      • 介绍表锁
      • 实际案例
        • 读锁示例
        • 写锁示例
      • 总结
    • 6.3、InnoDB的锁类型
      • ①读锁
      • ②写锁
      • ③MDL锁
      • ④意向锁
      • 总结
    • 6.4、其他角度对于锁的分类
      • 乐观锁(不加锁)
      • 悲观锁(加锁)
    • 6.5、锁等待
      • 介绍锁等待
      • 出现lock wait timeout ...
    • 6.6、死锁
      • 介绍死锁以及出现案例
      • 如何避免死锁
    • 各类锁总结
    • 6.7、MVCC
      • 6.7.1、引出与介绍MVCC
      • 6.7.2、认识当前读与快照读
      • 6.7.3、MVCC解决的问题
      • 6.7.4、MVCC实现原理(含undo log)
      • 6.7.5、read view(读视图)
  • MySQL中的三种日志
    • undo log(旧日志)
    • redo log(物理日志)
    • bin log(逻辑日志)
    • 总结
  • 参考文章

前言

本篇博客是MySQL的学习笔记,若文章中出现相关问题,请指出!

所有博客文件目录索引:博客目录索引(持续更新)

一、MySQL的目录结构

1.1、认识目录文件

手动安装mysql版

整体的目录结构

  • bin目录:用于放置一些可执行文件,如mysql.exe、mysqld.exe、mysqlshow.exe等。
  • data目录: 用于放置一些日志文件以及数据库。
  • docs目录:文档。
  • include目录:用于放置一些头文件,如:mysql.h、mysql_ername.h等。
  • lib目录:用于放置一系列库文件。
  • share目录: 用于存放字符集、语言等信息。

数据文件目录

  • data目录:存储数据库文件及表内容。
  • my.ini:配置文件,用于修改一些mysql的配置,如端口号,数据库连接池等。

1.2、配置文件设置

配置文件中详细信息见:mysql目录结构及配置文件

windows平台下设置

一般在C:\ProgramData\MySQL\MySQL Server 5.7目录下的my.ini,直接更改其中配置后重启服务器即可生效。


linux环境下设置

Linux查看mysql数据库服务器的bin工具:①cd /usr/binls | grep mysql

②配置文件my.cnf存放在哪里?

查看修改配置文件my.cnf:①cd /etc/ ②vim my.cnf

  • 其中包含了datadir(数据库中数据目录的位置),pid-file为存放mysql运行的进程pid号文件通过使用cat命令即可查看进程号。获取到进程号之后可使用kill pid 来关闭数据库。

两种修改配置文件方式

  1. 修改配置文件并重启服务器(对上线项目不推荐使用):vim打开/etc/目录下的my.cnf配置文件,修改设置之后,使用systemctl restart mysqld命令重启数据库。(针对于配置文件,永久改变)

  2. 设置全局变量方式(推荐),必须指定"GLOBAL"或者"@@global",同时必须要有super权限。(针对于内存加载的数据库配置,所有连接到服务器的用户都会使用该全局变量配置,重启之后就会不生效)

    • -- 设置全局变量(两种形式),需要进入到mysql环境命令行中
      mysql> select global wait_timeout=10;
      mysql> select @@global.wait_timeout=10;
      -- 查看全局变量是否设置成功(两种方式)
      mysql> select @@global.wait_timeout
      mysql> show global variables like 'wait_timeout'
      
    • 说明:使用这种方式的话,能够在不重启数据库前提下使用修改后的配置,不会造成生产环境出现问题,不过需要记得同时修改一下配置文件中对应配置信息,以防下次重启服务器会不生效。

  3. 设置会话变量方式:通过使用@@、@@session、@@local来进行设置。(针对于内存加载的数据库配置,只针对本次连接会话)

    • -- 设置会话变量(三种方式)
      mysql> select @@wait_timeout=10;
      mysql> select @@session.wait_timeout=10;
      mysql> select @@local.wait_timeout=10;
      -- 查看会话设置变量是否成功
      mysql> show variables like 'wait_timeout';
      mysql> show local variables like 'wait_timeout';
      mysql> show session variables like 'wait_timeout';
      
    • 说明:这种方式只对本次连接会话有效,换一个连接就不生效了!


二、MySQL的系统架构

2.1、MySQL系统的逻辑架构:


2.2、MySQL系统架构(包含每个部分介绍)

  • Connectors(Mysql向外提供的交互接口):连接器组件,是Mysql服务器向外提供的交互组件,上面也列举了多种语言来通过该组件操作sql语句,实现与mysql服务器进行交互。

  • Management Service & Utilities(管理组件与工具组件):提供对Mysql的集成管理,如备份(Backup)、恢复(Recovery)、安全管理(Security)等。

  • Connection Pool(连接池组件):负责监听客户端向mysql服务端发送的请求,接收请求,转发请求到目标模块,每个成功连接到Mysql服务器的客户请求都会被创建或分配一个线程,该线程负责客户端与Mysql服务器的通信,如接收客户端的命令,传递服务端的执行命令的结果信息等。

    • 连接池:若是没有连接池的话,来一个请求就要开启一个端口分配一个线程这样的话就会十分的浪费资源,通过使用连接池能够更好的管理连接,提供资源利用率。保证总连接数不超出预计数量的连接,更好的进行管理。
  • SQL interface(SQL接口组件):接收用户SQL命令,如DML、DDL和存储过程等,并将最终结果返回给用户。

  • Parser(查询分析器组件):首先分析SQL命令语法的合法性,并尝试将SQL命令分解为数据结构,若分解失败,则提示SQL语句不合理。

  • Optimizer(优化器组件):对SQL命令按照标准流程进行优化分析,那么就不是按照你写的sql语句从上至下执行了,根据对应优化策略来执行。(与cpu的指令重排都有相似的目的)

  • Caches & Buffers(缓存组件):缓存与缓冲组件。

    • 缓冲组件:将多条sql先放置在缓冲中,之后再统一提交执行。Mysql8把缓冲给自动关闭了。
    • 缓存:将一些sql语句及对应的查询结果(key、value形式)通过缓存形式保存到内存中,若是下次过来一条查询语句与缓存中sql语句一直,那么直接将查询结果返回。
  • Pluggable Storage Engines(可插拔存储引擎,mysql的存储引擎):如上图所示,包含多种类型的存储引擎,其各自都有优缺点,现在大多使用MyISAM以及InnDB,一般公司都使用InnDB,大公司的话考虑不同场景会选择MyISAM引擎。

    • 用途:存储引擎与文件系统打交道,存储引擎帮助我们去拿数据,存储数据也是存储引擎帮我们做,存储到文件系统。
  • File System(文件系统):

    • NTFS(New Technology File System,日志文件系统,最早是windows NT内核支持的,是一种磁盘格式)-NFS(Network File System,网络文件系统,能使使用者访问网络上别处的文件就像在使用自己的计算机一样);
    • SAN(Storage Area Network,存储区域网络,是一种专为存储网络建立的TCP/IP之外的专用网络)-NAS(Network Attached Storage,网络附属存储)
  • Files & Logs:文件与日志。


2.3、MySQL的查询过程

查询过程图如下

  1. 首先客户端发送一条SQL给服务器。
  2. 服务器会先检查查询缓存,如果命中了缓存,则直接返回存储在缓存中的结果(到此直接结束);若是没有命中进入下一阶段。
  3. 服务器端进行SQL解析、预处理,再由优化器生成对应的执行计划。
  4. MySQL根据优化器生成的执行计划,调用存储引擎的API来执行查询。
  5. 将查询结果返回给客户端。

对于其中每个步骤详细内容见:MySQL查询执行过程


三、学习I/O原理以及数据库选型

3.1、学习计算机硬盘原理

认识硬盘与磁盘的区别

  • 硬盘能够持久保存数据,但是执行效率慢;内存速度快,必须在有电情况下载才能存储数据,一旦掉电数据都会丢失。

为什么内存读的快,硬盘读得慢

  • 内存通过地址来读取数据,是随机读取;硬盘的话是将数据烧在盘片上,读取数据需要磁头从外向内读取(寻道)。一个是通过电来读取,一个是通过机械来读取,自然而然就是内存读的更快。
  • 固态硬盘比一般的硬盘(机械硬盘)读取快。但是比内存慢。CPU速度比内存更快。CPU>内存>硬盘(固态硬盘>机械硬盘)

相关知识

  1. 硬盘相关的名词:盘片、片面、磁头;扇区和磁道;磁头与柱面。
  2. 磁盘的存储容量=磁头数x每道扇区数x每扇区字节数。
  3. 读写一次磁盘信息所需要的时间:寻道时间,延迟时间,传输时间。
  4. 操作系统经常与内存、硬盘进行通信。操作系统通信内存以来作为最小单位,通信硬盘以为最小单位(认为好几个扇区)。
    • 扇区:硬盘的最小读取单元。
    • 块/簇:操作系统针对硬盘读写的最小单元。
    • page:内存与操作系统之间操作的最小单元。
      • 扇区<=块/簇<=page

知识点详细讲解见:IT楠老师—磁盘概念

重要知识点

计算机读取磁盘数据流程:当需要从磁盘读取数据时,系统会将数据地址传给磁盘,即确定要读的数据在哪个磁道,哪个扇区。为了读取这个扇区的数据,需要将磁头放到这个扇区上面(需要有一个操作过程—磁头需要移动对准相应磁道,这个过程叫做寻道,所耗费的时间叫做寻道时间),接着磁盘旋转将目标扇区转到磁头下,这个过程耗费的时间叫做旋转时间

  • IO操作即指的是外存操作,输入输出操作。

局部性原理与磁盘预读

  • 局部性原理:由于存储介质的特性,磁盘本身存取就比主存慢很多,加上机械运动耗费,磁盘的存取速度往往是主存的的十万分之一,因此为了提高效率,要尽量减少磁盘I/O(局部性原理目的)。为了达到该目的,硬盘往往不是严格按需读取,而是每次都会预读,比如本来只需要1个字节的数据,磁盘也会从该位置开始,顺序再依次向后读取一定长度的数据放入内存,因为科学认为既然你需要这部分位置的1个字节,那么后面连续的位置你在之后极有可能会再次访问,所以会直接多读取一部分到内存中,这就是计算机科学中著名的局部性原理
  • 预读:即践行局部性原理,在读取定长的数据时多读取一部分连续的值,这样下次若是需要该连续的字段就不需要再进行一次I/O操作,提升了性能!
    • 预读的长度一般为(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为页(大多操作系统中,页大小为4KB=212),磁盘与主存以页为单位交换数据。
    • 当程序要读取的数据不在主存中,会触发一个缺页异常,接着系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,接着异常返回,程序继续执行。

各个硬件延迟对比

  • L1 cache:高速缓存,Main memory:主存(内存条),disk seek:磁盘。可以看到磁盘的延迟最大!

3.2、Mysql数据库的数据结构选型

为什么选择B树数据结构?

数组:使用该数据结构对于插入数据的话,插入位置后的数据都需要往后移动。

链表:对于查询的话需要全表遍历,时间复杂度为log(n)

hash:类似于hashmap,其速度很快,但是只要是哈希就会有无序的问题,像数据库中就有order by(排序)操作,所以该种数据结构只能说不常用但也有。

:对于为什么不使用上面的数据结构都进行了说明,看来看去觉得树结构更加合理。

  • 二叉查找树:在查找一个数据时,二叉树是从根节点开始读起,若是查找数比当前节点小的话从左找,大则右找,每次读取一个数据,没有办法合理的利用局部性原理与磁盘预读,I/O次数太多太多,还有就是树的层次还是比较高,所以并不适合。
  • 查询时间复杂度为log2n(数据量越大,性能越高),对于1024条数据,依旧要查找10次才能找到。(2n=1024,n=10),I/O次数还是比较多。
  • B-树B+树:既然二叉查找也不太适合了,我们看向B树,每次读多个数据,每一个节点存多个数据的结构(减少I/O)就只有B-树和B+树。

说明:上面对于不同数据结构进行了分析,并且说明了不适合数据库进行读取,插入的缘由,最终确定来使用B树这种数据类型结构,因为其每个节点存多个数据,在二叉查找树之上减少了I/O次数,提升了性能,下面来看一下B+树与B-树。


B-树

B-树就是B树,其中的-并不是减号意思。B-树与B树都是B-tree 的翻译,两者是同一种数。

五分钟搞懂什么是B-树(全程图解) : 浅显易懂,讲解了在数据库中使用B-树的查询。

B-树(或称B树):类似普通的平衡树,B-树是一种平衡多路查找树。

为什么相对于二叉搜索树会更选择B-树

  • 其实B树在查询中的比较次数其实不比二叉查找树少,尤其是当单一节点中的元素数量很多时,可是相比磁盘IO的速度,在内存中的比较耗时几乎不计,所以只要树的高度足够低,IO次数足够少,就可以提升查找性能。

下面是B-数的简化图:二阶B-树为例(二阶的话,若是添加数据不会产生新阶,而是一直在第二层中扩展增加节点)

上面的B-树就是我们所说的索引,索引一般存储在磁盘中以如上的形式存储。内存每次读取磁盘时读取页的整数倍,一般每次读取一个节点,一个节点如下:

B-树的特点:

  1. 所有键值分布在整棵树中。
  2. 任何一个关键字出现且只出现在一个结点中。
  3. 搜索有可能在非叶子节点中结束。
  4. 在关键字全集内做一次查找性能逼近二分查找。

注意(重点):看图根节点不仅存放了节点(如主键id)还存放了对应数据信息。


B+树(InnoDB与MyISAM底层使用)

B+树:是B-树的变体,也是一种多路搜索树。

B+树简化图如下:二阶B+树

上图B+树与之前B-树示例的两点不同之处

  1. 非叶子节点并不存储真正的data(数据)如图根节点,所有的关键字存储在叶子节点出现。
  2. 所有的叶子节点增加了一个链指针(实质就是一个链表)。

举两个例子来说明吧:首先需要知道在Mysql数据库中的InnoDB存储引擎中的B+树既存储了索引又存储了所有的数据,MyISAM中的B+树只存储了索引的key值地址,实际的数据另外存储。推荐阅读1:MySQL中MyISAM与InnoDB底层使用的数据结构 、推荐阅读2——MySQL存储引擎MyISAM和InnoDB底层索引结构

  • 例子1(索引查询):通过主键id来查询数据,由于主键是索引,所以在进行I/O操作时是从根节点开始读取对应的索引id,从上至下能够很快找到对应的数据,如我要查询id为71的,只需要两次I/O操作即可找到该数据位置(如上图)!
  • 例子2(非索引查询):通过一个字段名来查找,由于该字段名没有索引,并且在B+树中除了叶子节点其他都是只存储索引的,那么对于该字段名查找就会从最底层的叶子节点进行一个个查询,就是相当于链表的整体遍历查询(从前到后),可想而知速度有多慢。

实践(感受索引的力量)

准备工作

目的描述:首先我们需要准备一个500000条数据的表格,确保每条记录中的一个字段不同方便后面测试!

-- ①选择test数据库(没有就新建一个,编码格式为:utf8,排序规则:utf8_general_ci)
use test;-- ②创建一个student表
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student`  (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`sex` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`education` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`randnum` char(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5052348 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;-- ③定义函数过程:插入5000000(五百万)条数据
CREATE DEFINER=`root`@`localhost` PROCEDURE `insertRecords`()
BEGINDECLARE c int DEFAULT 0; -- 声明c默认为0,int类型WHILE c < 5000000 DO   -- 循环10次INSERT into student(`name`,sex,education,randnum) VALUES("小王",'男',"苏州市职业大学",REPLACE(UUID(),'-',''));  -- 其中randnum使用uuid()生成set c = c+1;END WHILE;
END

此时数据库、表以及函数过程创建好了,接下来调用函数过程来插入五百万条数据:

-- 执行插入语句
CALL insertRecords()
  • 插入时间大概有5-6小时,占据磁盘400MB容量。

开始测试

①简单查询有索引与无索引的字段:可看到id是主键,默认带有索引

查询带有索引的主键id:SELECT * from student where id = 5000000;

查询没有带索引的randnum(也就是第5000000条):

②接下来我们设置randnum为索引再次查询测试一下

设置索引内容如下:

  • 设置好索引之后,我们可以看到索引长度一下子增长了237MB:

我去设置个索引字段占我磁盘这么大!!!赶紧看看有没有给我增加性能了,再测试一下:

好家伙好家伙没让我失望,这速度快了106倍,这查询速度舒服了!!!

说明:通过本次实践体会到了建立索引带来的好处,不过随之带来的就是磁盘资源的大量占用,有利也有弊,以空间换时间。


3.3、为什么使用B+树

再次看下B+树:

  1. 对于非叶子节点不存储数据,那么每个节点就能够存储更多的索引id,每个节点的范围更大更精确,这样的话I/O效率更高(减少IO)。
  2. MySQL是一种关系型数据库,区间访问是常见的一种情况(如区间查找),在B+树叶子节点中增加了链指针,加强了区间的访问性!

四、MySQL索引

4.1、索引的基本知识

【MySql数据库】索引相关知识总结 :可查看。

索引是什么

  • 可以给表中的字段设置索引,设置索引之后会对该字段进行B+树形式(或hash)的字段排序,提高了查询的速度。
  • 索引具有两种类型:B树索引(BTREE)与哈希索引(HASH)。
    • InnoDBMyISAM存储引擎支持BTREE索引,不支持HASH索引(一般使用这两种引擎)。
    • MEMORY存储引擎支持BTREEHASH索引。

为什么使用索引

  • MySQL的数据都是存在磁盘上的,若是只是按照普通方式存储(非树结构)那么查询一条记录会相对来说会很慢,建立索引的数据表在存储时使用B+树结构,这样就能够可以快速查询出数据。

聚簇索引特点:聚簇索引具有唯一性,由于聚簇索引是将数据根索引结构放到一块,因此一个表仅有一个聚簇索引。

索引的缺点

  1. 占用磁盘空间。
  2. 增加了插入和删除的操作时间。一个表拥有的索引越多,插入和删除的速度越慢。如要求快速录入的系统不宜建过多索引。原因:每插入或删除都要在维护数据的同时去维护聚簇索引或非聚簇索引。

什么时候添加索引

  • 一定要在查询数据慢的时候再加索引,不要什么情况都加索引!!!

按照索引方式查找是什么

  • 查找数据不需要一条一条从头到尾找,只需要按照B+树的存储结构自上而下找。

为什么设置主键自增id?①将id设置为主键其会默认设置为索引,形成一个聚簇索引;②若表没有主键id,依旧会有B+树结构,InnoDB会选择一个唯一且非空的索引代替;③若是没有这样的索引,InnoDB会隐式定义一个主键(类似于oracle的rowid,行号)来作为聚簇索引。

  • 如果设置了主键又希望单独设置聚簇索引呢?必须先删除主键,然后添加到我们想要的聚簇索引,最后恢复设置主键即可。
  • 不使用uuid作为主键原因:uuid生成的为36个字符,若是每次新增一条记录可能会在B+数的叶子节点中间去插入(会造成重新组织编排页),而相对于若是使用自增id作为主键,每次新增都是直接增加在最后,会更加好维护索引。
  • 最好建议:使用自增id作为主键。

表中行的物理顺序与索引中行的物理顺序是相同的,在创建任何非聚簇索引之前创建聚簇索引,这是因为聚簇索引改变了表中行的物理顺序,数据行,会按照一定的顺序排序并且自动维护这个顺序。


4.2、回表与索引覆盖

介绍回表与索引覆盖

根据索引字段来查询一般是这样的:

  • 根据索引在B+树上自上而下查询。
  • 一直到k层的叶子节点中查找到id。

回表:在非聚簇索引中找到id,接着根据id去聚簇索引中查找真正的数据(后面查找过程叫做回表)。

索引覆盖:若是以查找的数据直接通过索引找到了,没有回表操作即为覆盖索引。

说明:聚簇索引与非聚簇索引在下面索引分类中有具体介绍。


引出聚簇索引与非聚簇索引(重要)

一般你创建一张表(包含主键id)存储多条数据,MySQL将这些数据通过B+树形式存储到磁盘中,此时是以id来作为索引(因为其被设置成主键),此时将索引id与数据存放在一起的则称为聚簇索引,如下图:

  • 可以看到最下层叶子节点存储着索引id以及数据。

那么非聚簇索引呢?我们对上面创建的表中的name设置为索引字段(BTREE索引),此时数据库会再次创建一次B+树结构的索引,该树结构中只保存对应设置为索引的name以及对应的id,注意了没有数据,此时则为非聚簇索引,如下图:

  • name字段如何作为索引呢?name字段应该是字符类型的,所以会通过英文字母来建立对应的索引如A,B,C,L,N…来构建出索引,在最下层的叶子节点中保存中着对应的name字段,该字段对应主键id。

此时我们又会有一个疑问就是查询name索引字段是如何查询到对应的数据呢?

  • 过程:首先会去非聚簇索引中根据对应的字符(单个字符)索引找到对应的要查找的name字段,于此同时获取到主键id值,接着会有回表操作,也就是拿着id回到聚簇索引中查一遍最终拿到对应的数据。

下面则是根据name索引字段查询大致过程:若是只是根据主键id查询就不会经过非聚簇索引了,直接通过聚簇索引


4.3、索引的分类与创建

4.3.1、聚簇索引与非聚簇索引(InnoDB与MyISAM不同的索引表)

聚簇索引:将数据存储与索引放到了一块(即一棵B+树上),找到索引也就找到了数据。

非聚簇索引:索引与数据不放到一块,在一棵B+树中叶子节点不存放数据的。

两种引擎中识别聚簇索引与费聚簇索引

InnoDB以及MyISAM中的索引表分布:

  • 左图的主键索引就是聚簇索引,并且其辅助键索引就是非聚簇索引;右图的主键索引与辅助索引均不是聚簇索引。
  • 对于InnoDB引擎会将索引与数据存储到一个文件中;MyISAM引擎会索引与数据分开存储到两个文件中。(注意上图InnoDB引擎中的辅助键索引指代的是设置其他索引字段创建的)
  • 具体两类引擎介绍说明
    1. InnDB引擎使用的聚簇索引,将主键组织到一棵B+树中,而行数据就存储在叶子节点上,①若使用如where id=11根据该条件来查询主键,则会按照B+树的检索算法从上至下查找到对应的叶子节点中,之后获得行数据;②如若是使用where name='JOBS'会先从辅助键索引B+树上从上至下找到对应name对应的id值(该过程需要设置name为索引),接着回表到主键索引使用主键id来在主索引B+树上再执行一次B+树检索操作,最终到达叶子节点获取整行数据。
    2. MyISAM引擎使用的是非聚簇索引,其主键索引只存放主键id,辅助键索引只存储辅助键(及其他字段索引),而表数据是单独存储在一个独立的地方,这两颗B+树的叶子节点都使用了一个地址指向真正的表数据,这与InnoDB的B+树存储的内容不同,由于其索引树是独立的,通过辅助检索无需访问主键的索引树,而是直接在最后的叶子节点(存放的是地址)中指向对应的数据表数据。

重要:插入或删除一个数据就要维护聚簇索引,以及非聚簇索引


4.3.2、主键索引

主键索引:它是一种特殊的唯一索引,不允许有空值。一般是在建表的时候指定了主键,就会创建主键索引, CREATE INDEX不能用来创建主键索引,使用 ALTER TABLE来代替。

主键索引的相关操作

1、修改指定表字段为主键(语法):alter table 表名 add primary key( 列名 )

  • 例如:修改student表中的id字段为其添加主键alter table student add primary key(id)

2、在创建表的过程中设置字段为主键

-- ①直接在表字段之后添加primary key即设置id为主键索引
create table score(id int AUTO_INCREMENT primary key  comment "设置id为主键"
);-- ②在最后一行,使用primary key(指定字段)来设置主键
create table score(id int AUTO_INCREMENT comment "设置id为主键",PRIMARY key(id)
);

4.3.3、普通索引(常规索引,normal)

普通索引:这是最基本的索引,它没有任何限制。

普通索引的相关操作

下面创建索引最终效果normal(普通)的BETREE索引。

1、修改一个字段为普通索引(语法):alter table 表名 add index 索引名(指定字段)

  • 例如:修改student表中的name为索引,索引名是stu_name,SQL为alter table student add index stu_name(name)

2、为某个字段创建一个普通索引(语法):create index 索引名 on 表名(指定字段)

  • 例如:为student表中的name创建索引,索引名是stu_name,SQL为create index stu_name add on student(name)

3、创建表过程中添加添加多个普通索引。

create table test01(id int not null,name varchar(20) not null,index test_id(id),  -- 设置id为普通索引,索引名为test_idindex test_name(name)  -- 设置name为普通索引,索引名为test_name
)

4、为某个字段创建指定长度的普通索引(语法):create index 索引名 on 表名(指定字段(长度))

  • 说明:该条语法主要是为某个字段(一般为字符型)设置指定长度的索引,针对于一些长度很长的字段设置一部分为索引。
  • 例如:为student表中的passage创建索引,索引名是pass_name,长度为10,SQL为create table index pass_name add on student(passage(10))

5、删除普通索引(语法):drop index 索引名 on 表名

  • 例如:将student表中索引名为stu_name的删除,SQL为drop index stu_name on student

4.3.4、唯一索引(unique)

唯一索引(unique key):与普通索引类似,不同的就是索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。

唯一索引与主键的一些区别

  1. 主键可以被其他表引用为外键,而其他索引不能。
  2. 一个表最多创建一个主键(复合主键例外),但可以创建多个唯一索引。
  3. 主键更适合那些不容易更改的唯一标识,如自动递增列,身份证号。

唯一索引的相关操作(实际上就是在执行操作时替换unqiue index)

1、在表的指定字段上创建一个唯一索引(语法):create unique index 索引名 on 表名(指定字段)

2、修改指定字段为唯一索引(语法):alter table 表名 add unique index 索引名(指定字段)

其他如删除操作与之前的类似相同。


4.3.5、全文索引(fulltext,不常用)

全文索引:FULLTEXT索引用于全文搜索,在企业中一般不使用全文索引,通过使用ElacticSearch等中间件替代。

全文搜索的版本支持

  1. MySQL5.6以前的版本,只有MyISAM存储引擎支持全文索引。
  2. MySQL5.6之后的版本,InnoDBMyISAM存储引擎均支持全文索引。
  3. 数据类型为charvarchartext的字段才可以建全文索引。

为什么使用全文索引?一般想要查找采用数值比对,范围过滤即可完全大多数操作,如使用like+'%‘或like+’_'或正则表达式即可进行过滤查询,但是若是希望使用关键字来匹配查询,就需要基于相似度的查询,而不是之前的精确数值、文字比较,全文搜索使用于之类场景。

  • 什么场景能够遇到全文搜索,例如表中有一个id、content字段,content表示的是文章,此时你想要搜索一部分关键词,就需要手动设置全文索引 。
  • 日常生活中肯定使用过许多搜索引擎,许多搜索引擎的索引对象是超大量的数据,并且通常其背后都不是使用的关系型数据库,而是非关系型数据库,如redisMongoDB

注意点

  1. 使用全文索引前,搞清楚版本支持情况。
  2. 全文索引比like+%快N被,但是可能存在精度问题。
  3. 如果需要全文索引的是大量数据,建议先添加数据,再创建索引。
  4. 对于中文,可以使用MySQL5.7.6之后的版本或者第三方插件。

全文索引的相关操作

1、创建表过程中创建全文索引

create table test01(id int not null,content text not null,FULLTEXT key test01_content(content)  -- 创建content字段为全文索引
)

2、在已存在表中创建索引(语法):create fulltext index 索引名 on 表名(指定字段)

3、删除指定索引(语法):drop index 索引名 on 表名

4、修改表字段为其添加全文索引(语法):alter table 索引名 add fulltext index 表名(指定索引字段)。

5、全文搜索的使用语法(重要)

自然语言搜索:select * from test01 where match(content) against('a')

  • 自己测试了最终木有效果???

其他搜索模式可见:Mysql全文索引的使用


4.3.6、空间索引(spatial,不常用)

MySQL在5.7之后的版本支持空间索引,而且支持OpenGIS几何数据模型。这是在地理位置领域使用的一种索引,其他场景使用的很少了解即可。


4.3.7、复合索引(联合索引,重点!!!)

认识与创建复合索引

何时使用复合索引?当我们有多个查询条件时,建议将对应的多个条件设置为复合索引,复合索引(索引合并)效率是高于单独设置多个字段索引的。

  • 举个例子create index 索引名 on 表名(字段1,字段2,字段3):通过该方式直接添加三个字段为一个索引,其实际上会将3个字段合并来创建一个复合索引(三个字段合起来的),创建复合索引的效率高于单独创建三个索引的。
    在第一个索引有序的情况下,第二个有序,在第二个有序情况下第三个有序

优缺点

  • 优点:效率高,索引列越多,通过索引筛选出的数据越少,比单值索引效率高
  • 缺点:若是联合索引很多,那么索引列也就越多,创建的索引越多,索引都是存储在磁盘中的,通过索引算法(B+数为代表的形式来做索引的)来查找数据,虽说可以大幅度提升查询效率,但是与此通知若是做增删改操作,则需要花费额外时间去更新索引,并且设置索引所占磁盘大小也特别大

复合索引的操作如下

方式一:创建复合索引的方式(语法):create index 索引名 on 表名(字段1,字段2,...,字段n),下面是创建三个字段为一个索引的例子

  • 排序规则:对于其中合并之后的三个字段,实际上根据其对应初始设置来进行排序的,首先按照字段1排序,若是字段1排序相同情况,字段2会进行排序,若是出现字段1字段2排序相同情况,字段3也会进行排序。

方式二:修改指定表中的几个字段为复合索引(语法):alter table 表名 add index 索引名(字段1,字段2,...,字段n),创键效果如下。

方式三:删除指定索引(语法):drop index 索引名 on 表(字段1,字段2,...,字段3)


实例介绍复合索引的最左匹配原则(重要)

下面就是最左匹配原则的最好例子

首先有10条记录如下:

我们将四个字段设置复合索引:create index 索引名 on 表名(A,B,C,D),将A、B、C、D四个字段设置为一个索引(复合索引),excel模拟其中的索引操作如下:

  • 实际上的排序操作与数据库在创建复合索引几乎一致。首先会对A进行索引排序,若是出现A排序相同的情况,就会对B进行排序;若出现A排序、B排序相同情况时,就会对C进行排序;若是再出现A、B、C排序都相同情况时,就会对D进行排序!

复合索引创建好如下(根据排序根据):放置在前面A、B、C、D是对应的索引

我们可以注意到A列是按照升序排序的,之后的B、C、D列从整体上来看并不是按照升序排序的,接下来根据各个情况来进行描述。

①A字段相同情况的记录来说B字段是按照升序进行排序的,如下:

②A字段、B字段都相同情况下,C字段也是按照顺序排序的

③A字段、B字段、C字段都相同情况,D也是按照顺序排序的

上面主要想要说明的点是:多条件搜素时,尽量搜索的条件顺序与设置复合索引(多个字段)的相一致,否则无法索引将会无法生效。

为什么一定要按照设置索引的顺序来进行检索呢?由于复合索引的规则,只有当A出现值相同情况下,B字段才会进行排序,如下两行数据A字段15、20时,B对应数据并不是进行排序的,若是查询如where B=33 and A=15,那么索引就不会生效,从而会对B一行一行从到往下去找。

相反若是查询如where A=15 and B = 33呢,根据复合索引的规则,从左到右若是有相同的字段,后面一个字段就会排序,其效率就会特别高:

总结:若是设置复合索引,如index 索引名 表名(A,B,C,D),尽量条件搜索方式如①where A=1。②where A=1 and B=2。③where A=1 and B=2 and C=3。④where A=1 and B=2 and C=3 and D = 4。这种根据复合索引设置顺序去找往往是使用到了索引。


最左匹配原则及优化器

最左前缀匹配原则:对于定值查询一直都会是根据索引查询,但是一旦遇到范围查询,后边的索引会失效。MySQL会一直向右匹配知道遇到范围查询(>,<,between,like)就停止匹配,如建议(a,b,c,d)顺序索引,对于where a=1 and b=2 and c>3 and d=4,当遇到c时后面的索引就会失效了,如果建立(a,b,d,c)则一定能够用上索引,因为对于a,b,d始终是定值查询,c是范围查询。

  • MySQL优化器:MySQL底层有个优化器,对于查询条件会根据你设置的索引进行自动优化,若是你设置索引为(a,b,c),你自己写的SQL是where a=1 and c>5 and b=4,那么通过优化器会根据你设置的(a,b,c)索引顺序优化成where a=1 and b=4 and c>5 ,这样的话就会命中索引,提升效率。
  • 注意注意:优化器只会根据你设置的索引顺序进行优化,切记切记!=in可以乱序,对于范围查找(如>,<)需要放在最后,MySQL优化器对于等值与in进行顺序优化,对于范围查找不会优化。

实际案例及分析

实际案例:你要进行多条件查询,包含如下字段,product_name(产品名称)指定值,product_price(产品价格)区间范围,product_type(产品类型)指定值,现在要你设计复合索引,如何设计?

分析:一定要将指定值设置在前,区间范围设置在后,无论你SQL语句写的顺序如何,MySQL优化器会根据你设置的索引字段来对SQL语句进行优化,将一些关键索引字段移到前面,所以设计方案为:(product_name,product_type,product_price)两个定值在前,范围区间在后。


4.3.8、哈希索引(Hash,不常用也重要)

认识与创建哈希索引

MEMORY存储引擎支持hash索引。

一般很少使用哈希索引,但在一些场景中会使用。

哈希索引:基于哈希表实现,对于每一行数据,存储引擎都会对其设置的索引列计算一个哈希码,对于哈希索引,是将哈希码来作为索引进行存储的,同时保持着每个数据行的指针。

特点:天然优点无序、快,瞬间定位,时间复杂度O(1),查询任何东西只要一次。

缺点

  1. 哈希索引数据并不是按照索引顺序存储的,所以无法使用排序
  2. 哈希索引不支持部分索引列查找,因为哈希索引始终是用索引列的全部内容来计算哈希码,也就是说不能进行模糊查询
  3. 哈希冲突(不同索引列会用相同的哈希码)会影响查询速度,此时需遍历索引中的行指针,逐行进行比较。

创建哈希索引方式

语法:create index 索引名 using hash on 表(指定字段)

  • 记得表引擎使用Memary

使用哈希来解决BTREE索引较大的字段

问题描述+分析

问题描述:若是想要给一些超长的字段设置索引,可能会造成索引效果比较差,又长并且还是字符,如何在不使用哈希索引情况下解决这个问题呢?

分析:我们可以增加一列哈希列,将超长字段映射为哈希值(数字),接着对该哈希值使用索引即可提升效率!

方案如下:crc32()来获取指定字符哈希码,并将该哈希码字段设置为索引

过程如下

  • 设置url对应的哈希值为索引字段。

插入语句如下:其中的crc32()函数是将指定字符串转为指定的数字哈希码

-- 插入一条记录(url值以及对应的哈希值)
INSERT into url(url,urlhash) values('https://blog.csdn.net/cl939974883/article/details/115497511?spm=1001.2014.3001.5501',
crc32('https://blog.csdn.net/cl939974883/article/details/115497511?spm=1001.2014.3001.5501')) -- crc32()是一种哈希算法转为数字SELECT * from url

注意:若是要使用该哈希值来查询应该将url带上一同查询!!!

查询语句如下:为了防止出现哈希碰撞的现象所以使用两个条件

-- urlhash是哈希码(数字),其作为索引查询效果会很好
SELECT * from url WHERE urlhash = 1402908114 and url = 'https://blog.csdn.net/cl939974883/article/details/115497511?spm=1001.2014.3001.5501'


4.4、使用索引的问题

4.4.1、索引究竟何时使用?

引言:索引十分重要,当我们的查询SQL开始变慢时,就应该考虑去建立索引,根据SQL的查询条件来定建立指定索引,可以是主键索引、普通索引、复合索引…,建立索引能够大大提升我们的查询速度,但是建立索引会占用额外的磁盘空间,并且对于插入、删除操作不仅要对原有数据进行修改,还要去维护对应的B+树索引。

对于使用没有建立索引的字段作为查询条件时会进行全表扫描,若是单表数据量特别大且查询条件结果较少的话,其查询性能会特别差,此时就应该建立索引。但也不是所有表你都需要建立索引,根据情况来建,因为建立索引也会带有坏的影响,例如影响更新速度,若是建立了索引查询速度并没有多大效果,却影响了原本的更新速度,那么就称为过度索引

下面列出一些场景来作为你是否要建立索引的参考示例

(1)、适合建立索引场景

  1. 首先并不是建表时就直接建立索引,而是当你的查询SQL出现性能问题时才要去考虑建立索引。
  2. 频繁作为where条件语句查询的字段。例如where 字段1=2,字段1设为普通索引。where A=1 and C>3 and B=2,设置复合索引(A,B,C)
  3. 与其他表关联字段可建立索引,如表中的外键。例如user表中的stu_id作为外键引用student表的id。(stu_id设置索引)
  4. 排序字段可建立索引。例如order by 字段1。(字段1设索引)
  5. 分组字段可建立索引,因为分组的前提是排序。例如group by 字段1。(字段1设索引)
  6. 统计字段。例如:count(name)。(name设索引)

(2)、不适合建立索引场景

  1. 需要频繁更新的字段不适合建立索引。
  2. where条件中用不到的字段不适合建立索引。
  3. 表数据确定比较少(几百、千、万条)不需要建立索引。
  4. 对于数据重复且发布比较均匀的字段不适合建立索引,唯一性太差的字段不适合建立索引。例如:性别(男、女),真假(true,false),像这些字段在不同记录中重复情况过多也不建议建立索引。
  5. 参与列计算的字段不适合建立索引,索引会失效。例如where age+1=28,age索引字段就会失效,规避失效方式改为where age = 27
    • 若是在字段上进行计算,那么在索引树上也会进行计算那么不就破坏了索引树嘛,所以列一计算MySQL就会默认失效了。

tips

①对于日志表之类不是给用户的就不需要建立索引,因为其既占用磁盘资源还会影响插入操作,自己写的忍下即可。

②一般在企业中是不会删数据的(数据很珍贵),一般设置一个字段值del_flag作为删除标记,0表示没有删除,1表示删除。


4.4.2、索引不会包含有NULL值的列

为什么索引列无法存储null值

  • 由于索引是有序的,null值进入索引时,无法确定其应该放在哪里;还有就是就算你放在索引中,使用其null值作为条件搜索,null值是不确定的是无法找到对应索引树的叶子节点位置。
  • 这也是为什么主键默认是非空字段的主要原因。

使用null作为查询条件查找的操作是什么

  • 若是查询null,就会在聚簇索引的B+树的叶子节点层进行全表查找。

案例:此时有个需求,要对某个null字段进行索引,如何设计?

  • 解决过程:由于单独设置为null的字段为索引,其会自动失效,解决方法就是将该null字段与另一个非null字段设置成一个复合索引即可!

说明单列索引无法存储null值、复合索引无法存储全为null的值。


4.4.3、短索引使用

短索引:对串列进行索引,一般就是指定一个前缀长度。使用短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。

短索引创建方式create index 索引名 on 表名(指定字段(长度))

使用场景

场景1:例如针对于url字段,目的是区分是百度与新浪

https://www.baidu.com/s?wd=%E9%98%BF%E6%96%AF%E8%92%82%E8%8A%AC%E4%BB%80%E4%B9%88
http://blog.sina.com.cn/s/blog_9ba9f6570102z7p7.html?tj=1

只要设置url(20)为索引即可,将如下字段设置为索引即可

https://www.baidu.com/
http://blog.sina.com.cn/

场景2:例如针对于url字段,目的是区别百度图片还是问题查询

https://www.baidu.com/s?ie=utf-8&wd=xinlang
https://image.baidu.com/search/detailct=503316480&z=0&ipn=d&word=xinlang&step_word=&hs=0&pn=0&spn=0&di=145750&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&istype=0&ie=u

如何设置短索引也就很明显了,索引位置为url(17),到…baidu即可。

总结:视情况而定来建立短索引,对于一个字段中区分度高的部分来设置为短索引较好,短索引在进行索引时比较会更快。


4.4.4、排序(order by)的索引问题

前提说明:MySQL查询只使用一个索引!

whereorder by合用情况:因此如果在where子句中已经使用了索引的话,那么之后若是有order by 字段1,那么该字段1列是不会使用索引的。

注意点:尽量不要包含多个列的排序,如若进行多个列排序,那么最好对多个列进行复合索引,这样建立索引效率会更高。


4.4.5、MySQL索引失效的几种情况

下面介绍了5种索引失效的情况

①条件中有or那么就会造成索引的失效,除非or条件中的每个列都加上索引才会生效!

  • 例如where id = 1 or A = 1(条件:id为主键,A不是索引):该条查询语句id会失效原因是有or,要想让其生效需要将or条件中的每个字段设置为索引,在该题中将A设置为索引之后,即可生效。

MySQL优化器对SQL多个字段顺序进行优化后,若是不符合原本复合索引的最左匹配原则,那么复合索引就会不生效。

like查询以%开头会造成索引失效。

  • 例如 A like '%ab'(条件A为索引),该%在前的话A索引失效,如何让其生效,将%放置在后,如A like 'ab%',只要不放在开头即可生效。

MySQL优化器有时候会使用全表扫描来代替索引(索引失效),针对于这种情况是好事,能够提高效率。

若是全表扫描比索引快,则不使用索引。举个例子,例如表中数据有300万条数据,你想要查100万条数据,此时就算你使用索引去查询,MySQL优化器还是会将其优化为全表扫描,因为它认为使用全表扫描更快。

  • 为什么呢?一般的话你想要查表中30%数据,优化器都会去使用全表扫描,因为其效率相对于使用索引就会更高,你想一想若是使用索引查的话每查询一条数据就需要进行回表操作,该操作也是需要耗费时间的,而且你查的数量这么多都要占全表数据的30%了,大量的回表操作消耗的时间也不少,所以优化器会进行优化让其不走索引,走全表查询反而效率更快。

⑤参与列计算的字段索引也会失效!

  • 尽量让右边值进行运算。举个例子:如where A2=15,改为where A = 根号15,这样才能让A索引生效。

explain关键字

介绍:使用explain可以模拟优化器执行sql查询语句,查询出来的是对应的分析字段描述,根据对应的字段查看SQL语句的执行计划。查看SQL语句是否使用到了索引,有没有做全表扫描。

语法Explain SQL语句。在explain关键字之后添加sql语句即可。

应用场景:主要用于分析查询语句或表结构的性能瓶颈。

一般的执行结果都会有如下字段,接下来我们去了解一下每个字段的含义进而去分析你写的SQL语句:


1、id字段(三种情况)

①id相同情况时,即可表示分为一组。执行顺序:从上至下。

  • EXPLAIN SELECT * from   -- 笛卡尔积查询(加条件)
    emp e,dept d where e.deptno = d.deptno  -- 多表查询,筛选字段
    

②id不相同时,id的值越大说明越先被执行。执行顺序:id越大先执行。

  • EXPLAIN -- 子查询
    SELECT * from cart_order where id in (   -- 根据车位id来查询所有的订单SELECT id from carport  -- 获取所有的车位id
    )
    

③id相同且不同时。执行顺序:先执行大的一组,组中按照从上到下顺序

  • EXPLAIN -- 左、右连接查询+联合
    SELECT * from carport  c
    left join cart_order  o on c.id = o.cartport_id  -- 左查询
    UNION  -- 联合起来,相当于进行了全连接
    SELECT * from carport as c
    right join cart_order o on c.id = o.cartport_id  -- 右查询
    
  • 说明: 先执行大的一组即第2组,一组的id都相同就按照从上到下熟悉执行,所以整体执行顺序如上图序号!

总结:id不同情况会发生在子查询union;id相同的情况发生在进行左联、右联、多表查。


2、select_type

(1) SIMPLE:简单SELECT查询语句,允许普通的左联右联,不使用UNION或子查询等。

(2)primary(PRIMARY):最外层的select查询。

(3)subquery(SUBQUERY):子查询中的第一个select查询,不依赖与外部查询的结果集。

  • 例:EXPLAIN SELECT * from user where id = (SELECT id FROM user u where id = 1)

(4)derived(DERIVED):用于from子句中有子查询的情况,mysql会递归执行这些子查询,此结果集放在临时表中。

(5)union(UNION):union中的第二个或随后的select查询,不依赖于外部查询的结果集。

(6)union result(UNION RESULT):union表获取结果的select。


3、type字段(重要)

11种访问类型说明

type:查询所使用的访问类型,代表着你的SQL查询语句的性能优劣。

类型最好到最差(所有):NULL>system>const>eq_ref>ref>fulltext>ref_or_null>index_merge>unique_subquery>index_subquery>range>index>ALL
一般了解知晓10种即可:
NULL>system>const>eq_ref>ref>ref_or_null>index_merge>range>index>ALL

下面对10中常见的type字段进行说明描述:

①null(NULL):MySQL在优化阶段分解查询语句,在执行阶段用不着再访问表或索引。

  • 例:EXPLAIN SELECT * from user where id = 1000(id为索引,根据索引找不到值)
  • 原理描述:不需要去数据库查,直接通过索引就能够得知是否有该字段。简而言之就是通过索引字段查询无任何结果。
  • 简而言之:通过索引查其表查不到任何数据。

②system:表只有一行记录(等于系统表),这是const类型的特列,平时不太会出现, 可以忽略。

  • -- 例子:
    -- SELECT * from user where id = 1查询到一行,接着再使用select
    EXPLAIN SELECT * from (SELECT * from user where id = 1) a;
    
  • 问题描述:在MySQL5.7.32中不会出现该system字段,可能是进行了优化,下面是官网对于system的描述:

③const:通过索引一次就找到了,用于比较主键或唯一索引,由于只匹配一行数据,速度很快。若是主键在where列表中,MySQL就能将该查询转换为一个常量。

  • 例:explain SELECT * from user where id = 1(id为user的主键索引)。
  • 约束条件:const必须使用唯一索引或者主键id,若是其他索引可能会找到多个就不是const了。
  • 简而言之:通过唯一索引或主键查找到一条记录。

④eq_ref:唯一性索引扫描,对于指定索引键,索引表中只有一条记录与之匹配,常见于主键或唯一索引扫描。

  • 例:explain SELECT * from user where id in (SELECT id from user u1);(id为user表主键)

    • 过程描述:首先执行SELECT * from user进行全表查,接着通过使用主键id在索引表中查询唯一索引。
  • 评价:性能也很高,根据另一张表的主键id去查询另一张表。
  • 简而言之:在指定字段索引表中匹配指定索引行数据。

⑤ref:非唯一索引扫描,返回匹配某个单独非唯一索引值的非索引行,本质上也是一种索引访问,返回所有匹配某个单独值的行,可以去找多个符合条件的行,应该属于查找和扫描的混合体。

  • 例:explain SELECT * from user where name in (SELECT name from user)(name为普通索引,非唯一索引)

  • 例2:explain SELECT * from user where role_id is null(role_id为普通索引)
  • 约束条件:条件是非唯一字段与主键
  • 简而言之:使用某一个非唯一索引字段在非唯一索引表中去查找。

⑥ref_or_null:类似ref(非唯一索引),但是可以搜索值为null的行。

  • 例如:EXPLAIN SELECT * from user where name = '天天' or name is null,name为user表中的索引。
  • 问题描述:在MySQL5.7.32中我依旧得不到ref_or_null的结果,可能也进行了优化。

⑦index_merge:表示使用了索引合并的优化方法。

  • 例如:select * from user id = 1 or name = '天天'(id为主键、name为普通索引)
  • 问题描述:按情况应该是进行索引合并了,不过结果竟然是ALL,这两个字段都是单列索引。这里插一嘴,对于常使用的多个字段查询建议使用复合索引。

⑧index_subquery:索引子查询。

  • 例如:explain SELECT * from user where name in (SELECT name from user) or name = '天天'(name为普通索引,对于索引子查询,需要先在索引中查询到指定索引并且筛选条件)

  • 简而言之:对指定索引在索引表中获取并且筛选字段。

⑨range:索引范围找,使用一个索引来选择行,key列显示使用了哪个索引,一般就是在where语句中出现between、<>、in等的查询,针对索引字段。

  • 例:explain SELECT * from user where id BETWEEN 1 and 5(id为主键)

  • 简而言之:根据索引字段进行范围查询。

⑩index:全索引扫描,只遍历索引树,速度比ALL快。index是在索引中读取的,而all是从硬盘读的。

  • 例:EXPLAIN SELECT id from user(id为user表中的主键)

  • 简而言之:就是在索引树查询指定索引。

11、All:全表扫描,遍历全表找到所有行数据。

  • 例1:EXPLAIN SELECT * from user(直接查询所有表)

  • 例2:EXPLAIN SELECT * from user where name = '天天'(name非索引,普通字段)
  • 简而言之:就是对聚簇索引的叶子节点进行全表遍历查询,效率最低。

小总结

常见十种:NULL>system>const>eq_ref>ref>ref_or_null>index_merge>range>index>ALL

我们需要掌握了解下面几种类型

  • const:唯一索引的定制查。
  • ref:普通索引(非唯一索引)的定制查询。
  • range:索引范围查(任何索引)。
  • index:全索引查询(索引全表)。
  • ALL:全表遍历(聚簇索引叶子节点遍历)。

全表扫描注意事项

  1. 对于负向比较,如使用!=会引发全表扫描。
  2. 如果你查询结果需要空值记录,不要使用!=查询(不会将空值行包含进来),应该使用or条件,如or name is null
    • 使用!=null(错误):SELECT * from user where role_id != null

    • 使用is nul(正确):SELECT * from user where role_id is null
  3. or很有可能导致全表扫描,此时可以优化union查询。
  4. 建表时尽量加上默认值(default),避免空值情况。

4、table

table:用来表示输出行所引用的表名


5、partitions(分区)

partitions:分区,创建插入表的时候可以创建分区,例如时间分区(2020,2021),通过该字段能够告诉你数据是从哪个分区来的。

  • 分区能够适当增加查询的效率。MySQL中使用不多,Oracle对于优化使用很多。

6、possible_keys

possible_keys:在这个SQL中,mysql可以使用这个索引去辅助查找记录,当查询涉及到的字段,都会被列出,但不一定被查询使用,只能表示可能会使用到的索引。


7、key(重要)

key:实际上使用的索引,如果为null表示没有使用索引。


8、key_len

key_len:表示索引字段最大的可能长度(使用的字节数),根据该列计算查询中使用的索引长度,并非实际使用长度。在不损失精度的情况下,长度越短越好。该字段通过定义计算而得,不是通过表内检索出来的。

注意:key_len只指示了where中用于条件过滤时被选中的索引列,是不包含order bygroup by这一部分被选中的索引列。

key len的长度还和字符集有关,latin1一个字符占用1个字节,gbk一个字符占用2个字节,utf8一个字符占用3个字节。


9、ref

ref:用来显示使用哪个列或常数与key一起从表中选择相应的行。通常显示的是一个列的名字或者const(表示常量),一般为null。


10、rows(重要)

rows:mysql解析器认为执行此SQL时必须扫描的行数。该数值是一个预估值,不是具体值,通常比实际值小。


11、filtered

filtered:通常使用百分比显示,sql返回结果行数/所读到的行数(rows)的结果。

注意点:对于使用join时,前一个表的结果集大小直接影响了循环的行数。


12、Extra(重要)

Extra:表示的是对该sql语句的描述信息

  • using index:使用了索引
  • using filesort:使用的外部索引排序,不是按照表内的索引顺序读取
  • using temporary:使用了临时中间表保存中间结果,对结果排序使用临时表(如order by,group by)
  • using where:使用了where语句
  • using join buffer:使用了连接缓存
  • impossible where:where子句的值总是false,不能用来获取任何元祖。(不可能的where语句)
  • distinct:一旦mysql找到了与行相联合匹配的行,就不再搜索了。
  • select tables optimzed away:select 操作已经最优情况(MySQL根本没有遍历表或索引就返回数据了),表示查询的速度特别快。

总结

对几个比较重要的字段进行总结说明。

  1. id:表示表达读取顺序。

  2. select_type:查询的类型,主要用于区别普通查询、联合查询、子查询等复杂的查询。

  3. type(重要):查询所使用的访问类型,一共有10种,代表着你的性能优劣,一般有ref已经很好了,range也不错。通过该类别来看出是否是全表查,还是通过索引(唯一索引或普通索引)…,进行优化!!!

    • const:唯一索引的定制查。

    • ref:普通索引的定制查。

    • index-merge:索引合并,索引覆盖。

    • range:索引范围查(任何索引)。

    • index:全索引查询(索引全表)。

    • ALL:全表遍历(聚簇索引叶子节点遍历)。

  4. 重点主要看typekeyrows即可,就能够进行优化索引。还有对应的extra字段。

  5. 一般mysql查询不写select * from xx;一定会是全表遍历,一般都是写上对应的字段信息,因为若是单个字段或多个字段可能会走索引从而不进行全表扫描。

    • 举个例子:你实际只想要获取name以及age,但是你使用select * from xx;一定是全表遍历,若是使用select name,age from xx在不设置索引时可能依旧是全表遍历ALL,但是若是建立复合索引那么就是index。切记使用*一定会走全表遍历。
  6. 一般情况下你能够达到range,甚至说ref、const已经很不错了,若是实现不到range,实现index也可以。一定要规避ALL。


五、MySQL事务

5.1、事务介绍

认识事务

事务:一般使用事务来管理insert、update、delete操作,对于select也可以进行管理,不过一般针对于增删改,在MySQL中默认会开启事务,在执行每一条语句过程中都使用了事务。

目的:就是保证一个业务中的sql语句要么同时成功,一旦出现失败进行回滚。在实际的业务需求中,可能一个业务会操作多张表那么就需要使用到事务,若是事务中出现了异常等情况该业务就会进行回滚操作。

在MySQL中inndb引擎支持事务,而myisammemary等引擎不支持事务。

MySQL中的事务

在MySQL中默认是开启事务的(自动提交),也就是说每次执行一条SQL语句都会进行提交操作,也可以看作每一条SQL语句都是一个事务。

查看是否开启自动提交show variables like 'autocommit';,默认是开启的

关闭MySQL的自动提交:单个会话生效set autocommit = 0或设置全局范围生效set @@global.autocommit = 0

说明:一般情况下我们不会对该自动提交变量进行修改为不生效即为0,而是通过使用命令如beginstart transcation来开启事务,以及使用commit;提交或rollback回滚。只要你开启事务了,若是连接中断或者出现断电都能够进行回滚操作!

-- 例如模板
begin;   -- 开启事务,下面两句为业务执行语句
insert语句;
update语句;
rollback;  -- 回滚或者commit提交

扩展:在数据库中实际上可以设置回滚点,一旦出现异常可以回滚到执行回滚点。savepoint 保存点rollback to 保存点


5.2、事务的四大特征

一般来说事务满足四个特征,即为ACID,仅仅是针对于事务的概念

  • 原子性(Atomicity):一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个缓解。若是事务在执行过程中发生错误,就会进行回滚(rollback)到事务开启前的状态;若是事务过程中都正常执行,最终执行提交操作。
  • 一致性(Consistency):在事务开启前后,数据库的完整性没有破坏,这表示写入的资料全部符合预设规则,这包含资料的精确度、串联性以及后续数据库可以自发地完成预定的工作。举例:A向B转了100,转成功之后A一定是-100,B+100。
  • 隔离性(Isolation):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以阻止多个事务由于并发执行、交叉执行而导致数据的不一致。一般数据库都会提供几个隔离级别之后会进行介绍。举例:A在对一张银行卡取钱,该过程B不能向该银行卡打钱。
  • 持久性(Durability):事务处理结束之后,对数据的修改就是永久的,即使系统故障也不会丢失。

5.3、事务并发问题描述(4个)

何时发生事务并发问题?当多个事务同时操作同一个数据库的相同数据时。

并发问题描述4种

  1. 脏读(Dirty read):当一个事务正在访问数据并且对数据进行修改,该修改操作还没有提交到数据库时,这时另外一个事务也访问了这个数据,接着使用该数据。由于该数据是另外一个事务未最终提交的数据,则为"脏数据",对于该脏数据进行的操作可能是不正确的。
  2. 丢失修改(Lost to modify):指一个事务读取一个数据时,另外一个事务也访问了该数据,那么第一个事务修改该数据后,第二个事务也修改了该数据,接着都依次进行提交,那么其中的一个事务做的操作就会丢失,也就称为丢失修改。例如:事务1读取某表数据为A=20,事务2也读取A=20,接着事务1进行A=A-1操作,事务2页修改A=A-1操作,最终结果为A=19,事务1的修改被丢失。
  3. 不可重复读(Unrepeatableread):指在一个事务内多次读同一个数据,在事务1还没有结束时,事务2也访问该数据。那么在事务1两次读数据之间,由于事务2的修改导致事务1两次读取的数据可能不一样,发生了在一个事务内两次读到的数据不一致,即为不可重复度。
  4. 幻读(Phantom read):幻读与不可重复读类似。其在事务1中读取了几行数据,接着另一个并发事务2插入了一些数据。接着事务1进行查询过程中发现了原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。

不可重复读与幻读的区别

  • 不可重复读关键在于修改,前后读取发现数据进行了更改;幻读的关键在于新增或删除,如第二次读取数据时发现记录增多或减少。

5.4、事务的特性—隔离性

事务特性:事务与事务进行隔离

  • 在有些情况同时有多个客户端连接服务器并对同一张表进行操作,此时就需要进行事务隔离。一个事务的操作不会影响另一个事务。

5.4.1、查看与设置隔离级别

查看当前会话的隔离级别:select @@tx_isolation;。MySQL默认是隔离第三级别可重复读。

查看系统的隔离级别:select @@global.tx_isolation;

修改隔离级别

-- 1.设置会话的隔离级别,隔离级别由低到高设置依次为:
set session transaction isolation level read uncommitted;
set session transaction isolation level read committed;
set session transaction isolation level repeatable read
set session transaction isolation level serializable;-- 2、设置当前系统的隔离级别,隔离级别由低到高设置依次为:
SET GLOBAL TRANSACTION ISOLATION LEVEL read uncommitted;
SET GLOBAL TRANSACTION ISOLATION LEVEL read committed;
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ
SET GLOBAL TRANSACTION ISOLATION LEVEL serializable;

一般常使用会话隔离级别来进行测试,注意设置全局语句时前面需要大写。

注意注意:设置会话隔离级别后,查看全局隔离级别会不一致,需要查看会话隔离级别。一般在一个会话连接中设置会话隔离级别才会生效!!!


5.4.2、4种隔离级别

四种隔离级别说明

SQL 标准定义了四种隔离级别

  • READ-UNCOMMITTED(读取未提交):最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、不可重复读或幻读。
  • READ-COMMITTED(读取已提交):允许读取并发事务已经提交的数据。解决了脏读问题,但是不可重复读或幻读仍有可能发生。
  • REPEATABLE-READ(可重复读):对同一字段的多次读取结果是一致的,除非数据本身是被自己的事务修改。解决脏读、不可重读问题,但是幻读仍有可能发生。
  • SERIALIZABLE(可串行化):最高的隔离级别,完全服从与ACID的隔离级别,所以的事务依次执行,这样事务之间就完全不可能产生干扰。解决脏读、不可重复度以及幻读

可串行化实例演示:对于可串行化的隔离级别,其对于事务是依次执行的,若是同一时间多个事务同时执行,其中的增删改查都有可能会进入阻塞状态知道另外一个事务执行了提交操作结束。

在MySQL中默认使用的是第三种隔离级别:REPEATABLE-READ(可重复读);在Oracle中使用第二种隔离级别:READ-COMMITTED(读取已提交)。

扩展说明(来源于资料)

与SQL隔离级别标准的不同,InnoDB存储引擎在REPEATABLE-READ(可重复读)事务隔离级别下使用的是Next-Key Lock锁算法,因此该级别已经可以避免幻读的产生(达到了SQL标准的SERIALIZABLE(可串行化)),这与其他数据库系统是不同的。

为什么大多数数据库使用第二隔离级别READ-COMMITTED(读取已提交)?

  • 因为隔离级别越低,事务请求的锁越少,主要是提升性能。对于InnoDB存储引擎默认使用的是可重读,其并不会有任何性能损失。

注意:InnoDB存储引擎在分布式事务情况下会使用到SERIALIZABLE(可串行化)隔离级别。


5.4.3、示例演示隔离级别解决的问题

解决不可重复读

问题描述:在并发下可能会出现在一个事务中多次读取到不相同内容的指定记录。

MySQL中的RR(可重复读)就解决了脏读、不可重复读,并且也解决了幻读(因为间隙锁的存在)。

示例如下

上面的案例中包含了MVCC实际演示、第三级别避免的不可重复读。


六、MySQL的锁机制

介绍锁的概念

锁的介绍概念

引出锁:数据库是一个多用户使用的共享资源。当多个用户并发的存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若是对该类情况不加以控制可能就会读取与存储不正确的数据,破快数据的一致性。

如何实现对数据库的并发控制呢?通过加锁的方式来实现,当事务对某个对象进行操作前,先向系统发出请求,对其进行加锁,加锁后事务对该数据对象就有了一定的控制,在该事务释放锁之前,其他的事务就不能对次数据对象进行修改操作。

数据库锁机制:数据库为了保证数据的一致性,使各种共享资源在被访问时变得有序而设计的一种规则,例如串行化隔离级别。

MySQL中的锁机制比较简单显著的特点是不同的存储引擎支持不同的锁机制。

  • InnoDB支持行锁(有时会升级为表锁)。
  • MyISAM和MEMORY存储引擎采用的是表级锁。

锁的类型

基本类型(两种):排他锁(Exclusive Locks,即X锁)与共享锁`(Share Locks,即S锁)。数据库利用这两种基本的锁类型来对数据库的事务进行并发控制。

  • 当数据对象被加上排他锁时,其他的事务不能对它进行读取与修改。
  • 当加上共享锁时,数据对象可以被其他事务读取,但是不能修改。

按照锁的粒度划分行锁表锁页锁

  • 表锁:锁住表,特点就是开销小、加锁快,不会出现死锁。锁粒度大,发生锁冲突的概率小,并发度相对低。
  • 行锁:锁住表中的行,特点就是开销大、加锁嘛,会出现死锁情况,锁粒度小,发生锁冲突的概率高,并发度高。
  • 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

6.1、InnoDB行锁

InnoDB默认的事务隔离级别是RR(repeatable read,重复读),当参数为innodb_locks_unsafe_for_binling=0的模式下,行锁有三种。

特点:锁住表中的行,特点就是开销大、加锁嘛,会出现死锁情况,锁粒度小,发生锁冲突的概率高,并发度高。


①记录锁(Record Lock)

记录锁:封锁记录,也叫做行锁。

案例背景

表如下:teacher_no为主键,其他初始状态下无索引

DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher`  (`teacher_no` int(11) NOT NULL,`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`score` int(255) NOT NULL,PRIMARY KEY (`teacher_no`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Records of teacher
-- ----------------------------
INSERT INTO `teacher` VALUES (1, 'liner', 0);
INSERT INTO `teacher` VALUES (2, 'maolaoshi', 0);

(1)、name字段无索引(上表锁)

事务一:开启事务,通过name字段来更新teacher表的一条记录。

事务二:开启事务,在事务一更新好之后进行查询测试允许读,进行更新该表的其他字段进入阻塞状态

说明:右边的事务更改的是左边事务同一张表的其他行记录(进入到阻塞),该案例说明了在事务中若是通过一个非索引字段去更改一个值,那么就会上表锁。

说明:首先执行左边事务的修改操作(通过非索引字段),接着执行右边的修改操作该操作是对右边同一张表的同一条记录进行更改字段值同样也进入阻塞,在上面例子中已经能够得知其上的是表锁。


(2)、name字段带索引(上行锁)

对唯一索引进行等值判断会上行锁!(查询结果始终为一个结果)

例子:where id = 15;,其中id为主键索引(唯一索引)

首先将该表的name字段设置为索引再进行测试。

过程如下

事务一:开启事务,更新teacher表字段name为’liner’的行记录。

事务二:开始事务,更新teacher表字段name为’maolaoshi’成功,更新teacher表字段name为’liner’进入阻塞状态。

说明:由于对name进行设置索引,此时就会多出一个非聚簇索引树,左边先进行对name='liner’修改操作就会上锁,注意这里是对非聚簇索引树上锁,接着右边的事务对同表非同记录更新是可以的,对于同表同一记录则进入阻塞状态,说明上的是行锁。


总结

根据对应字段去更新一张表的指定记录时,有下面两种情况根据是否设置索引来分:

  • 若是该字段不是索引上的是表锁,即整张表被锁住,其他事务根据该表是写锁还是读锁进行相应的业务操作(若是写锁,其他事务对该表啥都不能干一直阻塞;若是读锁对该表其他事务只能读,写会进入阻塞),只有当事务1进行提交或回滚才能够释放锁,其他事务结束阻塞状态执行对应的事务。
  • 若是该字段是索引上的是行锁,本session能够进行读写操作,其他session只能够进行读操作,对于写操作会进入阻塞直至本session提交。

原理分析(自己分析,之后若有误会更正):①若是你不建立指定索引,那么只会有一个聚簇索引树,根据指定非索引字段查询记录一定走的是全表遍历,此时你又开启了事务进行修改操作那么就会对该树进行上锁,即称为表锁,此时其他事务对于该表只能进行读,哪怕是同表其他行也是不能进行改操作的。

②若是你对某个字段建立索引,那么就会生成一个非聚簇索引树(存放索引字段及id),此时你开启一个事务并根据其索引字段进行修改操作,那么就会对该非聚簇索引树进行上锁,你也可以将其看做是行锁,此时你还有个聚簇索引树所以其他事务能够对同表的其他行进行改操作,但是对于前一个事务修改的那行不能进行写操作。


②间隙锁(GAP Lock)

介绍说明

RR(Repeatable Read,第三种隔离级别)这个级别下,为了避免幻读,引入了间隙锁,该锁锁定的是记录范围,不包含记录本身,也就是不允许在范围内插入数据。

间隙锁的作用:保证某个间隙内的数据在锁定情况下不会发生任何变化。比如mysql默认隔离级别下的可重复读(RR)。

间隙范围:根据检索条件向下寻找最靠近检索条件的记录值A作为左区间,向上寻找最靠近检索条件的的记录值B作为右区间,即索引的间隙为(A,B),注意了是开区间;

  • 举例(重要):例如id索引值依次为10,20,30,40,50,接着进行加锁查询判断如select xxx from xx where id<30,看条件id<30,仅有10,20,左范围定为查询结果最小值向上找最临界的记录值,右范围为查询结果最大值向下找最临界记录值为30,所以间隙范围为**(?,30)**。

关于不同索引产生锁的情况说明:使用非索引的字段查询会上表锁

  • 唯一索引:等值判断只会产生记录锁,范围查询会产生间隙锁
  • 普通索引:等值判断也会产生间隙锁。(因为普通索引也会有多条结果重复的情况查询结果可能会有多条,为了避免产生幻读会上间隙锁)

实际案例

前提准备

表名:teacher。主键:teacher_no

score无任何索引,根据后面案例来决定增加。

实例:仅仅是演示下间隙锁产生的效果

(1)、唯一索引等值判断只会产生行锁

条件:设置score为唯一索引。

产生行锁语句:select * from teacher where score = 30 lock in share mode;

(2)、唯一索引进行范围查产生间隙锁

条件:设置score为唯一索引。

范围查产生间隙锁:select * from teacher where score < 30 lock in share mode;,根据上面表该锁范围是(?,30),?表示是小于表score最小值10,30指的是在score<30中表最小为20向下的最近的一个score值。下面给出进入阻塞两种情况(不限定于该两种情况)

(3)、普通索引等值判断产生间隙锁(范围查询也肯定会产生)

条件:设置score为普通索引

下面可以很明显看到对普通索引进行定值查询会产生间隙锁,定值score=30,会根据30向上找到20作为左边临界值,向下找到50作为右边临界值,间隙范围为:(20,50),若是在该范围中进行插入与更新操作都会进入到阻塞状态直到另一个事务提交。


③临键锁(记录锁与间隙锁组合,next-key lock)

临键锁:是记录锁与间隙锁的组合,它的封锁范围既包含索引记录,又包含索引区间。

实际举例:若是根据普通索引去更新某个值,会对该行记录上记录锁,会对这一范围上间隙锁,此时两者的结合为临键锁。

条件:score为普通索引。

下面就演示了记录锁以及间隙锁存在情况:

说明:实际上在MySQL中使用lock in share mode或增删改操作都会默认使用next-key锁实现,具体是什么锁根据sql语句来看。


总结

1、行锁分为记录锁、间隙锁以及临键锁,临建锁实际上就是记录锁+间隙锁。

2、记录锁:指定值行。根据索引字段来修改某条记录时(结果集为1行),会上行锁,本session能够进行读写操作,其他session对该表能够进行读操作,写操作会进入阻塞。

3、间隙锁:针对于范围结果集。对指定范围进行上锁,其他事务在间隙范围中无法进行增删改操作,避免了幻读。

4、临键锁:就是记录锁+间隙锁组成,MySQL的当前读实际上就是临键锁操作(包括增删改、select .... lock in share modeselect ... for update)。

有很大一部分情况都是临键锁!


6.2、表锁(含读、写锁)

介绍表锁

认识表锁

表锁:对一张表进行上锁,

特点:偏向MyISAM存储引擎,开销小,加锁快;无死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。

场景:在编辑表或执行修改表操作时可以给表加上表锁这样就会很安全,避免不同步的事情发生,在并发情况下会很慢可能就是上表锁的原因。

  • 场景一:有一张表300万数据,你现在要在一个事务中修改150万数据,这种情况下为了更快操作使用表锁,而不是使用行锁(使用行锁的话会有上大量锁的时间,效率不高)。
  • 场景二:对于并发度低,一个事务涉及多表时也可以使用表锁。扩展说明一下MySQL中会监控死锁,使用行锁有可能会造成死锁情况,MySQL对于死锁情况会进行回滚,这样的话就会造成大量事务回滚。

表锁的使用方式

自动上表锁:一个事务提交前,进行修改操作其条件字段是非索引的就会上表锁,在记录锁(1)中有案例。

手动上表锁:lock table 表名 read(write); ,你可以对表设置写锁或者读锁(会自动开启一个事务,我自己认为的)。

  • 解锁方式:UNLOCK TABLESbegin(解锁以及提交事务)。

    • 对于begin为什么能解锁猜测原因:在上表锁的时候实际上已经开了一个事务了,接着若是再使用begin就相当于在一个session中再开启一个事务,在一个会话中若是开启第二个事务就会自动提交前一个事务,提交了事务也就相当于解锁了。

查看加锁的表:show open tables;,其中若是In_use为1的则表示上锁。

注意情况:手动上表锁时,使用commitrollback命令并不会解锁。手动上表锁时会自动开启一个事务,本session中做的修改操作只有在表解锁之后才会提交!!!


实际案例

读锁示例

读锁:本session对表上读锁之后,只允许读不允许写操作(报错);其他session允许对该表读,进行写操作会进入阻塞状态,直至该表进行解锁。

案例介绍:在session1中给teacher表上读锁,使用读锁的本session只能查不能修改会有报错提示;其他session允许对teacher表查,对于修改操作会进入阻塞状态。

  • 上读锁命令:lock tables teacher read;

解锁操作:这是演示begin解锁。


写锁示例

写锁:只允许本session进行读写操作,其他session在该表锁未释放期间会进入阻塞,无法读写。

案例介绍:在session1中给teacher表上写锁,此时其他session无法对该表进行查询操作及修改操作。

  • 对表上写锁命令:lock tables teacher write;

发现情况:其他session对已经上的表锁进行查询时,只要teacher表不释放锁,那么其他session会一直阻塞等待下去,这与之前行锁进入阻塞有所不同,行锁阻塞的话会有个超时时长,一旦超时会结束本次查询并提示你重试。

接着案例进行解锁操作:这里使用unlock tables;解锁。

解锁之后,其他session即可对该表进行查询以及其他操作了。


总结

1、表锁不会产生死锁问题,粒度大,在一次业务中对于单表进行大数据量更新或者并发度低时涉及大量多表操作可以使用表锁。

2、对于表锁可以自动上或者手动上,自动上表锁情况是根据某个无索引字段进行修改操作会上表锁。

3、手动设置表锁需要你设置读锁还是写锁(lock tables 表 write(read)),手动上表锁时会自动开启一个事务并且上锁,上锁之后进行的修改操作只有在解锁之后才会被提交,解锁命令有两个分别是unlock tablesbegin,解锁就相当于提交事务并且对表解锁。

4、读锁与写锁的实际情况。

  • ①session1对A表上读锁时,session1对A表只能进行读操作,不能对A表进行写操作(会报错误);其他session对A表也可以进行读操作,但是不能进行写操作(进入阻塞状态)。
  • ②session1对A表上写锁时,session1对A表能够进行读写操作;其他session对A表不能进行任何读写操作(都会进入阻塞状态)。

6.3、InnoDB的锁类型

在InnoDB的锁类型中主要有读锁(共享锁)、写锁(排他锁)、意向锁和MDL锁。


①读锁

读锁(共享锁,shared lock):简称S锁,一个事务获取了数据行或表的读锁,其他事务能够获得该行或该表的读锁但不能获得写锁,也就是说一个事务在读取一个数据行时,其他事务可以读但是不能够对该行进行增删改的操作。

简而言之:同一时间允许多个事务读,但是只能一个事务写。

  • 注意说明:session1对表上读锁时,session1本身也只能查,修改操作会报错。

读锁在select方式中的应用

  1. 第一种是自动提交模式下(autocommit=1)的select查询语句,不需要加任何锁直接返回查询语句,这就是一致性非锁定读。
  2. 第二种通过select ... lock in share mode 被读取的行记录或行记录范围上加一个读锁,其他事务可以读,但是想申请写锁(即写操作)就会进入阻塞。

其他应用:针对于表锁

  1. lock tables 表名 read:该条语句会自动开启事务并且上读锁。

②写锁

写锁(排他锁或独占锁,exclusive lock):简称X锁,一个事务获取了一个数据行的写锁,其他事务就不能再获取该行的其他锁(读、写锁)。

  • 注意:在MySQL中使用写锁,其他事务依旧能够进行读操作,因为MySQL通过MVCC解决了。

简而言之:同一时间只允许一个事务进行读写,其他事务本不能进行读写操作的,但在MySQL中是可以的因为通过了MVCC来解决。

写锁实际应用

  1. select ... for update:对行记录或行记录范围上的是写锁。
  2. update:普通更新操作。

针对于表写锁应用

  1. lock tables 表名 write:该条语句会自动开启事务并对该表进行上写锁。

③MDL锁

来源:在MySQL 5.5中引入了MDL锁(meta data lock),用于保证表中元数据的信息。

  • meta:元数据,实际就是表结构。这个锁指的就是当你上了这个锁其他事务就不能更改你的表结构。

如何使用MDL锁?在会话A中表开启了查询事务之后,就会自动获得一个MDL锁,会话B就不可以执行任何DDL(对于表的增删改查)语句,例如不能为表中添加字段的操作。

目的:在事务进行中,防止其他事务修改表结构。

实例演示

提交了事务之后,即可成功修改表结构!


④意向锁

在MySQL中的InnoDB引擎中,意向锁表级锁,分为两种类型:

  • 意向共享锁(IS):指的是给一个数据行加共享锁前必须获取该表的意向共享锁。
  • 意向排他锁(IX):指的是一个数据行加排他锁前必须获取该表的意向排他锁。

介绍IXIS是表级锁,不会和行级的XS锁发生冲突。只会和表级的XS发生冲突。

如何添加?在添加行锁之前会先拿到意向锁。

作用说明:当再向一个表添加表级X锁(排他锁,写锁)时。

  • 如果没有意向锁的话,则需要遍历整个表是否有行锁的存在,以免发生冲突。
  • 如果有意向锁,只需要判断该意向锁与即将添加的表级锁是否兼容即可。因为意向锁的存在代表了行级锁的存在或者即将有行级锁的存在。无需遍历整表即可获取结果。

总结

1、读锁(S锁),简而言之就是同一时间多个事务可以同时读,只有一个事务可以写。在MySQL 5.7版本实际测试对表上读锁只允许多个事务同时读,上锁的事务也不能写会报错。

  • 应用:①自动提交模式下的select语句。②select ... lock in share mode。③上表锁,lock tables 表名 read

2、写锁(X锁),简而言之就是同一时间只能上锁的事务进行读写,其他事务不允许读写。在MySQL中使用写锁,其他事务依旧能够进行读操作,因为MySQL通过MVCC解决了。

  • 应用:①select ... for update。②update操作。③lock tables 表名 write(自动开启事务并上写锁)。

3、MDL锁与意向锁都是为了在事务进行过程中,其他事务不能执行DDL(表结构修改)语句,防止出现数据不一致情况。

  • MDL锁:当开启一个查询事务之后即可开启一个MDL锁(表锁),锁的是元数据,即表结构。
  • 意向锁:两种IS、IX,在添加行锁之前添加。其存在作用是代表该表具有行级锁存在或即将有行级锁的存在,当添加表级X锁时仅需要与意向锁比较是否兼容即可!

6.4、其他角度对于锁的分类

乐观锁(不加锁)

乐观锁:始终以一种乐观的心态,总认为数据总是不会产生冲突,所以其是没有从数据库加锁的,一般使用版本字段来实现乐观锁,一旦发现冲突则让用户返回错误的信息,让用户来去决定。

具体实现方式:通过在一张表中加version字段,通过使用version字段值来保证每次事务操作的同一版本。

过程:即在一个事务开始之前先查询对应字段记录的version值,进行更新操作时会去拿version也作为更新条件,若是版本一致进行更新并且version+1,若是版本不一致说明出现了同步情况,则会认为是过期数据不进行执行进行回滚。

优点:通过程序实现,不会存在死锁问题,适用场景也相对乐观,阻止不了除了程序之外的数据库操作,效率也较高。


悲观锁(加锁)

悲观锁:总是很悲观,每次操作数据总觉的别人会对该数据进行操作。依靠数据库提供的锁机制实现,上面举的所有锁都是悲观锁的例子(共享锁、排他锁等),他阻止一切数据库操作。

注意:数据库的增删改查都会加排他锁,而查询不会加任何锁!


6.5、锁等待

介绍锁等待

锁等待:当你给行或者表上锁时,其他事务进行查或写操作(实际按对应锁要求)就会进入阻塞,这个阻塞实际上就是锁等待的过程。

查看锁等待时间select @@innodb_lock_wait_timeout。默认锁等待时间为50秒。

设置(修改)锁等待时间(会话级别):set innodb_lock_wait_timeout = 10;

设置好了之后测试:这里update是写锁,并且检索字段为主键(唯一索引)即上的是行锁且是写锁


出现lock wait timeout …

问题描述:执行指定行记录时在其他事务已经上锁,阻塞等待指定时长(默认50秒)仍未获得锁出现异常错误。

当事务1对指定行或行范围进行上锁时,其他事务若是对上锁进行更新操作会进入阻塞状态,若是指定时间内还在阻塞就会报出错误如下:只展示异常情况

结论:该条更新语句不执行,本次事务并没有回滚以及提交。也就是说仅仅是这条语句失效!


6.6、死锁

介绍死锁以及出现案例

介绍死锁

死锁:指的是两个或两个以上的进程在执行过程中,因为争夺资源而造成的一种互相等待的现象,就是所谓的锁资源请求产生了回路现象,即死循环。

造成死锁的后果:InnoDB引擎会自动检测死锁,一旦出现死锁就会报错误异常,并且将当前事务回滚。回滚操作会造成大量业务白做,所以死锁尽量不要出现。

死锁示例

死锁出现情况描述

假设有两个事务,事务1对A记录进行上锁,事务2对B记录进行上锁(这个过程视为同步进行的),接着事务1想要对B记录进行操作,事务2想要对A记录做操作,此时就会出现死锁问题,即两个事务同时对各自事务锁进行阻塞等待。

结论:出现死锁时会默认一个事务报出异常并且回滚整个事务,另一个事务死锁sql会执行成功并且事务可以继续往下执行。


如何避免死锁

死锁的危害很大,会直接让检测出死锁的事务全部进行回滚操作,所以一定要避免出现死锁问题!!!

1、如果不同的程序会并发处理同一个表或者涉及多行记录,尽量约定使用相同顺序访问表,可以大大减少死锁的发生。

写事务时应该按照一定顺序写(上锁的行记录,即检索条件),按照以下方式就不会出现死锁问题!!!
事务1      事务2A记录  A记录B记录  B记录

2、业务中尽量采用小事务,避免使用大事务,及时提交和回滚事务,可减少死锁产生的概率。

3、若是一个大事务,尽量做到一次锁定所需要的所有资源(也就是上表锁),减少死锁发生的概率。

4、对于非常容易发生死锁的业务,可以尝试使用升级锁的力度,即行锁->表锁来减少死锁的发生。


各类锁总结

结构分

表锁:自动上(非索引字段作为检索条件);手动上(可上读锁、写锁)。

行锁:记录锁、间隙锁、临键锁。

  • 实际上通过临键锁解决了幻读的问题,MySQL中第三隔离级别实际上就解决了幻读的问题。

保证事务进行过程中的表的不会被其他事务进行修改结构。

都是表锁

MDL锁(meta data lock):元数据锁,开始查询事务会自动上锁。

意向锁:获取行锁前,先获取意向锁。

  • 分为IX、IS意向锁,

读写分

读锁(S锁):允许多个事务同时读,但是都不能写。

写锁(x锁):一个事务能读能写。而在MySQL中其他事务可以读,因为有MVCC的存在。

思想分

乐观锁:很乐观,不会上锁。一般通过给表加version字段来保证版本一致,每次更新时会校验版本。

悲观锁:很悲观,总是认为其他事务会操作本事务目标数据。数据库实现锁,上面提到的锁都是悲观锁。

说明:各个锁的细节可去上面对应章节小总结部分找,很详细。


6.7、MVCC

6.7.1、引出与介绍MVCC

引出MVCC

首先理解一下读写锁:

  • 读锁是允许多个事务读,不能写。
  • 写锁是按理说只允许一个事务进行读写,而在MySQL中其他事务是允许读的(读到的是上一个版本记录),因为其实现了MVCC

问题提出:在实际生活中,一个网站读肯定是比写多,并且若是一篇文章在进行更新,几千人此时访问该文章此时都进入阻塞,那么用户体验就会极差,那么如何去解决一个事务在写时,其他事务依旧能够进行读操作呢?

解答:实际上MySQL已经为我们解决了该问题,其通过实现MVCC实现了在写锁情况下,其他事务也能够对该记录进行读操作。只不过读到的是上一个版本的而已。

  • 查询到的记录实际上是undo log中保留的记录,即左边事务开始前的状态。

介绍MVCC

MVCC(Multi-Version Concurrency Control,多版本并发控制):一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。

目的:MVCC在MySQL的InnoDB引擎中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。


6.7.2、认识当前读与快照读

文章:当前读和快照读的区别

Mysql隔离性之Read View、详细分析MySQL事务日志(redo log和undo log)

MVCC多版本并发控制

普通读(也称为快照读,Consistent Read):就是单纯的 SELECT 语句,即不加锁的select语句。

  • 使用前提:隔离级别不是串行级别,串行级别下的快照读会退化成当前读。
  • 执行方式:生成 ReadView,直接利用 MVCC 机制来进行读取,并不会对记录进行加锁
    • ReadView:事务开启时,当前所有事务的一个集合,这个数据结构中存储了当前Read View中最大的ID及最小的ID。
  • 实现方式:undo log + MVCC 。基于的是多版本并发控制(MVCC),其也是行锁的一个变种
    • undo log:是回滚日志,提供回滚操作。
    • MVCC(Multi-Version Concurrency Control,多版本并发控制):一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。
      • 目的:主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。
  • 好处:使用快照读不与上锁的抢占资源,而是读的快照并不是进行上锁,提高了并发性。
  • 注意:快照读可能读到的并不一定是数据的最新版本,可能是之前的历史版本。

当前读:读取的是最新版本,读取时还要保证其他并发事务不能修改当前记录,就会对读取的记录进行上锁。

  • 实现方式:next-key 锁(行记录锁+间隙锁)

SQL语句如:

  • select … lock in share mode:上的是读锁 。
  • select … for update:上的是写锁。
  • update(写锁) 、delete 、insert

6.7.3、MVCC解决的问题

数据库并发场景一般有三种

  1. 读-读:不存在任何问题,也不需要进行并发控制。
  2. 读-写:有线程安全问题,可能会造成事务隔离性问题,可能会遇到脏读、不可重复读、幻读。
  3. 写-写:有线程安全问题,可能会存在丢失修改的问题,例如两个事务同时更新,一类更新会消失。

MVCC是一种用来解决读-写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个修改保存一个版本,版本与事务链接戳关联,当事务1进行了修改操作,事务2对该记录进行读取操作时实际上读取的是事务1开始前的数据库快照。

解决问题:并发读写指定记录时,可以在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发的性能。同时还解决脏读、幻读(应该是间隙锁)、不可重复读隔离问题,但是不能解决更新丢失问题。

MVCC对应的组合:最高提高数据库并发性能,解决读写、写写冲突问题

  • MVCC+悲观锁:MVCC解决读写冲突,悲观锁解决写写冲突。
  • MVCC+乐观锁:MVCC解决读写冲突,乐观锁解决写写冲突。

6.7.4、MVCC实现原理(含undo log)

介绍MVCC依靠的三个隐藏字段(每条记录中)

MVCC实现主要是靠每条记录中的三个隐藏字段、undo日志以及read view实现:下面介绍三个隐藏字段

  • DB_TRX_ID(事务ID):6byte,最近修改(修改/插入)事务ID。
  • DB_ROLL_PTR(回滚指针):7byte,回滚指针,指向这条记录的上一个版本。
  • DB_ROW_ID(隐式主键):6byte,隐含的自增ID(隐藏主键),如果数据表中没有主键,InnoDB会自动以DB_ROW_ID即该字段产生一个聚簇索引。

注意:在每条记录中还有一个删除flag隐藏字段,该字段用于表示该记录被更新或删除,而并不是代表真正删除。

undo log是什么?存储就旧记录(事务中执行的操作)

undo log:旧的数据放置在undo log中,真实的数据放置在表中,每条记录进行更改或删除操作等会不断增加一条undo log日志,通过回滚指针来指向上一个版本记录,产生的是一个数据链,沿着数据链就能够找到所有版本。并且通过undo log就能够实现事务的回滚,来回滚到之前的版本。

undo log分为两种:

  1. insert undo log:事务在insert新记录时产生的undo log,只在事务回滚时需要,并且在事务提交后可以立即丢弃
  2. update undo log:事务在进行updatedelete时产生的undo log。该种log不仅在事务回滚时需要,在快照读时也需要,所以一般不随意删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被purge线程统一清除。

purge线程起到的作用?用来清理标记可安全删除的undo log记录

  • 更新或删除仅仅是设置一下老记录的deleted_bit,并不会直接将过时的记录删除。为了节省磁盘空间,InnoDB专门用purge线程来清理delete_bittrue的记录。purge线程本身也维护一个read view,若是指定记录delete_bittrue,并且该记录的事务ID相对于purge线程的read view可见,那么该记录表示可以安全清除!

MVCC的执行流程

对于初始一条记录在数据库中,设DB_ROW_ID(隐式主键)为1,事务ID与回滚之前都为null。

接下来介绍事务中进行更新操作的执行流程

  1. 修改该行数据时,数据库对该行加锁。(暂未修改)
  2. 将该行数据拷贝到undo log中,先查看是否有旧记录,若是有,那么最新的旧数据(即新拷贝的记录)作为undo log中指定链表的表头,也就是插在该行记录的undo log最前面。(暂未修改)
  3. 进行修改操作在数据库表中,并且修改隐藏字段的中的事务ID进行+1,并且回滚指针指向刚刚拷贝到undo的副本记录。(修改)
  4. 事物提交,释放锁。之后purge线程会将标记可安全删除的记录进行清除。

注意:一旦begin了,就会产生一个事务id


6.7.5、read view(读视图)

读视图:该视图并不一定是最新的数据,可能是undo log中某个版本的数据。

啥是读视图,什么时候来的?若是在事务中使用了select(快照读),就会产生一个读视图或称为快照,那么接下来事务中继续使用该select就会使用这个快照,就永远都不会变了直至事务提交结束。

  • 该快照记录并维护系统当前活跃事务的ID,每个事务开启时都会分配一个ID,这个ID是递增的,最新的事务ID最大。

目的:帮助你在一个事务中读取到的数据是一样的。

  • 也正因为是MVCC以及read view解决了不可重复读的问题。并且解决并发时读-写阻塞的问题!

MySQL中的三种日志

undo log(旧日志)

与MVCC相关,一般是配合MVCC进行读取旧版本的日志(解决读写问题)。

undo log:旧的数据放置在undo log中,真实的数据放置在表中,每条记录进行更改或删除操作等会不断增加一条undo log日志,通过回滚指针来指向上一个版本记录,产生的是一个数据链,沿着数据链就能够找到所有版本。并且通过undo log就能够实现事务的回滚,来回滚到之前的版本。

undo log分为两种:

  1. insert undo log:事务在insert新记录时产生的undo log,只在事务回滚时需要,并且在事务提交后可以立即丢弃
  2. update undo log:事务在进行updatedelete时产生的undo log。该种log不仅在事务回滚时需要,在快照读时也需要,所以一般不随意删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被purge线程统一清除。

purge线程起到的作用?用来清理标记可安全删除的undo log记录

  • 更新或删除仅仅是设置一下老记录的deleted_bit,并不会直接将过时的记录删除。为了节省磁盘空间,InnoDB专门用purge线程来清理delete_bittrue的记录。purge线程本身也维护一个read view,若是指定记录delete_bittrue,并且该记录的事务ID相对于purge线程的read view可见,那么该记录表示可以安全清除!

redo log(物理日志)

redo log主要是存储记录(其中也包含内存、磁盘区域),主要用来做宕机恢复。

redo log(物理日志):叫做重写日志,记录了文件的变更,存储数据被修改的值。

  • 目的:主要做日志恢复用的。

引出redo log

数据是先加载到内存之后进行修改的,完成之后再刷新会磁盘,为了减少磁盘I/O一般会进行攒数据,攒够了来进行刷新到磁盘。但如果此时断了电,数据就会丢失,根据这个问题推出了redo log日志。

redo log日志由于是顺序写,其效率比数据库上操作要快的多,每插入一条数据之后就要插入一条redo log日志。在redo log日志中提供了一些策略,在redo log中也有内存也有磁盘,每次记录redo log日志时也是先记录在内存再往磁盘上刷,这主要是跟你选择什么策略决定?

  • 若是选择1则表示一提交事务就刷新了保证数据一定完成(指的是redo log中完整保存记录值,以后恢复可以从redo log中进行恢复),一旦宕机重启会自动去比对redo log磁盘中的数据是否比真实磁盘中的指定数据大(LSN),若是磁盘的大说明已经是最新值,若小则将redo log磁盘中的数据刷新过去。为了安全调成1,为了性能调成0。

对于策略选择说明:对于日志记录可以设置为0,少一条几条无所谓。对于用户、订单一条都不能丢应当设置为1保证安全。

bin log(逻辑日志)

binlog(逻辑日志):叫做二进制日志,存储了逻辑sql修改语句。


总结

undo log:叫做重做日志,指的是旧的日志链(即事务中进行的修改、删除操作),配合MVCC,解决读写问题,通过read view读视图来获取undo log中的日志。

redo log:叫做物理日志,在该日志中主要存储记录。

binlog:二进制日志,存储逻辑sql修改语句。


数据库优化:索引是很关键内容(避免不了问到)。小公司靠索引优化占7,80。大公司数据量特别大就要分库分表,读写分离。


参考文章

[1]. 修改mysql的全局变量_mysql用户变量和全局变量

[2]. MySQL查询执行过程

[3]. ns单位换算

[4]. 五分钟搞懂什么是B-树(全程图解)

[5]. 推荐阅读1:MySQL中MyISAM与InnoDB底层使用的数据结构 两个引擎底层都是使用B+树来存储数据,InnDB是将索引与数据都存储到一个文件中,而MyISAM将索引与数据分开存储为两个文件

[6]. 推荐阅读2——MySQL存储引擎MyISAM和InnoDB底层索引结构

[7]. 【MySql数据库】索引相关知识总结

[8]. 聚簇索引和非聚簇索引(通俗易懂 言简意赅)

[9]. Mysql全文索引的使用

[10]. mysql: 哈希索引,虽不常用,但威力巨大

[11]. mysql5.7允许空值_数据库允许空值(null),往往是悲剧的开始 介绍空值null案例

[12]. MySQL EXPLAIN结果集分析 - 附带大量案例

[13]. 什么是间隙锁?


我是长路,感谢你的耐心阅读。如有问题请指出,我会积极采纳!
欢迎关注我的公众号【长路Java】,分享Java学习文章及相关资料
Q群:851968786 我们可以一起探讨学习
注明:转载可,需要附带上文章链接

MySQL学习笔记 04、MySQL进阶(索引、事务、锁)相关推荐

  1. MySQL学习笔记 | 04 - MySQL数据库基本操作(增加、修改、删除、查看)

    MySQL学习笔记 | 01-为什么要使用数据库 MySQL学习笔记 | 02 - MySQL在Windows下的安装.配置.服务启动/停止.用户登录.查看版本号 MySQL学习笔记 | 03 - M ...

  2. MySQL学习笔记04【数据库的查询操作、今日内容、表的约束】

    MySQL 文档-黑马程序员(腾讯微云):https://share.weiyun.com/RaCdIwas 1-MySQL基础.pdf.2-MySQL约束与设计.pdf.3-MySQL多表查询与事务 ...

  3. mysql 学习笔记--存储引擎、索引、sq优化

    全面的 mysql学习笔记–通用语法.函数.数据类型.约束.多表查询.事务 全面的 mysql学习笔记–存储引擎.索引.sql优化 全面的mysql学习笔记–视图/存储过程/触发器.锁.InnoDB引 ...

  4. MySQL 学习笔记-第三篇-索引、存储过程和函数、视图、触发器

    目录 1 索引 1.1 索引简介 1.2 创建索引 1.3 删除索引 1.4 MySQL 8.0 的新特性 1 -支持降序索引 1.5 MySQL 8.0 的新特性 2 -统计直方图 2 存储过程和函 ...

  5. 一千行MySQL学习笔记(MySQL常见SQL语句全详解)

    /* 启动MySQL */ net start mysql/* 连接与断开服务器 */ mysql -h 地址 -P 端口 -u 用户名 -p 密码/* 跳过权限验证登录MySQL */ mysqld ...

  6. MySQL学习笔记:MySQL管理

    文章目录 一.准备工作 1.查看MySQL安装目录 2.启动MySQL服务 二.MySQL数据库管理 1.查看数据库 2.创建数据库 3.显示数据库创建信息 4.使用数据库 5.显示当前打开的数据库 ...

  7. 【MySQL】mysql学习笔记04 事务

    – =事务============ – 事务原则:ASID原则 – 原子性 – 一致性 – 隔离性 – 持久性 – mysql是自动开启事务提交的 1.建表 CREATE DATABASE shop ...

  8. MySQL学习笔记(五)—— 索引存储引擎

    索引&存储引擎 一.MySQL存储引擎介绍 1.什么是数据库存储引擎 2.如何查看引擎 建表时指定引擎 修改表的引擎 **MyISAM和InnoDB的区别** 二.索引 1.什么是索引 2.索 ...

  9. 学习笔记(04):MySQL数据库运维与管理-02-二进制日志及其管理

    立即学习:https://edu.csdn.net/course/play/10084/221708?utm_source=blogtoedu 二进制日志 二进制日志包含所有描述数据库更改的事件(ev ...

最新文章

  1. R语言ggplot2可视化小提琴图(violin plot)并使用ggsignif添加分组显著性(significance)标签
  2. java 本地通信_java – 本地JVM之间的通信
  3. 【平台兼容性】jeecg3.7 兼容weblogic 部署改造方案
  4. 10分钟!构建支持10万/秒请求的大型网站
  5. 从程序员到项目经理(二)
  6. spring boot2 kafka
  7. 80后开网店卖故事:1500多位为感觉而埋单
  8. linux的jdk、tomcat、tomcat安装等
  9. 红黑树与平衡二叉树_大佬用这近百张图来给我解释红黑树,看完直接跪了!
  10. 汉王考勤机管理系统服务器,汉王考勤管理系统7
  11. Unity发布WebGL之后读取StreamingAssets文件路径数据
  12. Asp开发中出现“msxml3.dll 错误 ‘800c0005‘系统未找到指定的资源
  13. elasticsearch控制match执行过程的低级查询处理规则
  14. react在线编辑Excel表格
  15. python 文件夹中的文件批量处理 高通道tif图片转换成jpg格式
  16. 【Apollo学习笔记】从零开始Apollo系统安装
  17. Factors and Factorials
  18. 实时全局光照RSM-Reflective Shadow Maps(RSM)
  19. e-office10.0用户连接SqlServer数据库配置说明
  20. 如安在Linux的把持琐屑下应用假造光驱

热门文章

  1. AD域安全攻防实践(附攻防矩阵图)
  2. r安卡翻译成英文_Bianca[百安卡,比安卡]英文名的中文翻译意思、发音、来源及流行趋势-千代英文名...
  3. Ubuntu16.04卸载不常用软件
  4. 电脑安装USB写入保护功能杜绝U盘传染病毒
  5. 少的有点可怜,955 不加班的公司名单...
  6. 如果没有了月球会怎样?
  7. Q绑查询在线查询手机号,免费查Q绑怎么防止qq信息泄露
  8. 学it的和学计算机的一样吗,非计算机专业改行学IT有前景吗?
  9. 基于Python实现本地音乐播放器的制作
  10. 并发编程 (三) 线程安全性之避免线程不安全