MySQL
网课链接 https://www.bilibili.com/video/BV1KW411u7vy?p=1
相关笔记链接 https://github.com/RingoTangs/LearningNote/blob/master/MySQL/MySQL.md
关系型数据库
SQL高级
数据库内部结构和原理
数据库建模优化
数据库索引建立
SQL 语句优化
SQL 编程(自定义函数、存储过程、触发器、定时任务)
mysql 服务器的安装配置
数据库的性能监控分析与系统优化
各种参数常量设定
主从复制
分布式架构搭建、垂直切割和水平切割
数据迁移
容灾备份和恢复
shell 或 python 等脚本语言开发
对开源数据库进行二次开发
# 查看Linux服务器上是否安装过MySQL
rpm -qa | grep -i mysql # 查询出所有mysql依赖包
# 1、拉取镜像
docker pull mysql:5.7
# 2、创建实例并启动
docker run -p 3306:3306 --name mysql \
-v /mydata/mysql/log:/var/log/mysql \
-v /mydata/mysql/data:/var/lib/mysql \
-v /mydata/mysql/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
进入mysql容器 docker exec -it 容器名称|容器id bin/bash 进入mysql
docker exec -it 容器名称|容器id env LANG=C.UTF-8 bin/bash 进入容器可以输入中文
容器中进入mysql客户端
mysql -u root -p
# 3、mysql配置 /mydata/mysql/conf/my.conf
[client]
#mysqlde utf8字符集默认为3位的,不支持emoji表情及部分不常见的汉字,故推荐使用utf8mb4
default-character-set=utf8
[mysql]
default-character-set=utf8
[mysqld]
#设置client连接mysql时的字符集,防止乱码
init_connect='SET collation_connection = utf8_general_ci'
init_connect='SET NAMES utf8'
#数据库默认字符集
character-set-server=utf8
#数据库字符集对应一些排序等规则,注意要和character-set-server对应
collation-server=utf8_general_ci
# 跳过mysql程序起动时的字符参数设置 ,使用服务器端字符集设置
skip-character-set-client-handshake
# 禁止MySQL对外部连接进行DNS解析,使用这一选项可以消除MySQL进行DNS解析的时间。但需要注意,如果开启该选项,则所有远程主机连接授权都要使用IP地址方式,否则MySQL将无法正常处理连接请求!
skip-name-resolve
# 4、重启mysql容器
docker restart mysql
# 5、进入到mysql容器
docker exec -it mysql /bin/bash
# 6、查看修改的配置文件
cat /etc/mysql/my.conf
第三方软件包 一般放/opt
var/lib/mysqu/
mysql数据库文件的存放路径
/var/lib/mysqlatguigu.cloud.pid
/usrlshare/mysql
配置文件目录
mysql.server命令及配置文件
/usr/bin
相关命令目录
mysqladmin mysqldump等命令
/etc/init.d/mysql
启停相关脚本
进入到mysql数据库并查看字符集
# show variables like 'character%';
# show variables like '%char%';
MySQL5.7配置文件位置是/etc/my.cnf或者/etc/mysql/my.cnf,如果字符集不是utf-8直接进入配置文件修改即可。
windows 配置文件 my.ini
linux 配置文件 /etc/my.cnf
主要配置
1)二进制日志 log-bin 主要用于主从复制
开启mysql binlog功能
log-bin=mysql-bin
2)错误日志 log-error 默认是关闭的,记录严重的警告和错误信息,每次启动和关闭的详细信息
数据库错误日志文件
log-error = error.log
3)查询日志 log 默认关闭,记录查询的`sql`语句,如果开启会降低`MySQL`整体的性能,因为记录日志需要消耗系统资源
慢查询sql日志设置
slow_query_log = 1
slow_query_log_file = slow.log
4)数据文件
- `frm文件`:存放表结构
- `myd`文件:存放表数据
- `myi`文件:存放表索引
var/lib/mysql 默认路径
- `Connectors`:指的是不同语言中与SQL的交互。
- `Connection Pool`:管理缓冲用户连接,线程处理等需要缓存的需求。MySQL数据库的连接层。
- `Management Serveices & Utilities`:系统管理和控制工具。备份、安全、复制、集群等等。
- `SQL Interface`:接受用户的SQL命令,并且返回用户需要查询的结果。
- `Parser`:SQL语句解析器。
- `Optimizer`:查询优化器,SQL语句在查询之前会使用查询优化器对查询进行优化。就是优化客户端请求query,根据客户端请求的 query 语句,和数据库中的一些统计信息,在一系列算法的基础上进行分析,得出一个最优的策略,告诉后面的程序如何取得这个 query 语句的结果。For Example: `select uid,name from user where gender = 1;`这个`select `查询先根据`where `语句进行选取,而不是先将表全部查询出来以后再进行`gender`过滤;然后根据`uid`和`name`进行属性投影,而不是将属性全部取出以后再进行过滤。最后将这两个查询条件联接起来生成最终查询结果。
- `Caches & Buffers`:查询缓存。
- `Pluggable Storage Engines`:存储引擎接口。MySQL区别于其他数据库的最重要的特点就是其插件式的表存储引擎(注意:存储引擎是基于表的,而不是数据库)。
- `File System`:数据落地到磁盘上,就是文件的存储。
MySQL数据库和其他数据库相比,MySQL有点与众不同,主要体现在存储引擎的架构上,插件式的存储引擎架构将查询处理和其他的系统任务以及数
据的存储提取相分离。这种架构可以根据业务的需求和实际需求选择合适的存储引擎。
逻辑架构分层
- 连接层:最上层是一些客户端和连接服务,包含本地sock通信和大多数基于客户端/服务端工具实现的类似于`tcp/ip`的通信。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于`SSL`的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作权限。
- 服务层:MySQL的核心服务功能层,该层是MySQL的核心,包括查询缓存,解析器,解析树,预处理器,查询优化器。主要进行查询解析、分析、查询缓存、内置函数、存储过程、触发器、视图等,select操作会先检查是否命中查询缓存,命中则直接返回缓存数据,否则解析查询并创建对应的解析树。
- 引擎层:存储引擎层,存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API与存储引擎进行通信。不同的存储引擎具有的功能不同,这样我们可以根据自己的实际需要进行选取。
- 存储层:数据存储层,主要是将数据存储在运行于裸设备的文件系统之上,并完成与存储引擎的交互。
`show engines;`命令查看MySQL5.7支持的存储引擎
`show variables like 'default_storage_engine%';`查看当前数据库正在使用的存储引擎。
Percona为MySQL数据库服务器进行了改进,在功能和性能上较MySQL有着很显著的提升。该版本提升了在高负载情况下的InnoDB的性能、为DBA提供一些非常有用的性能诊断工具;另外有更多的参数和命令来控制服务器行为。
对比项 | MyISAM | InnoDB |
---|---|---|
主外键 | 不支持 | 支持 |
事务 | 不支持 | 支持 |
行表锁 | 表锁,即使操作一条记录也会锁住整张表,不适合高并发操作 | 行锁,操作时只锁某一行,不对其他行有影响,适合高并发操作 |
缓存 | 只缓存索引,不缓存真实数据 | 不仅缓存索引还要缓存真实数据,対内存要求较高,而且内存大小対性能有决定性影响 |
表空间 | 小 | 大 |
关注点 | 性能 | 事务 |
默认安装 | Y | Y |
原因
查询语句太差
索引失效(单值,复合)
关联太多join(设计缺陷或者不得已的需求)
服务器调优以及各个参数的设置(缓冲、线程数等)不合理
FROM 笛卡尔积
ON 主表保留
JOIN 不符合ON也添加
WHERE 非聚合 非SELECT别名
GROUP BY 改变对表引用
HAVING 只作用分组后
SELECT DISTEINCT
ORDER BY 可使用SELECT别名
LIMIT
select # 7 DISTINCT
# 8
from # 1
JOIN # 3 ON # 2
where # 4
group by # 5
having # 6
order by # 9
limit # 10
7种JOIN 相关链接 https://www.cnblogs.com/fudashi/p/7491039.html
/* 1 */ A全
SELECT FROM TableA A LEFT JOIN TableB B ON A.Key = B.Key;
/* 2 */ B全
SELECT FROM TableA A RIGHT JOIN TableB B ON A.Key = B.Key;
/* 3 */ AB
SELECT FROM TableA A INNER JOIN TableB B ON A.Key = B.Key;
/* 4 */ A-AB
SELECT FROM TableA A LEFT JOIN TableB B ON A.Key = B.Key WHERE B.Key IS NULL;
/* 5 */ B-AB
SELECT FROM TableA A RIGHT JOIN TableB B ON A.Key = B.Key WHERE A.Key IS NULL;
/* 6 */ A+B
SELECT FROM TableA A FULL OUTER JOIN TableB B ON A.Key = B.Key;
/* MySQL不支持FULL OUTER JOIN这种语法 可以改成 1+2 */
SELECT FROM TableA A LEFT JOIN TableB B ON A.Key = B.Key
UNION
SELECT FROM TableA A RIGHT JOIN TableB B ON A.Key = B.Key;
/* 7 */ A+B-AB
SELECT FROM TableA A FULL OUTER JOIN TableB B ON A.Key = B.Key WHERE A.Key IS NULL OR B.Key IS NULL;
/* MySQL不支持FULL OUTER JOIN这种语法 可以改成 4+5 */
SELECT FROM TableA A LEFT JOIN TableB B ON A.Key = B.Key WHERE B.Key IS NULL;
UNION
SELECT FROM TableA A RIGHT JOIN TableB B ON A.Key = B.Key WHERE A.Key IS NULL;
官方定义 帮助mysql高效获取数据的数据结构
本质 是排好序的快速查找数据结构
索引目的 提高查询效率,可以类比字典
索引会影响MySQL查找WHERE的查询条件和排序ORDER BY两大功能
数据本身之外,数据库还维护着一个满足特定查找算法的数据结构,这些数据结构以某种方式指向数据,这样就可以在这些数据结构的基
础上实现高级查找算法,这种数据结构就是索引
BTREE
逻辑删除原因
数据分析
索引
一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在磁盘上
平时所说的索引,如果没有特别指明,都是指B树(多路搜索树,并不一定是二叉的)结构组织的索引。其中聚集索引,次要索引,覆盖
索引,复合索引,前缀索引,唯一索引默认都是使用B+树索引,统称索引。当然,除了B+树这种数据结构的索引之外,还有哈希索引
(Hash Index)等
linux查看磁盘空间命令
df -h
索引
优点
1)查找 降低IO成本
2)排序 降低数据排序的成本,降低CPU的消耗
缺点
1)实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间的
2)虽然索引大大提高了查询速度,但是同时会降低表的更新速度,对表频繁的进行`INSERT`、`UPDATE`和`DELETE`。因为更新表的时候,
MySQL不仅要保存数据,还要保存一下索引文件每次更新添加的索引列的字段,都会调整因为更新所带来的键值变化后的索引信息
3)索引只是提高效率的一个因素,如果MySQL有大数据量的表,就需要花时间研究建立最优秀的索引或优化查询
索引分类
单值索引 一个索引只包含单个列,一个表可以有多个单列索引
唯一索引 索引列的值必须唯一,但是允许空值
复合索引 一个索引包含多个字段
基本语法
```
/* 1、创建索引 [UNIQUE]可以省略*/
/* 如果只写一个字段就是单值索引,写多个字段就是复合索引 */
CREATE [UNIQUE] INDEX indexName ON tabName(columnName(length));
ALTER tabName ADD [UNIQUE] INDEX [indexName] ON (columnName(length));
/* 2、删除索引 */
DROP INDEX [indexName] ON tabName;
/* 3、查看索引 */
/* 加上\G就可以以列的形式查看了 不加\G就是以表的形式查看 */
SHOW INDEX FROM tabName \G;
```
使用`ALTER`命令来为数据表添加索引
```
/* 1、该语句添加一个主键,这意味着索引值必须是唯一的,并且不能为NULL */
ALTER TABLE tabName ADD PRIMARY KEY(column_list);
/* 2、该语句创建索引的键值必须是唯一的(除了NULL之外,NULL可能会出现多次) */
ALTER TABLE tabName ADD UNIQUE indexName(column_list);
/* 3、该语句创建普通索引,索引值可以出现多次 */
ALTER TABLE tabName ADD INDEX indexName(column_list);
/* 4、该语句指定了索引为FULLTEXT,用于全文检索 */
ALTER TABLE tabName ADD FULLTEXT indexName(column_list);
```
索引的数据结构
1)BTREE索引
2)Hash索引
3)full-text索引
4)R-Tree索引
相关文章 https://blog.csdn.net/ywl470812087/article/details/86218842
哪些情况需要建索引
主键自动建立唯一索引
频繁作为查找条件的字段
查询中与其他表关联的字段,外键关系建立索引
频繁更新的字段不适合创建索引(因为每次更新都需要更新索引)
where条件用不到的字段不创建索引
在高并发下倾向创建阻和索引
查询中排序的字段
查询中统计或者分组字段
哪些情况不要建索引
记录太少的表
经常增删改的表
频繁更新的字段不适合创建索引
Where条件里用不到的字段不创建索引
假如一个表有10万行记录,有一个字段A只有true和false两种,并且每个值的分布概率大约为50%,那么对A字段建索引一般不会提高
数据库的查询速度。索引的选择性是指索引列中不同值的数目与表中记录数的比。如果一个表中有2000条记录,表索引列有1980个不同
的值,那么这个索引的选择性就是1980/2000=0.99。一个索引的选择性越接近于1,这个索引的效率就越高
MySQL Query Optimizer 查询优化器
Mysql中有专门负责优化SELECT语句的优化器模块,主要功能:通过计算分析系统中收集到的统计信息,为客户端请求的Query提供他认
为最优的执行计划(他认为最优的数据检索方式,但不见得是DBA认为是最优的,这部分最耗费时间)
MySQL常见瓶颈
CPU: CPU在饱和的时候一般发生在数据装入内存或从磁盘上读取数据时候
IO 磁盘I/O瓶颈发生在装入数据远大于内存容量的时候
服务器硬件的性能瓶颈 top,free, iostat和vmstat来查看系统的性能状态
Explain
EXPLAIN:SQL的执行计划,使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理SQL语句的。
语法:`explain` + `SQL`
explain select * from pms_category \G;
可以查看以下信息:
- `id`:表的读取顺序。
- `select_type`:数据读取操作的操作类型。
- `possible_keys`:哪些索引可以使用。
- `key`:哪些索引被实际使用。
- `ref`:表之间的引用。
- `rows`:每张表有多少行被优化器查询。
`id`:表的读取和加载顺序。
值有以下三种情况:
- `id`相同,执行顺序由上至下。
- `id`不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行。
- `id`相同不同,同时存在。永远是id大的优先级最高,id相等的时候顺序执行。
`select_type`:数据查询的类型,主要是用于区别,普通查询、联合查询、子查询等的复杂查询。
- `SIMPLE`:简单的`SELECT`查询,查询中不包含子查询或者`UNION `。
- `PRIMARY`:查询中如果包含任何复杂的子部分,最外层查询则被标记为`PRIMARY`。
- `SUBQUERY`:在`SELECT`或者`WHERE`子句中包含了子查询。
- `DERIVED`:在`FROM`子句中包含的子查询被标记为`DERIVED(衍生)`,MySQL会递归执行这些子查询,把结果放在临时表中。
- `UNION`:如果第二个`SELECT`出现在`UNION`之后,则被标记为`UNION`;若`UNION`包含在`FROM`子句的子查询中,外层`SELECT`将被标记为`DERIVED`。
- `UNION RESULT`:从`UNION`表获取结果的`SELECT`。
`table`:显示这一行的数据是关于哪张表的
`type`:访问类型排列。
从最好到最差依次是:`system`>`const`>`eq_ref`>`ref`>`range`>`index`>`ALL`。除了`ALL`没有用到索引,其他级别都用到索引了。
一般来说,得保证查询至少达到`range`级别,最好达到`ref`。
- `system`:表只有一行记录(等于系统表),这是`const`类型的特例,平时不会出现,这个也可以忽略不计。
- `const`:表示通过索引一次就找到了,`const`用于比较`primary key`或者`unique`索引。因为只匹配一行数据,所以很快。如将主键置于`where`列表中,MySQL就能将该查询转化为一个常量。
- `eq_ref`:唯一性索引扫描,读取本表中和关联表表中的每行组合成的一行,查出来只有一条记录。除 了 `system` 和` const` 类型之外, 这是最好的联接类型。常见于主键或唯一索引扫描
- `ref`:非唯一性索引扫描,返回本表和关联表某个值匹配的所有行,查出来有多条记录。
- `range`:只检索给定范围的行,一般就是在`WHERE`语句中出现了`BETWEEN`、`< >`、`in`等的查询。这种范围扫描索引比全表扫描要好,因为它只需要开始于索引树的某一点,而结束于另一点,不用扫描全部索引。
- `index`:`Full Index Scan`,全索引扫描,`index`和`ALL`的区别为`index`类型只遍历索引树。也就是说虽然`ALL`和`index`都是读全表,但是`index`是从索引中读的,`ALL`是从磁盘中读取的。
- `ALL`:`Full Table Scan`,没有用到索引,全表扫描。
`possible_keys`:显示可能应用在这张表中的索引,一个或者多个。查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用。
`key`:实际使用的索引。如果为`NULL`,则没有使用索引。查询中如果使用了覆盖索引,则该索引仅仅出现在`key`列表中。
`key_len`:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。
显示的值为索引字段的最大可能长度,并非实际使用长度,即`key_len`是根据表定义计算而得,不是通过表内检索出的。
在不损失精度的情况下,长度越短越好。
相关链接 https://blog.csdn.net/qq_34930488/article/details/102931490
`ref`:显示索引的哪一列被使用了,如果可能的话,是一个常数。哪些列或常量被用于查找索引列上的值。
`rows`:根据表统计信息及索引选用情况,大致估算出找到所需的记录需要读取的行数。
`Extra`:包含不适合在其他列中显示但十分重要的额外信息。
- `Using filesort`:说明MySQL会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中无法利用索引完成的排序操作成为"文件内排序"。九死一生
- `Using temporary`:使用了临时表保存中间结果,MySQL在对查询结果排序时使用了临时表。常见于排序`order by`和分组查询`group by`。临时表対系统性能损耗很大。十死无生
- `Using index`:表示相应的`SELECT`操作中使用了覆盖索引,避免访问了表的数据行,效率不错!如果同时出现`Using where`,表示索引被用来执行索引键值的查找;如果没有同时出现`Using where`,表明索引用来读取数据而非执行查找动作。
- `Using where`:表明使用了`WHERE`过滤。
- `Using join buffer`:使用了连接缓存。
- `impossible where`:`WHERE`子句的值总是false,不能用来获取任何元组。
覆盖索引
就是select的数据列只用从索引中就能够取得,不必从数据表中读取,换句话说查询列要被所使用的索引覆盖。
注意:如果要使用覆盖索引,一定不能写SELECT *,要写出具体的字段。
select的数据列只用从索引中就能够取得,不必读取数据行,MySQL可以利用索引返回select列表中的字段,而不必根据
索引再次读取数据文件,换句话说查询列要被所建的索引覆盖。
索引分析
单表
范围索引会导致失效
两表
左连接将索引创建在右表上更合适,右连接将索引创建在左表上更合适。
这是由左连接特性决定的。LEFT JOIN条件用于确定如何从右表搜索行,左边一定都有
三表
根据两表查询优化的经验,左连接需要在右表上添加索引,所以尝试在另外两个表上添加索引
Join语句的优化
- 尽可能减少`JOIN`语句中的`NestedLoop`(嵌套循环)的总次数:永远都是小的结果集驱动大的结果集。
- 优先优化`NestedLoop`的内层循环。
- 保证`JOIN`语句中被驱动表上`JOIN`条件字段已经被索引。
- 当无法保证被驱动表的`JOIN`条件字段被索引且内存资源充足的前提下,不要太吝惜`Join Buffer` 的设置。
索引失效(应该避免)
- 全值匹配我最爱
- 最佳左前缀法则
- 不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描
- 索引中范围条件右边的字段会全部失效
- 尽量使用覆盖索引(只访问索引的查询,索引列和查询列一致),减少`SELECT *`
- MySQL在使用`!=`或者`<>`的时候无法使用索引会导致全表扫描
- `is null`、`is not null`也无法使用索引
- `like`以通配符开头`%abc`索引失效会变成全表扫描(使用覆盖索引就不会全表扫描了)
- 字符串不加单引号索引失效
- 少用`or`,用它来连接时会索引失效
全值匹配我最爱:索引都存在
最佳左前缀法则:如果索引是多字段的复合索引,要遵守最佳左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的字段。
- 从最左 如果断开的话 也只能用第一个
索引列上不计算:在索引列上进行计算,会使索引失效
范围之后全失效:查询范围的字段使用到了索引,但是范围之后的索引字段会失效
覆盖索引尽量用:查询一定不用*
不等有时会失效
like百分加右边
- 覆盖索引保两边:如果一定要使用`%like`,而且还要保证索引不失效,那么使用覆盖索引来编写SQL。
字符要加单引号
少用`or`,用它来连接时会索引失效
/* 最好索引怎么创建的,就怎么用,按照顺序使用,避免让MySQL再自己去翻译一次 */
/* 1.全值匹配 用到索引c1 c2 c3 c4全字段 */
EXPLAIN SELECT * FROM `test03` WHERE `c1` = 'a1' AND `c2` = 'a2' AND `c3` = 'a3' AND `c4` = 'a4';
/* 2.用到索引c1 c2 c3 c4全字段 MySQL的查询优化器会优化SQL语句的顺序*/
EXPLAIN SELECT * FROM `test03` WHERE `c1` = 'a1' AND `c2` = 'a2' AND `c4` = 'a4' AND `c3` = 'a3';
/* 3.用到索引c1 c2 c3 c4全字段 MySQL的查询优化器会优化SQL语句的顺序*/
EXPLAIN SELECT * FROM `test03` WHERE `c4` = 'a4' AND `c3` = 'a3' AND `c2` = 'a2' AND `c1` = 'a1';
/* 4.用到索引c1 c2 c3字段,c4字段失效,范围之后全失效 */
EXPLAIN SELECT * FROM `test03` WHERE `c1` = 'a1' AND `c2` = 'a2' AND `c3` > 'a3' AND `c4` = 'a4';
/* 5.用到索引c1 c2 c3 c4全字段 MySQL的查询优化器会优化SQL语句的顺序*/
EXPLAIN SELECT * FROM `test03` WHERE `c1` = 'a1' AND `c2` = 'a2' AND `c4` > 'a4' AND `c3` = 'a3';
/* 6.用到了索引c1 c2 c3三个字段, c1和c2两个字段用于查找, c3字段用于排序了但是没有统计到key_len中,c4字段失效*/
EXPLAIN SELECT * FROM `test03` WHERE `c1` = 'a1' AND `c2` = 'a2' AND `c4` = 'a4' ORDER BY `c3`;
/* 7.用到了索引c1 c2 c3三个字段,c1和c2两个字段用于查找, c3字段用于排序了但是没有统计到key_len中*/
EXPLAIN SELECT * FROM `test03` WHERE `c1` = 'a1' AND `c2` = 'a2' ORDER BY `c3`;
/* 8.用到了索引c1 c2两个字段,c4失效,c1和c2两个字段用于查找,c4字段排序产生了Using filesort说明排序没有用到c4字段 */
EXPLAIN SELECT * FROM `test03` WHERE `c1` = 'a1' AND `c2` = 'a2' ORDER BY `c4`;
/* 9.用到了索引c1 c2 c3三个字段,c1用于查找,c2和c3用于排序 */
EXPLAIN SELECT * FROM `test03` WHERE `c1` = 'a1' AND `c5` = 'a5' ORDER BY `c2`, `c3`;
/* 10.用到了c1一个字段,c1用于查找,c3和c2两个字段索引失效,产生了Using filesort */
EXPLAIN SELECT * FROM `test03` WHERE `c1` = 'a1' AND `c5` = 'a5' ORDER BY `c3`, `c2`;
/* 11.用到了c1 c2 c3三个字段,c1 c2用于查找,c2 c3用于排序 */
EXPLAIN SELECT * FROM `test03` WHERE `c1` = 'a1' AND `c2` = 'a2' ORDER BY c2, c3;
/* 12.用到了c1 c2 c3三个字段,c1 c2用于查找,c2 c3用于排序 */
EXPLAIN SELECT * FROM `test03` WHERE `c1` = 'a1' AND `c2` = 'a2' AND `c5` = 'a5' ORDER BY c2, c3;
/*
13.用到了c1 c2 c3三个字段,c1 c2用于查找,c2 c3用于排序 没有产生Using filesort
因为之前c2这个字段已经确定了是'a2'了,这是一个常量,再去ORDER BY c3,c2 这时候c2已经不用排序了!
所以没有产生Using filesort 和(10)进行对比学习!
*/
EXPLAIN SELECT * FROM `test03` WHERE `c1` = 'a1' AND `c2` = 'a2' AND `c5` = 'a5' ORDER BY c3, c2;
/* GROUP BY 表面上是叫做分组,但是分组之前必定排序。 */
/* 14.用到c1 c2 c3三个字段,c1用于查找,c2 c3用于排序,c4失效 */
EXPLAIN SELECT * FROM `test03` WHERE `c1` = 'a1' AND `c4` = 'a4' GROUP BY `c2`,`c3`;
/* 15.用到c1这一个字段,c4失效,c2和c3排序失效产生了Using filesort */
EXPLAIN SELECT * FROM `test03` WHERE `c1` = 'a1' AND `c4` = 'a4' GROUP BY `c3`,`c2`;
`GROUP BY`基本上都需要进行排序,索引优化几乎和`ORDER BY`一致,但是`GROUP BY`会有临时表的产生。
索引优化的一般性建议:
- 对于单值索引,尽量选择针对当前`query`过滤性更好的索引。
- 在选择复合索引的时候,当前`query`中过滤性最好的字段在索引字段顺序中,位置越靠前越好。
- 在选择复合索引的时候,尽量选择可以能够包含当前`query`中的`where`子句中更多字段的索引。
- 尽可能通过分析统计信息和调整`query`的写法来达到选择合适索引的目的。
分析慢SQL的步骤
分析:
1、观察,至少跑1天,看看生产的慢SQL情况。
2、开启慢查询日志,设置阈值,比如超过5秒钟的就是慢SQL,并将它抓取出来。
3、explain + 慢SQL分析。
4、show Profile。
5、运维经理 OR DBA,进行MySQL数据库服务器的参数调优。
总结(大纲):
1、慢查询的开启并捕获。
2、explain + 慢SQL分析。
3、show Profile查询SQL在MySQL数据库中的执行细节和生命周期情况。
4、MySQL数据库服务器的参数调优。
小表驱动大表
优化原则:对于MySQL数据库而言,永远都是小表驱动大表。
IN和EXISTS
```
/* 优化原则:小表驱动大表,即小的数据集驱动大的数据集 */
/* in:适合外部表数据大于子查询的表数据的业务场景*/
SELECT * FROM `A` WHERE `id` IN (SELECT `id` FROM `B`)
/* exist适合 子查询中表数据大于外查询表中数据的业务场景 */
SELECT * FROM `A` WHERE EXISTS (SELECT 1 FROM `B` WHERE `B`.id = `A`.id);
```
EXISTS:
- 语法:`SELECT....FROM tab WHERE EXISTS(subquery);`
- 该语法可以理解为:将主查询的数据,放到子查询中做条件验证,根据验证结果(`true`或是`false`)来决定主查询的数据结果是否得以保留。
提示:
- `EXISTS(subquery)`子查询只返回`true`或者`false`,因此子查询中的`SELECT *`可以是`SELECT 1 OR SELECT X`,它们并没有区别。
- `EXISTS(subquery)`子查询的实际执行过程可能经过了优化而不是我们理解上的逐条对比,如果担心效率问题,可进行实际检验以确定是否有效率问题。
- `EXISTS(subquery)`子查询往往也可以用条件表达式,其他子查询或者`JOIN`替代,何种最优需要具体问题具体分析。
ORDER BY 关键字优化
尽量使用Index方式排序,避免使用FileSort方式排序
带头大哥在就不会产生FileSort
```
/* 没有使用索引进行排序 产生了Using filesort */
EXPLAIN SELECT * FROM `talA` ORDER BY `age` ASC, `birth` DESC;
```
`ORDER BY`满足两情况,会使用`Index`方式排序:
- `ORDER BY`语句使用索引最左前列
- 使用`WHERE`子句与`ORDER BY`子句条件列组合满足索引最左前列
尽可能在索引列上完成排序操作,遵照索引建的最佳左前缀原则
如果不在索引列上,File Sort有两种算法:MySQL就要启动双路排序算法和单路排序算法
1、双路排序算法:MySQL4.1之前使用双路排序,字面意思就是两次扫描磁盘,最终得到数据,读取行指针和`ORDER BY`列,対他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取对应的数据输出。一句话,从磁盘取排序字段,在`buffer`中进行排序,再从磁盘取其他字段。
取一批数据,要对磁盘进行两次扫描,众所周知,IO是很耗时的,所以在MySQL4.1之后,出现了改进的算法,就是单路排序算法。
2、单路排序算法:从磁盘读取查询需要的所有列,按照`ORDER BY`列在`buffer`対它们进行排序,然后扫描排序后的列表进行输出,它的效率更快一些,避免了第二次读取数据。并且把随机IO变成了顺序IO,但是它会使用更多的空间,因为它把每一行都保存在内存中了。
但是单路排序算法有问题:如果`SortBuffer`缓冲区太小,导致从磁盘中读取所有的列不能完全保存在`SortBuffer`缓冲区中,这时候单路复用算法就会出现问题,反而性能不如双路复用算法。
单路复用算法的优化策略:
- 增大`sort_buffer_size`参数的设置。
- 增大`max_length_for_sort_data`参数的设置。
提高ORDER BY排序的速度:
- `ORDER BY`时使用`SELECT *`是大忌,查什么字段就写什么字段,这点非常重要。在这里的影响是:
- 当查询的字段大小总和小于`max_length_for_sort_data`而且排序字段不是`TEXT|BLOB`类型时,会使用单路排序算法,否则使用多路排序算法。
- 两种排序算法的数据都有可能超出`sort_buffer`缓冲区的容量,超出之后,会创建`tmp`临时文件进行合并排序,导致多次IO,但是单路排序算法的风险会更大一些,所以要增大`sort_buffer_size`参数的设置。
- 尝试提高`sort_buffer_size`:不管使用哪种算法,提高这个参数都会提高效率,当然,要根据系统的能力去提高,因为这个参数是针对每个进程的。
- 尝试提高`max_length_for_sort_data`:提高这个参数,会增加用单路排序算法的概率。但是如果设置的太高,数据总容量`sort_buffer_size`的概率就增大,明显症状是高的磁盘IO活动和低的处理器使用率。
为排序使用索引
- MySQL两种排序方式:`Using filesort`和`Index`扫描有序索引排序。
- MySQL能为排序与查询使用相同的索引,创建的索引既可以用于排序也可以用于查询。
ORDER BY 能使用索引最左前缀
如果WHERE子句中使用索引的最左前缀定义为常量,则O
ORDER BY能使用索引
不能使用索引进行排序
- 排序不一致
- 丢失头字段索引
- 丢失中间字段索引
- 字段不是索引的一部分
- 对于排序来说,多个相等条件(a=1 or a=2)也是范围查询
GORUP BY优化
- `GROUP BY`实质是先排序后进行分组,遵照索引建的最佳左前缀。
- 当无法使用索引列时,会使用`Using filesort`进行排序,增大`max_length_for_sort_data`参数的设置和增大`sort_buffer_size`参数的设置,会提高性能。
- `WHERE`执行顺序高于`HAVING`,能写在`WHERE`限定条件里的就不要写在`HAVING`中了。
慢查询日志简介
long_query_time
值的SQL,则会被记录到慢查询日志中。long_query_time
的默认值为10,意思是运行10秒以上的语句。explain
进行全面分析。默认情况下,MySQL数据库没有开启慢查询日志,需要我们手动来设置这个参数
当然,如果不是调优需要的话,一般不建议启动该参数,因为开启慢查询日志会或多或少带来一定的性能影响。慢查询日志支持将日志记录写入文件。
SHOW VARIABLES LIKE '%slow_query_log%';
。SET GLOBAL slow_query_log = 1;
。使用该方法开启MySQL的慢查询日志只对当前数据库生效,如果MySQL重启后会失效。如果要使慢查询日志永久开启,需要修改my.cnf
文件,在[mysqld]
下增加修改参数。
# my.cnf
[mysqld]
# 1.这个是开启慢查询。注意ON需要大写
slow_query_log=ON
# 2.这个是存储慢查询的日志文件。这个文件不存在的话,需要自己创建
slow_query_log_file=/var/lib/mysql/slow.log
修改long_query_time
的时间,需要在my.cnf
修改配置文件
[mysqld]
# 这个是设置慢查询的时间,我设置的为3秒
long_query_time=3
可以使用命令修改,也可以在my.cnf参数里面修改。
假如运行时间正好等于long_query_time的情况,并不会被记录下来。也就是说,在mysql源码里是判断大于long_query_time,而非大于
等于
查出慢查询日志的总记录条数:SHOW GLOBAL STATUS LIKE '%Slow_queries%';
日志分析工具mysqldumpslow
日志分析工具mysqldumpslow
:在生产环境中,如果要手工分析日志,查找、分析SQL,MySQL提供了日志分析工具mysqldumpslow
# 1、mysqldumpslow --help 来查看mysqldumpslow的帮助信息
root@1dcb5644392c:/usr/bin# mysqldumpslow --help
Usage: mysqldumpslow [ OPTS... ] [ LOGS... ]
Parse and summarize the MySQL slow query log. Options are
--verbose verbose
--debug debug
--help write this text to standard output
-v verbose
-d debug
-s ORDER what to sort by (al, at, ar, c, l, r, t), 'at' is default # 按照何种方式排序
al: average lock time # 平均锁定时间
ar: average rows sent # 平均返回记录数
at: average query time # 平均查询时间
c: count # 访问次数
l: lock time # 锁定时间
r: rows sent # 返回记录
t: query time # 查询时间
-r reverse the sort order (largest last instead of first)
-t NUM just show the top n queries # 返回前面多少条记录
-a don't abstract all numbers to N and strings to 'S'
-n NUM abstract numbers with at least n digits within names
-g PATTERN grep: only consider stmts that include this string
-h HOSTNAME hostname of db server for *-slow.log filename (can be wildcard),
default is '*', i.e. match all
-i NAME name of server instance (if using mysql.server startup script)
-l don't subtract lock time from total time
# 2、 案例
# 2.1、得到返回记录集最多的10个SQL
mysqldumpslow -s r -t 10 /var/lib/mysql/slow.log
# 2.2、得到访问次数最多的10个SQL
mysqldumpslow -s c -t 10 /var/lib/mysql/slow.log
# 2.3、得到按照时间排序的前10条里面含有左连接的查询语句
mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/slow.log
# 2.4、另外建议使用这些命令时结合|和more使用,否则出现爆屏的情况
mysqldumpslow -s r -t 10 /var/lib/mysql/slow.log | more
由于开启过慢查询日志,开启了`bin-log`,我们就必须为`function`指定一个参数,否则使用函数会报错。
```
SET GLOBAL log_bin_trust_function_creators=1;
```
上述修改方式MySQL重启后会失败,在`my.cnf`配置文件下修改永久有效。
```
[mysqld]
log_bin_trust_function_creators=ON
```
MySQL提供可以用来分析当前会话中语句执行的资源消耗情况。可以用于SQL的调优的测量。默认情况下,参数处于关闭状态,并保存最
近15次的运行结果。
```
查看Show Profile功能是否开启
mysql> SHOW VARIABLES LIKE 'profiling';
开启Show Profile功能
mysql> SET profiling=ON;
查看结果,执行SHOW PROFILES
Duration:持续时间。
mysql> SHOW PROFILES;
可以在SHOW PROFILE中看到一条SQL中完整的生命周期
SHOW PROFILE cpu,block io FOR QUERY ;
```
`Show Profile`查询参数备注:
- `ALL`:显示所有的开销信息。
- `BLOCK IO`:显示块IO相关开销(通用)。
- `CONTEXT SWITCHES`:上下文切换相关开销。
- `CPU`:显示CPU相关开销信息(通用)。
- `IPC`:显示发送和接收相关开销信息。
- `MEMORY`:显示内存相关开销信息。
- `PAGE FAULTS`:显示页面错误相关开销信息。
- `SOURCE`:显示和Source_function。
- `SWAPS`:显示交换次数相关开销的信息。
`Show Profile`查询列表,日常开发需要注意的结论:
- `converting HEAP to MyISAM`:查询结果太大,内存都不够用了,往磁盘上搬了
- `Creating tmp table`:创建临时表(拷贝数据到临时表,用完再删除),非常耗费数据库性能。
- `Copying to tmp table on disk`:把内存中的临时表复制到磁盘,危险
- `locked`:死锁。
配置启动
编码启用
不要在生产环境中开启这个功能
命令
set global general_log=1;
set global log_output='TABLE';
select * from mysql.general_log;
读锁 共享锁
写锁 排他锁
表锁偏向`MyISAM`存储引擎,开销小,加锁快,无死锁,锁定粒度大,发生锁冲突的概率最高,并发度最低。
```
# 查看数据库表锁的命令
SHOW OPEN TABLES;
# 给mylock表上读锁,给book表上写锁
LOCK TABLE `mylock` READ, `book` WRITE;
# 查看当前表的状态
mysql> SHOW OPEN TABLES;
In_use 1 表示上锁
# 释放给表添加的锁
UNLOCK TABLES;
```
读锁案例
```
# SESSION1
SESSION1为mylock表加了读锁,可以读mylock表
SESSION1为mylock表加了读锁,不可以修改mylock表
SESSION1为mylock表加了读锁,不可以读其他的表
# SESSION2
SESSION1为mylock表加了读锁,SESSION2可以读mylock表
SESSION1为mylock表加了读锁,SESSION2修改mylock表会被阻塞,需要等待SESSION1释放mylock表
SESSION1为mylock表加了读锁,SESSION2可以读其他表
```
写锁案例
~~~
# SESSION1
SESSION1为mylock表加了写锁,可以读mylock的表
SESSION1为mylock表加了写锁,SESSION1为mylock表加了写锁,不能读其他表可以修改mylock表
SESSION1为mylock表加了写锁,不能读其他表
# SESSION2
SESSION1为mylock表加了写锁,SESSION2读mylock表会阻塞,等待SESSION1释放
SESSION1为mylock表加了写锁,SESSION2写mylock表会阻塞,等待SESSION1释放
SESSION1为mylock表加了写锁,SESSION2可以读其他表
~~~
结论
`MyISAM`引擎在执行查询语句`SELECT`之前,会自动给涉及到的所有表加读锁,在执行增删改之前,会自动给涉及的表加写锁
MySQL的表级锁有两种模式:
- 表共享读锁(Table Read Lock)
- 表独占写锁(Table Write Lock)
対`MyISAM`表进行操作,会有以下情况:
- 対`MyISAM`表的读操作(加读锁),不会阻塞其他线程対同一表的读操作,但是会阻塞其他线程対同一表的写操作。只有当读锁释放之后,才会执行其他线程的写操作。
- 対`MyISAM`表的写操作(加写锁),会阻塞其他线程対同一表的读和写操作,只有当写锁释放之后,才会执行其他线程的读写操作。
```
mysql> SHOW STATUS LIKE 'table%';
可以通过Table_locks_immediate和Table_locks_waited状态变量来分析系统上的表锁定。具体说明如下
Table_locks_immediate:产生表级锁定的次数,表示可以立即获取锁的查询次数,每立即获取锁值加1。
Table_locks_waited:出现表级锁定争用而发生等待的次数(不能立即获取锁的次数,每等待一次锁值加1),此值高则说明存在较严重的表级锁争用情况。
此外,MyISAM的读写锁调度是写优先,这也是MyISAM不适合作为主表的引擎。因为写锁后,其他线程不能进行任何操作,大量的写操作会使查询很难得到锁,从而造成永远阻塞。
```
事务的ACID:
原子性(Atomicity):事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行。
一致性(Consistent):在事务开始和完成时,数据都必须保持一致状态。这意味着所有相关的数据规则都必须应用于事务的修改,以保持
数据的完整性;事务结束时,所有的内部数据结构〈如B树索引或双向链表)也都必须是正确的。
隔离性(lsolation):数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的“独立”环境执行。这意味着事务处理过程中的中
间状态对外部是不可见的,反之亦然。
持久性〈Durable)﹔事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能够保持。
并发事务处理带来的问题
脏读(Dirty Reads)
不可重复读(Non-Repeatable Reads)
幻读(Phantom Reads)
隔商级别 | 读数据一致性 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|---|
未提交读〔 Read uncommitted ) | 最低级别 | 是 | 是 | 是 |
已提交度(Read committed | 语句级 | 否 | 是 | 是 |
可重复读( Repeatable read ) | 事务级 | 否 | 否 | 是 |
可序列化( Serializable ) | 最高级别,事务级 | 否 | 否 | 否 |
偏向`InnoDB`存储引擎,开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度最高。
`InnoDB`存储引擎和`MyISAM`存储引擎最大不同有两点:一是支持事务,二是采用行锁。
```
# 开启MySQL数据库的手动提交 需要手动写commit
mysql> SET autocommit=0;
```
无索引行锁会变表锁
varchar 没加''
间隙锁
当我们用范围条件而不是相等条件检索数据,并请求共享或者排他锁时,`InnoDB`会给符合条件的已有数据记录的索引项加锁,对于键值在条件范文内但并不存在的记录,叫做"间隙(GAP)"。
`InnoDB`也会对这个"间隙"加锁,这种锁的机制就是所谓的"间隙锁"。
危害
因为`Query`执行过程中通过范围查找的话,他会锁定整个范围内所有的索引键值,即使这个键值不存在。
间隙锁有一个比较致命的缺点,就是当锁定一个范围的键值后,即使某些不存在的键值也会被无辜的锁定,而造成在锁定的时候无法插入锁定键值范围内的任何数据。在某些场景下这可能会対性能造成很大的危害。
如何锁定一行
begin;
`SELECT .....FOR UPDATE`在锁定某一行后,其他写操作会被阻塞,直到锁定的行被`COMMIT`。
mysql InnoDB引擎默认的修改数据语句,update,delete,insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何锁类型,如果加排他锁可以使用select ...for update语句,加共享锁可以使用select ... lock in share mode语句。所以加过排他锁的数据行在其他事务种是不能修改数据的,也不能通过for update和lock in share mode锁的方式查询数据,但可以直接通过select ...from...查询数据,因为普通查询没有任何锁机制
行锁分析
```
mysql> 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`:系统启动后到现在总共等待的次数(重要)
`InnoDB`存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面所带来的性能损耗可能比表级锁定会要更高一些,但是在整体并发处理能力方面要远远优于`MyISAM`的表级锁定的。当系统并发量较高的时候,`InnoDB`的整体性能和`MyISAM`相比就会有比较明显的优势了。
但是,`InnoDB`的行级锁定同样也有其脆弱的一面,当我们使用不当的时候,可能会让`InnoDB`的整体性能表现不仅不能比`MyISAM`高,甚至可能会更差。
复制的基本原理
MySQL复制过程分为三步:
- Master将改变记录到二进制日志(Binary Log)。这些记录过程叫做二进制日志事件,`Binary Log Events`;
- Slave将Master的`Binary Log Events`拷贝到它的中继日志(Replay Log);
- Slave重做中继日志中的事件,将改变应用到自己的数据库中。MySQL复制是异步且串行化的。
slave会从master读取binlog来进行数据同步
复制的基本原则
每个slave只有一个master
灭个slave只能有一个唯一的服务器ID
每个master可以有多个slave
复制的最大问题
延时
一主一从常见配置
Master和Slave的MySQL服务器版本一致且后台以服务运行。
主从配置都是配在[mysqld]节点下,都是小写
主机修改my.ini配置文件
从机修改my.cnf配置文件
重启后台mysql服务
关闭防火墙
测试主从复制
停止主从复制功能