mysql优化1

Posted 我爱钻研

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mysql优化1相关的知识,希望对你有一定的参考价值。

1. mysql架构与sql执行流程【上】

讲师:青山

时长:1h5min

计划:2020/1/19 11:30 – 12:00

 

1.1. MySQL的发展历史

 

2000年MySQL开源,就出现很多分支。

MariaDB【oracle收购后,】,

Percona Server---XtraDB Engine

1.1.1.MYSQL的拼读

MySQL-----【My Ess Que Ell 】----注意:写法-----大小写【简历---体现专业性】

 

 

 

1.1.2.Mysql的官网

https://dev.mysql.com/doc/5.7/en/

 

1.2.SQL执行流程

一条查询语句是如何执行的?

 

Select * from user_innodb where name = ‘青山’;

 

1.2.1.客户端连接服务端

1.2.1.1.通信类型

同步/异步

1.2.1.2.连接方式

长连接/短连接

 

MySQL支持两种方式:

短连接:当一个sql执行完毕,会立即close这个连接。

长连接:保持连接多次使用,不需要频繁地关闭、打开连接。一个连接可被多个客户端复用。

       一般在数据库连接池中使用。它对服务端的影响【消耗内存】

 

 

1.2.1.3.通信协议

       》unix Socket

       >TCP/IP

              连接时,-h参数来体现

       》Named Pipes

       >Share Memory

              后面两种,需要在安装勾选才会有效

 

1.2.1.4.通信方式

 

 

 

MySQL里面使用的半双工通信方式:

》要么客户端------向服务端发送数据

》要么服务端------向客户端响应数据

1.2.2.MySQL服务端参数

 

1.2.1.1.连接参数

一个连接保持多长时间没有使用,会关闭它?

(1).超时时间【8个小时】

wait_timeout【等待超时时间】,如:jdbc程序

interactive_timeout【交互式超时时间】如:数据库连接工具

 

 

 

show GLOBAL VARIABLES like \'wait_timeout\';

show GLOBAL VARIABLES like \'interactive_timeout\';

 

(2).连接数量

show global status like \'Thread%\';

 

 

 

说明:

       当客户端建立一个连接后,服务端是通过线程Thread来进行处理的。

 

Show processlist;

 

 

 

(3).最大连接数

show VARIABLES like \'max_connections\';

 

 

 

Mysql5.5官方默认值为100

Mysql5.7官方默认值为151,如下所示:

 

 

 

说明:

       它的最大值可以设置为2^14 = 16384。

       并不是连接数设置越大,性能越好。

 

       系统参数有两种级别:session和global,当不写明时,默认session

 

 

(4).一次发送数据的最大长度

       当超过这个最大值时,可能会报错。所以,避免不带limit的查询操作

show VARIABLES like \'max_allowed_packet\';

 

MySQL5.7官方默认值为4MB:

 

 

 

MySQL5.5默认值为1MB,如下所示:

 

 

 

(5).MySQL查询缓存开关

show VARIABLES like \'query_cache%\';

 

 

 

而MySQL5.5版本是打开的,MySQL5.5是关闭的。

为什么会关闭它呢?说明MySQL官方不推荐使用,为什么呢?

 

因为查询缓存的生效条件要求很严格,很多情况会导致缓存失效:

》sql语句必须完全一样【甚至空格也不能多或少】

》一张表只要有一条数据发生变化【更新】,所有数据缓存会失效

 

所以,在mysql8.0.0版本中查询缓存模块已经被移除掉了。

(6)查询优化器监控开关

 

show VARIABLES like \'optimizer_trace%\';

 

 

 

修改值:

       Set optimizer_trace = ‘enabled=on’   //开启监控

注意:

       这个查询优化器是MySQL5.7以后才有的,5.5版本是没有的。

(7)查看数据库下所有表的存储引擎

show table status from dsg_dmsdb;

 

 

 

 

(8) 服务端数据的存储目录查看

 

1.2.2.2.参数设置

A.永久设置

       通过修改配置文件。

              Vi /etc/my.cnf

B.动态修改

       使用set命令

 

1.2.3.MySQL的sql执行流程

1.客户端和服务端建立连接

2.服务端缓存数据

       MySQL默认查询缓存的开关是关闭的。

 

3.解析器模块-----校验sql语句

       当我们输入一条不符合sql语句的sql后,点击执行,会报错

 

》词法解析

       把sql拆分成一个个的关键字,select ,*,from…

》语法解析

       经过解析后,解析过程构成一个解析树,如下所示:

 

 

 

       服务端sql的执行有很多的执行路径,执行方式,这些执行方式是怎么得到的呢?我们又是如何了解的呢?

      

       它们由预处理器来完成。

 

       当得到这些执行路径后,又应该选择哪一条执行?如何选择【判断标准是什么?】

这部分功能,由优化器Optimizer来处理。最后得到一个执行计划Execution Plan

 

       优化:sql开销小,就选择哪一种。

 

如何查询一条sql语句的开销情况:

show status like \'Last_query_cost\';

 

 

 

 

 

关于优化器的书籍推荐:

       《数据库查询优化器的艺术-原理解析》

 

      

查询优化器监控情况:

Select * from information_schema.optimizer_trace\\G  

说明:

       执行前提,必须要开启监控开关.

       返回json。分析如下:

 

 

 

 

 

4.数据存储

       经过优化后,生成执行计划,plan是在哪里执行?由谁来执行?

 

       逻辑上,数据是存储在Table表结构中的,可以理解为excel表格。

 

       在存储数据时,还需要组织数据的存储结构。这个结构是由什么决定的呢?

它是由存储引擎来决定的。

 

       Mysql中有多种存储引擎,它们是可以相互替换。

       表新建完成后,存储引擎是可以修改的。

 

       在服务端数据的存储目录查看:

 

 

1. MySQL架构与sql执行流程【下】

讲师:青山

时长:48min

计划:1/20/2020 10:20 –11:20---15:38

2.1.MySQL模块详解

 

 

 这四层自顶向下分别是网络连接层,服务层(核心层),存储引擎层,系统文件层。我们自顶向下开始讲解。

2.1.1.网络接入层

作用

     主要负责连接管理、授权认证、安全等等。每个客户端连接都对应着服务器上的一个线程。服务器上维护了一个线程池,避免为每个连接都创建销毁一个线程。当客户端连接到MySQL服务器时,服务器对其进行认证。可以通过用户名与密码认证,也可以通过SSL证书进行认证。登录认证后,服务器还会验证客户端是否有执行某个查询的操作权限。这一层并不是MySQL所特有的技术。

 

为什么要设计成线程池?

      在服务器内部,每个client都要有自己的线程。这个连接的查询都在一个单独的线程中执行。想象现实场景中数据库访问连接实在是太多了,如果每次连接都要创建一个线程,同时还要负责该线程的销毁。对于系统来说是多么大的消耗。由于线程是操作系统宝贵的资源。这时候线程池的出现就显得自然了,服务器缓存了线程,因此不需要为每个Client连接创建和销毁线程。 

 

2.1.2.服务层

作用    

 

      第二层服务层是MySQL的核心,MySQL的核心服务层都在这一层,查询解析,SQL执行计划分析,SQL执行计划优化,查询缓存。以及跨存储引擎的功能都在这一层实现:存储过程,触发器,视图等。通过下图来观察服务层的内部结构:

 

 

 

下面来简单分析SQL语句在服务层中具体的流程:

 

查询缓存

    在解析查询之前,服务器会检查查询缓存,如果能找到对应的查询,服务器不必进行查询解析、优化和执行的过程,直接返回缓存中的结果集。

 

解析器与预处理器

    MySQL会解析查询,并创建了一个内部数据结构(解析树)。这个过程解析器主要通过语法规则来验证和解析。比如SQL中是否使用了错误的关键字或者关键字的顺序是否正确等等。预处理会根据MySQL的规则进一步检查解析树是否合法。比如要查询的数据表和数据列是否存在等。

 

查询优化器

    优化器将其转化成查询计划。多数情况下,一条查询可以有很多种执行方式,最后都返回相应的结果。优化器的作用就是找到这其中最好的执行计划。优化器并不关心使用的什么存储引擎,但是存储引擎对优化查询是有影响的。优化器要求存储引擎提供容量或某个具体操作的开销信息来评估执行时间。

 

查询引擎

    在完成解析和优化阶段以后,MySQL会生成对应的执行计划,查询执行引擎根据执行计划给出的指令调用存储引擎的接口得出结果。

2.1.3.存储引擎层

作用

    负责MySQL中数据的存储与提取。 服务器中的查询执行引擎通过API与存储引擎进行通信,通过接口屏蔽了不同存储引擎之间的差异。MySQL采用插件式的存储引擎。MySQL为我们提供了许多存储引擎,每种存储引擎有不同的特点。我们可以根据不同的业务特点,选择最适合的存储引擎。如果对于存储引擎的性能不满意,可以通过修改源码来得到自己想要达到的性能。例如阿里巴巴的X-Engine,为了满足企业的需求facebook与google都对InnoDB存储引擎进行了扩充。

特点:

    存储引擎是针对于表的而不是针对库的(一个库中不同表可以使用不同的存储引擎),服务器通过API与存储引擎进行通信,用来屏蔽不同存储引擎之间的差异。

 

下面大致介绍一下MySQL中常见的的存储引擎

InnoDB

    特点:支持事务,适合OLTP应用,假设没有什么特殊的需求,一般都采用InnoDB作为存储引擎。支持行级锁,从MySQL5.5.8开始,InnoDB存储引擎是默认的存储引擎。

MyISAM

    特点

        不支持事务,表锁设计,支持全文索引,主要应用于OLAP应用

    场景

        在排序、分组等操作中,当数量超过一定大小之后,由查询优化器建立的临时表就是MyISAM类型报表,数据仓库

Memory

    特点

        数据都存放在内存中,数据库重启或崩溃,表中的数据都将消失,但是标的结构还是会保存下来。默认使用Hash索引。

    场景

        适合存储OLTP应用的临时数据或中间表。

        用于查找或是映射表,例如邮编和地区的对应表。

 

除此之外还有CSV,Federated、Archive等等。 

2.1.4.系统文件层

作用

        该层主要是将数据库的数据存储在文件系统之上,并完成与存储引擎的交互。

 

2.2.再讲sql执行流程----更新sql

一条更新语句是如何执行的?【包括修改,删除和插入】

Update user_innodb set name = ‘penyuyan’ where id = 1;

2.2.1.buffer pool

 

更新操作时,会从缓冲池buffer pool中读取数据----来自于磁盘中page页

 

修改时,先在内存中修改buffer pool中数据,当数据与磁盘中数据不一致时,再进行同步。

 

查看buffer pool的状态:

show status like \'%innodb_buffer_pool%\';

 

 

 

查询buffer pool默认大小:

show VARIABLES like \'%innodb_buffer_pool%\';

 

 

 

说明:

       这里由于开关关闭,无法查询到默认大小innodb_buffer_pool_chunk_size

 

我们把buffer pool放到内存当中,如果内存占满了怎么办呢?使用LRU内存淘汰算法进行清理不常用数据。

 

 

2.2.1.1.Change buffer区域

当我们要更新page页,加载到buffer pool中时,直接同步到磁盘,即可完成更新。

 

但是,如果要更新的数据在磁盘中,需要先加载到buffer【至少进行一次磁盘io】,再进行更新。那么,buffer Pool有没有好的优化操作呢?

       这个优化,由Change buffer来完成,它的优化原理:

       如果更新记录,不是一种唯一索引的情况,就直接把数据写入change buffer,直接进行更新。不需要比较是否重复。适应于写多读少的场景。

 

 

查看change buffer的参数:

show VARIABLES like \'%change_buffer%\';

 

 

 

innodb_change_buffer_max_size表示change buffer占buffer pool的百分比。

2.2.1.2.log buffer

       如果内存中dirty页的数据还未刷入磁盘,就挂掉了,在向磁盘写入文件时,可能会存在只写入一部分就挂掉的情况。为了防止这种情况,写入磁盘时,会把写入操作以日志的方式记录到log buffer中,当重新启动时,可以从log buffer中加载恢复数据。

 

       Log buffer是用来保存,即将写入磁盘的数据。

 

查看log buffer的大小:

show VARIABLES like \'%log_buffer%\';

 

 

 

innodb_log_buffer_size表示log buffer的大小,默认为16MB.这里可能有修改。

 

 

 

2.2.1.3.redo log

       文件系统中存储redo log模块,它有WAL【Write Ahead Logging】先写日志,后写文件的机制。

 

       写入redo log能提升IO性能,写入效率会提高。为什么同样是写入磁盘,而写入redo log会更快呢?

      

       需要了解两个概念:

》随机IO

》顺序IO

 

磁盘的结构如下:

磁盘的最小单元,称为扇区。【是一个扇形区域】,通常是512b

操作系统与内存的交互,最小单位,称为page页。

操作系统与磁盘的交互,最小单位,称为块。

 

 

如果我们操作的数据是随机分布在磁盘上不同的page页,不同的扇区的话,这个数据该如何找到它呢?

       它是通过磁臂arm旋转,旋转到一个指定的page页之后,盘键找到这个扇区,才能找到需要的数据。一直到在不同的扇区里面把所有的数据拿到之后,IO操作才算完成。

       这种方式称为随机IO.可以知道这种方式,读取数据是比较慢的。

 

       而顺序IO,只需要一次寻址找到数据,然后依次顺序读取相临的数据,就会快速很多。

 

       写入redo log日志文件,是以顺序IO的方式进行,读取性能较快。

 

       Redo log日志文件,主要是用来做灾难恢复的。

 

Redo log的特点:

       》它是基于innodb

       》它是基于物理page

2.2.1.4.log buffer的刷盘时机

Log buffer数据是如何写入redo log文件的,它与事务相关。

 

它的决定参数:

show VARIABLES like \'%log_at_trx_commit%\';

 

 

 

这个参数有0,1,2三种值,默认值为1,只要提交事务,就会写入数据到磁盘文件中。

 

2.2.1.5.binlog日志

       它是用于记录DDL DML,是逻辑日志

 

作用:

       主从

       数据恢复

 

 

2.2.2.Innodb架构

查看官网:

https://dev.mysql.com/doc/refman/5.7/en/innodb-architecture.html

 

 

 

2.2.3.更新sql执行流程总结

1.从内存或磁盘读取数据

2.执行器 执行sql,写入数据

3.先写入日志文件undo log,redo log

4.写入buffer pool

 

 

注意:在内存和磁盘文件之间,有很多工作线程[后台线程]

 

2.2.3.1.加上binlog日志文件后,sql的执行流程

 

 

 

 

3.MySQL的索引原理及使用原则【上】

时长:58min

计划:2020/1/20 15:40 –16:40

目标:

       》索引的本质

       》索引底层数据结构

       》不同存储引擎的索引实现方式

       》索引的创建及使用原则

3.1.MySQL常见优化手段

思考:

       》表的索引越全越好,因为不管什么情况下都能用到索引,对吗?

       》为什么不要在性别字段【值重复较高】上建索引?

       》为什么不建议使用身份证【随机】作为主键?

       》模糊匹配like abc%,like %2673%,like %888都用不到索引,对吗?

       》不要使用select * ,写明具体查询字段,为什么?

3.2.索引的本质

       假设有一张表user_innodb,有500百万条数据,测试查询性能?

Select * from user_innodb where name = \'青山\';

如果没有添加索引,大约需要11s

 

然后创建索引,再进行查询,

 

3.2.1.索引到底是什么?

       【维基百科】:是数据库管理系统(DBMS)中一个排好序的数据结构,以协助快速查询、更新数据库表中数据。

 

       索引建立后,查询数据就相当于查字典。

      

理解:

       建立一个存储地址,与存储数据的映射关系。当建立索引后,可以根据地址,快速定位到要查询的数据。

 

 

普通的数据查询:需要进行全表扫描

3.2.2.创建索引

       有两种方式:新建表时创建,和建表后修改表结构添加

 

1.修改表结构添加索引

Alter table user_innodb add INDEX idx_name(name);

说明:

       Idx_name:索引名称

       (name)针对name字段创建索引。

 

当执行创建索引的sql时,速度较慢,500万条数据【大约需要1min30s】

 

 

2.使用navcat可视化工具创建索引

 

 

 

MySQL中提供3种索引类型:

Normal:普通---------Index/key

Unique:唯一,主键索引是特殊的唯一索引,还要求字段值Not null.

Full Text:全文索引,主要针对文本数据,如:varchar,text

       在文本中匹配一个字符串,常使用模糊匹配【like ‘%abc%’】,但是它会导致索引失效。

为了解决这种大文本中数据查询的性能问题,需要使用Full text全文索引。

 

3.2.2.1.全文索引创建

》新建表时,创建

CREATE TABLE `user_innodb` (

  `id` int(11) NOT NULL,

  `name` varchar(255) DEFAULT NULL,

  `gender` tinyint(4) DEFAULT NULL,

  `phone` varchar(255) DEFAULT NULL,

  PRIMARY KEY (`id`),

  Fulltext index(name) //创建全文索引

或 fulltext key `context_fulltext`(`content`)        //创建全文索引

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

全文索引,匹配字符串,使用match语句。

Select * from fulltext_test where match(content) against(\'test\' IN NATURAL LANGUAGE MODE);

 

3.3.索引底层数据结构推导
3.3.1.引出

玩游戏-----猜数字,只有5次机会,女朋友说:猜猜我双十一买了多少钱的东西?

 

我猜想:

       第一次-------1万----------低了

       第二次-------3万----------高了

       第三次-------你会猜多少呢?会猜2万------使用2分查找

 

二分查找,是查找有序数组效率较高的查询算法,所以,索引的底层数据结构猜想----也可能会拿手二分查找。

 

3.3.1.1.索引的底层数据结构数据模型

(1)有序数组:

分析:

       等值查询【=】

       比较查询【>】

       根据index下标,进行查询,性能很高

 

但是,更新值时,性能较差了。

       因为数据增加或减少了,会导致数组大小发生变化,数组下标就需要相应移动。

       所以,有序数组不适合数据变更的场景。

 

(2)单链表:                                                                                                                                                                    

 

       为了优化,数据库更新操作,sql执行性能问题。可以考虑使用单链表。

 

特点:

       更新快,查询慢【因为需要从第一个节点开始遍历】

 

再分析,那么有没有一种使用二分查询算法,链表呢?--------BST【二叉查找树】

(3)二叉查找树(Binary Search Tree)

 

特点:

       左子树的节点 <父节点

       右子树的节点 >父节点

 

       树的节点数据,投映到一个平面,就是一个天然有序的链表。

 

这种结构的问题:

       当插入数据刚好是一个有序的列表时,构成一个单链表,也称为斜树。

 

 

 

这种情况,就需要从头开始查找,时间复杂度为O(n).查询性能不能得到很好提升。

 

 

问题分析:

       这种树的特点,全部元素都集中到右子树。左子树没有元素。我们称它为不平衡二叉树。

       因此,有人提出平衡二叉树。

(4)平衡二叉树(AVL,Tree-Balanced BST)

特点:左右子树深度差绝对值不能超过1

 

下面来模拟,插入1,2,3,4,5,6的情况

 

》插入1,2 情况如下:

 

 

 

》再插入3

 

 

 

分析:

       当插入3时,右边子树深度为2,而左边子树深度为0,违背高度差。

       所以,发生了左旋操作,第二个节点2提升为父节点。

 

》再插入,4,5,6,如下图所示:

 

 

假设使用AVL存储数据,物理实现逻辑如下所示:

 

 

(5)多路平衡查找树 Balanced Tree【B树】

 

 

查询逻辑:

       如需要查询id = 15的数据,查询顺序如下

       15 《 17----查询左子树

       15 》12 查询右子树,找到磁盘块7,

       再根据磁盘地址,直接找到id = 15的数据

 

 

B树的实现原理:

       B树是通过分裂和合并保持树的平衡的。

 

(6)B+树

       也称加强版多路平衡查找树

B+树存储数据逻辑实现

 

 

 

 

特点:

       关键字= 度=树的高度

       只有叶子节点才存储数据

       叶子节点增加指针---指向下一节点,形成有序链表结构

       对于范围查询,只需要在叶子节点上进行查找即可

 

 

特点总结:

       》B+ Tree能解决b tree解决的问题

       》扫库,扫表能力更强----叶子节点指针

       》磁盘读写能力更强---根节点存储地址,叶子节点存数据,3次IO,可存储2千万

       》排序能力更强

       》效率更加稳定

 

数据存储大小:

       假设一条记录,内存大小为1Kb,一个叶子节点可放16条记录。

       假设id为bigint,8节点 + 指针 = 14b

 

       16k = 16384 /14 = 1170个分叉-------》1170

 

       1170 * 1170 * 16 = 21902400,2千万数据

 

树的深度为2,只需3次IO

3.3.2.InnoDB逻辑存储结构

 

 

3.3.2.1.文件系统中的页

 

 

 

3.3.2.2.页存放数据

 

 

1.页分裂

3.3.2.3.行格式

       表的行格式,决定一个表的行数据是如何物理存储的,也影响着数据查询性能和数据库操作。

 

InnoDB存储引擎支持四名的格式:REDUNDANTCOMPACT, DYNAMIC,和COMPRESSED

 

Antelope(羚羊)InnoDB内置的文件格式,有两种行格式:

       》REDUNDANT

       》COMPACT(5.6默认)

Barracuda(梭子鱼)InnoDB Plugin支持的文件格式,新增两种行格式:

       》DYNAMIC(5.7默认)

》和COMPRESSED

 

3.3.3.AVL Tree数据存储

       当数据存储引擎为InnoDB,使用AVL Tree存储索引。一个树的节点设置为一个页的大小,即为16KB.

 

       一个节点需要存储3个数据:

       》键值

       》数据的磁盘地址

       》子节点的引用

 

以int型字段为例,上面的3个数据占用内存只有几十byte.一个页的大小为16KB,造成内存浪费。

 

      

显然这种数据结构,也是不可行

 

4.MySQL的索引原理及使用原则【下】

4.1.索引底层数据结构为什么不使用红黑树?

红树的定义:

       1.节点分为红色或黑色

       2.根节点必须是黑色

       3.叶子节点都是黑色的NULL节点

       4.红色节点的两个子节点都是黑色(不允许两个相邻的红色节点)

       5.从任意节点出发,到其每个叶子节点的路径中包含相同数量的黑色节点

达到效果:

       从根节点到叶子节点的最长路径不大于最短路径的2倍----保持树的左右基本平衡

 

4.2.MyIsam存储引擎

       索引的数据是存储到磁盘文件上的,myIsam型数据表在磁盘上会生成3个文件:

》*.frm

》*.MYD 存储数据文件

》*.MYI 存储索引值

 

4.2.1.MyISAM-主键索引

 

 

4.3.MyISAM-辅助索引

 

 

 

4.4.InnoDB主键索引

InnoDB型数据表,在磁盘上存储文件为:

》*.frm

》*.ibd 存放数据及索引信息

 

特点:

    是以主键为索引来组织数据的存储

 

 

 

4.5.聚集【簇】索引

    它是索引的键值的逻辑顺序。

如:id为有序1,2,3,4…,表数据行的物理存储顺序,和它是一致的。就称为聚集索引

 

只有主键索引,是聚集索引。

 

4.6.InnoDB的辅助索引

    它和主键索引是不一样的,表示图解如下:

 

主键索引:存储索引和数据

辅助索引:存储索引和主键值

 

如果使用辅助索引(如:name)来查询数据,需要经过两次:

》先在辅助索引的B+树上找到索引值对应的id

》再把id拿到主键索引的B+树上进行查找,最终在叶子节点上找到完整的数据

 

辅助索引的查询,为什么要这样设计?为什么不在叶子节点上存储地址?

 

    原因:地址可能占用空间比较大,浪费存储空间。

此外,我们知道管理磁盘最小的单位是页,如果发生页的分裂,行数据的地址是会改变的。

所以,不存储地址,而存储主键值【不会改变】

 

 

没有主键怎么办呢?

   

   

一张表有没有可能没有聚集索引?

    是不可能的,如果表中没有定义主键,它会选择表中第一个不包含null的unique key,来作为聚集索引来组织数据存储。

    如果表中没有unique的key,它会选用内置的6byte长的rowId来作为聚集索引。

 

4.7.索引的使用原则

4.7.1.误区1.并不会对所有字段建立索引

 

结论:

    当字段重复值很多时,没有必要建立索引

 

4.7.1.1.相关概念
【1】列的离散度

离散度公式:

    count(distinct(column_name))/count(*)

 

比值越大,离散度越高。

 

gender和name相比,哪一个离散度更高?

    gender一般只有0或1两个值,离散高较低。

 

【2】联合索引最左匹配原则

当对多个字段,创建联合索引时,如下语句:

alter table user_innodb add INDEX `comidx_name_phone`(`name`,`phone`);

 

 

name和phone建立联合索引,顺序性非常重要:

 

sql

sql使用:

    where name = ‘青山’and phone = ‘136’

 

如果前面一个字段name不在where子句中,就会导致索引失效,会按全表扫描。

【3】覆盖索引

什么是回表?

 当使用辅助索引时,需要先辅助索引的B+树,再扫描主键索引的B+树,这种动作称为回表。

 

什么是覆盖索引?

 

    查询字段只含索引字段,如:user表中name,phone两个建立联合索引。

select name from user where name = ‘青山’

 

这种查询,称为覆盖索引

 

 

特点:

    这种查询,不需要再经过主键索引的B树,能提升查询性能。

    能够避免回表。

   

验证:

    可使用Explain工具,查看Extra的值,如果为using index,说明sql语句使用到覆盖索引。

 

【4】索引条件下推

 

【4】索引条件下推ICP

 

创建一个employee表:

drop table employees;

CREATE TABLE `employees` (

 `emp_no` int(11) NOT NULL,

 `birth_date` date  NULL,

 `first_name` varchar(14) NOT NULL,

 `last_name` varchar(16) NOT NULL,

 `gender` enum(\'M\',\'F\') NOT NULL,

 `hire_date` date  NULL,

 PRIMARY KEY (`emp_no`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1;

//增加索引

alter table employees add index idx_lastname_firstname(last_name,first_name);

 

 

//插入数据

INSERT INTO `employees` (`emp_no`, `birth_date`, `first_name`, `last_name`, `gender`, `hire_date`) VALUES (1, NULL, \'698\', \'liu\', \'F\', NULL);

INSERT INTO `employees` (`emp_no`, `birth_date`, `first_name`, `last_name`, `gender`, `hire_date`) VALUES (2, NULL, \'d99\', \'zheng\', \'F\', NULL);

INSERT INTO `employees` (`emp_no`, `birth_date`, `first_name`, `last_name`, `gender`, `hire_date`) VALUES (3, NULL, \'e08\', \'huang\', \'F\', NULL);

INSERT INTO `employees` (`emp_no`, `birth_date`, `first_name`, `last_name`, `gender`, `hire_date`) VALUES (4, NULL, \'59d\', \'lu\', \'F\', NULL);

INSERT INTO `employees` (`emp_no`, `birth_date`, `first_name`, `last_name`, `gender`, `hire_date`) VALUES (5, NULL, \'0dc\', \'yu\', \'F\', NULL);

INSERT INTO `employees` (`emp_no`, `birth_date`, `first_name`, `last_name`, `gender`, `hire_date`) VALUES (6, NULL, \'989\', \'wang\', \'F\', NULL);

INSERT INTO `employees` (`emp_no`, `birth_date`, `first_name`, `last_name`, `gender`, `hire_date`) VALUES (7, NULL, \'e38\', \'wang\', \'F\', NULL);

INSERT INTO `employees` (`emp_no`, `birth_date`, `first_name`, `last_name`, `gender`, `hire_date`) VALUES (8, NULL, \'0zi\', \'wang\', \'F\', NULL);

INSERT INTO `employees` (`emp_no`, `birth_date`, `first_name`, `last_name`, `gender`, `hire_date`) VALUES (9, NULL, \'dc9\', \'xie\', \'F\', NULL);

INSERT INTO `employees` (`emp_no`, `birth_date`, `first_name`, `last_name`, `gender`, `hire_date`) VALUES (10, NULL, \'5ba\', \'zhou\', \'F\', NULL);

 

show variables like \'optimizer_switch\'; //查询优化器

 

index_merge=on,index_merge_union=on,

index_merge_sort_union=on,index_merge_intersection=on,

engine_condition_pushdown=on   

index_condition_pushdown索引条件下推开关,默认on

 

奇怪:我的mysql中没有查询到这个开关?

 

set optimizer_switch = \'engine_condition_pushdown=off\';

//set默认当前session级别,只有加上global才会影响其他的会话。

 

当开关关闭后,来测试一个sql查询:

SELECT * from employees WHERE last_name = \'wang\' and first_name LIKE \'%zi\';

这条sql有两种执行方式:

 

1.先找出姓wang的二级索引数据,然后回表,到主键索引里面查询所有符合条件的数据

 

2. 先找出姓wang的二级索引数据,在从二级索引中筛选出,以zi结尾的索引【剩下一个索引】,然后再到主键索引中查询符合条件的数据。

 

显然,第二种方式,查询性能更高。

 

注意:

    索引的过滤是在存储引擎进行的。数据的过滤是在mysql的server层进行的。

 

 

 

当开关设置为off时,explain工具查询到sql使用二级索引方式,extra的值为Using where

 

当开发为on时,extra 的值为Using index_condition,支持索引下推。

 

 

4.7.1.2.创建索引的原则

1.在用于where 判断order排序和join的on字段上创建索引

2.索引的个数不要太多【索引要消耗存储空间,过多可能影响insert数据】

3.区分度低【重复度高】的字段,例如:性别,不要创建索引

4.频繁更新的值,不要作为主键或索引【会发生数据调整,产生页分裂】

5.组合索引把散列性高【区分度高】的值放在前面

6.创建复合索引,而不是修改单列索引

 

作业:

7.过长的字段,怎么创建索引?

 

    可以使用前缀索引,它的语法如下:

key `address`(`address(12)`)           //指定匹配位数

然后通过离散度公式,来测试匹配位数

 

8.为什么不建议用无序的值(如:身份证,uuid)作为索引?

    如果使用随机值作为主键,可能会导致数据插入时发生页分裂

 

4.7.1.3.什么情况导致索引失效?

1.索引列上使用函数(replace,substring,concat,sum,count,avg),表达式

2.字符串不加引号,出现隐式转换

3.like条件中左边带%【最左前缀匹配】

4.负向查询能用到索引吗?

 

    如:<>,!=,not in…等关键字,not like一定不能有效

 

不等,可能会使用主键索引。

4.7.2.explain工具

    查询一条sql语句,预计要扫描多少行数据?

 

使用:

    explain select * from table where name = “qingshan”

如果name创建索引,并且值不会重复,结果rows =1,只需要扫描一行

 

4.7.3.查看表的索引

show indexes from table;

 

5.Mysql事务和锁【上】

时长:54min

计划:2020/3/18 23:27

 

目标:

       》事务特性,与事务并发造成的问题

       》事务读一致性问题的解决方案

       》MVCC的原理

       》锁的分类,行锁的原理,行锁的算法

5.1.什么是数据库事务?

5.1.1.事务的典型场景

       技术层面,只有与数据库更新有关方法

       业务层面,多系统共同存储,金融行业---转帐

5.1.2.什么是事务【定义】

       事务是数据库管理系统【DBMS】执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。

       逻辑单位:理解为最小单位,不可再分割。

       操作序列:dml语句,ddl语句。

5.1.3.哪些存储引擎支持事务

只有InnoDB,ndb【集群】

5.1.4.事务四大特性

原一隔持ACID

ACID

原子性[Atomicity]

    最小单位,不可再分。

对数据库的操作,要么全部成功,要么全部失败。

对于已经的提交的sql,如何撤销呢?需要通过回滚来实现。

回滚是如何实现的呢?undoLog记录是数据修改之前的値。 一旦发生异常,通过undo log来实现回滚。

 

 

一致性【Consistent】

 

    是指数据库的完整性约束没有被破坏。事务执行前后,数据都是一个合法的状态。

如:主键必须唯一,字段长度必须符合要求。

    还有指用户自定义数据的完整性。如:转帐业务,转入与转出必须保持一致。

 

隔离性【Isolation】

 

    可能出现多个事务,操作同一张表或操作同一张表的同一行数据,同时去操作,就会出现一些如并发等干扰性操作。

    隔离性要求,不同事务之间相互独立,互不干扰。

 

持久性【Durable】

    对数据库的操作【增删改】,只要事务提交成功,它的执行结果就应该是永久性的。不会因为系统宕机或机器重启,而导致数据恢复到提交之前。

 

思考:

    数据库的事务应该如何实现?或者数据库崩溃应该如何恢复?

    事务的崩溃恢复是能redo log来实现,磁盘在操作数据时,会先把数据写到内存的buffer pool缓冲池里面。如果在刷盘时出现异常,那么,就可以在系统重启之后读取redo log里面的数据,写入到磁盘,保证数据一致性。

    如果磁盘页上的数据本身已经损坏,redo log是无法恢复的。有一个double write双写缓冲,为数据页创建一个副本,保证数据页的完整性。

 

 

5.1.5.数据库什么时候会出现事务

    最终发送一个指令到数据库执行,才会开启一个事务。

 

1.查询数据库版本:

select VERSION();

 

 

 

2.查询存储引擎

show variables like \'%engine%\';

默认InnoDB

 

 

 

3.查看事务隔离级别:

show GLOBAL variables like \'tx_isolation\';

REPEATABLE-READ 可重复读

》事务并发会带来什么问题

 

4.创建一张student表

 

 CREATE TABLE `student` (

  `id` int(11) NOT NULL,

  `sname` varchar(255) NOT NULL,

  `sno` varchar(20) NOT NULL,

  `company` varchar(11) NOT NULL

) ENGINE=CSV DEFAULT CHARSET=utf8mb4;

 

插入3条数据:

 

INSERT INTO `student` (`id`, `sno`, `sname`, `company`)

VALUES (1, \'GP16666\', \'猫老公\', \'Tencent\');

 

INSERT INTO `student` (`id`, `sno`, `sname`, `company`)

VALUES (2, \'GP17777\', \'个人的精彩\', \'Baidu\');

INSERT INTO `student` (`id`, `sno`, `sname`, `company`)

VALUES (3, \'GP18888\', \'菜鸟\', \'Alibaba\');

 

//执行语句,查看是否有事务?

UPDATE student set sname = \'猫老公111\' where id = 1;

是有事务,自动开启并提交事务

 

什么参数来控制事务的开启与提交?

show global variables like \'autocommit\';

autocommit默认值为on.

 

设置autocommit的值为off

set session autocommit = \'off\';

需要手动开启事务:

begin;或start transaction;

 

begin;

UPDATE student set sname = \'猫老公222\' where id = 1;

ROLLBACK;

先依次执行前两个sql,但未提交事务,所以值不会修改。

 

 

结束事务:

commit;或rollback;

 

注意:

    事务所持有的锁,在事务结束之后,就会自动释放掉。

    当我们使用客户端连接数据库服务端,关闭会话【一个窗口】,连接断开,就会结束事务,也会释放锁。

 

 

5.1.6. 事务并发会带来什么问题

有3大问题:

5.1.6.1.脏读

 

 

 

前提:两个事务[Transation A 和 B]操作同一张表同一行数据

说明:

       事务A通过select查询到一条记录,事务B执行update更新语句【但未提交】,修改age=18.

       事务A再执行select查询,获取不同的结果。

结论:

       同一事务A,执行同样的select语句,得到不同的结果。是因为其他事务【B】修改了数据【未提交】,即读取到其他事务未提交的数据。

       其他事务未提交的数据,可能回滚,所以读取到的是脏数据。这种情况称为脏读。

5.1.6.2.不可重复读

 

 

说明:

       事务A通过select查询到一条记录,事务B执行update更新语句【并完成提交】,修改age=18.

       事务A再执行select查询,获取不同的结果。

 

结论:

       同一事务A,执行同样的select语句,得到不同的结果。是因为其他事务【B】修改了数据【并提交】,即读取到其他事务已提交的数据。

       其他事务已提交的数据,不能回滚,所以不可能重复读取原来数据。这种情况称为不可重复读。

5.1.6.3.幻读

 

 

 

前提:两个事务[Transation A 和 B]操作同一张表

说明:

       事务A通过select范围查询,查询到一条记录,事务B执行insert语句【并且提交】

       事务A再执行select范围查询,获取两条记录,得到不同的结果。

结论:

       同一事务A,两次执行同样的select语句,得到不同的结果。是因为其他事务【B】插入了新数据【并提交】,即读取到比原来更多的数据。

       这种读取更多的数据,可理解为是原来数据的幻影,称为幻读。

5.1.6.4.问题解决

 事务并发带来3大问题其实都是数据库读一致性问题。必须由数据库提供一定的事务隔离机制来解决。

 

       打个比方,我们到餐厅去吃饭,基本的桌椅应该由餐厅来提供,而不是我们自带。

 

所以,就由很多的数据库专家联合制定标准。产生数据库的4大隔离级别。

 

 

 

5.1.7.数据库四大隔离级别

SQL92 ANSI/ISO标准:

http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt

 

搜索_iso:

 

3种问题:

1) P1 ("Dirty read"): SQL-transaction T1 modifies a row. SQL-

            transaction T2 then reads that row before T1 performs a COMMIT.

            If T1 then performs a ROLLBACK, T2 will have read a row that was

            never committed and that may thus be considered to have never

            existed.

 

         2) P2 ("Non-repeatable read"): SQL-transaction T1 reads a row. SQL-

            transaction T2 then modifies or deletes that row and performs

            a COMMIT. If T1 then attempts to reread the row, it may receive

            the modified value or discover that the row has been deleted.

 

         3) P3 ("Phantom"): SQL-transaction T1 reads the set of rows N

            that satisfy some <search condition>. SQL-transaction T2 then

            executes SQL-statements that generate one or more rows that

            satisfy the <search condition> used by SQL-transaction T1. If

            SQL-transaction T1 then repeats the initial read with the same

            <search condition>, it obtains a different collection of rows.

 

 

 

 

 

不同的数据库厂商有不同的数据库隔离级别:

Oracle只支持RC【默认】,串行化

Mysql有4种:默认是可重复读

 

思考:

       如果要解决读一致性的问题,保证一个事务中前后两次读取数据结果一致,实现事务隔离,应该怎么做?

 

5.1.8.事务隔离级别的解决方案

解决方案:

第一种:

在读取数据前,对其加锁,阻止其他事务对数据进行修改【LBCC】lock Based Concurrency Control【基于锁的并发控制】

 

第二种:

       生成一个数据请求时间点的一致性数据快照(Snapshot),并用这个快照来提供一定的级别(语句级或事务级)的一致性读取【MVCC】Multi Version Concurrency Control

 

5.8.1.1.MVCC

该方案是如何实现的?

DB_TRX_ID,6字节:插入或更新行的最后一个事务的事务ID,事务编号是自动递增的(创建版本)

DB_ROLL_PTR,7字节:回滚指针(删除版本)

 

 

 

 

 

上面是mysql数据库InnoDB中实现的两个隐藏字段,都理解为当前事务的编号。

 

它的核心思想:

       一旦我们开启一个事务,在当前事务里面已经开始查询数据了,在我后面插入的数据是查询不到的。即使数据在后面被修改或删除,依然可读取到数据。

 

 

思考:

       这个快照是什么时候创建的?读取数据时,怎么能保证读取到的是快照的数据,而不是最新的数据或已经被其他事务修改的数据?这个应该如何来实现?

 

 

 

理解过程如下:

https://www.processon.com/view/link/5d29999ee4b07917e2e09298

第一个事务,初始化数据(检查初始数据),插入两条数据[insert]


Transaction 1
begin;
insert into mvcctest values(NULL,\'qingshan\') ;
insert into mvcctest values(NULL,\'jack\') ;
commit;
此时的数据,创建版本是当前事务 ID,删除版本为空:

 

注意:

    navcat客户端如何表示不同事务?

    其实一个会话窗口【创建一个查询器】,就表示一个事务。

  

 

 

第二个事务,执行第 1 次查询,读取到两条原始数据,这个时候事务 ID 是 2:

 

 

 第三个事务,插入数据:

 

 

 

此时的数据,多了一条 tom,它的创建版本号是当前事务编号,3:

 

第二个事务,执行第 2 次查询:

以上是关于mysql优化1的主要内容,如果未能解决你的问题,请参考以下文章

使用 C++ 反转句子中的每个单词需要对我的代码片段进行代码优化

从JVM的角度看JAVA代码--代码优化

linux中怎么查看mysql数据库版本

连接MySQL出现错误:ERROR 1045 (28000): Access denied for user ‘root‘@‘localhost‘ (using password: YES)(代码片段

部分代码片段

Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段