MySQL数据库高级
本文最后更新于:3 年前
Mysql优化先康康嗷,我裂开来,Docker看的脑阔疼,学完这个再去学K8S嗷!!!
Mysql的Linux版的安装:
Ubuntu20.04中Mysql修改用户密码,yyds!!!
- 还有什么开机自动启动啊,字符集啊,安装位置验证啊等一系列的验证和配置工作嗷!!!
- Mysql相应目录详解:
1 |
|
- 修改字符集:
- 大问题啊兄弟!!!
- 先复制一份出来,再去修改,不然你昨晚的时间就白浪费了orz。
1 |
|
- 配置文件处于:
1 |
|
中的my.cnf嗷!!!
修改Mysql的默认编码哈!!!
查看编码:
1 |
|
可以看出,上面的默认都是utf-8嗷!!!
在修改字符集编码的时间点之前创建的库,字符集可能就固定了嗷。在修改字符集编码的时间点之后创建的库,才能够按照全新配置文件来创建呀!!!注意一下,更改完配置文件之后,要将mysql重启,才能够起到更新的目的嗷!!!
主要配置文件:
上面这个就是一系列的主要配置文件嗷!!!
- frm是图书馆,myd就是书,myi就是书的编号嗷!!!
初识Mysql架构:
Mysql架构可以在不同的场景中应用并发挥良好的功能,主要体现在存储引擎的架构上。插件式的存储引擎将查询处理和其他的系统任务以及数据的存储提取相分离。
出问题的时候,根据不同的层去做不同的优化行为和优化措施嗷!!!
具体分层:
- 连接层:
- 服务层:
- 引擎层:
最常用就是:InnoDB和MyISAM
- 存储层:
查看搜索引擎:
1 |
|
1 |
|
上面可以查看默认搜索引擎嗷!!!
MyISAM和InnoDB的区别:
- 阿里巴巴大部分Mysql数据库其实使用的percona的原型加以修改。(把InnoDB给改了orz)
- AliSql和AliRedis很猛嗷!!!
索引优化:
优化分析:
性能下降SQL慢:
- select语句写的烂
- 索引失效(建了索引没用上):
- 单值索引:
- 只用了该表的某一个字段建立索引
- 复合索引:
- 使用多个字段建立索引
- 单值索引:
- 关联查询的join太多了(设计缺陷或者不得已的需求)
- 服务器调优以及各个参数设置(缓冲,线程数等)
常见通用的Join查询:
- 手写和机读是不一样的嗷!!!
- Mysql是先从FROM开始执行SQL的嗷!!!
所有的七种Join关系:
select * from table A inner join table B on A.Key = B.Key
select * from table A left join table B on A.Key = B.Key
select * from table A right join table B on A.Key = B.Key
select * from table A left join table B on A.Key = B.Key where B.Key is NULL
select * from table A right join table B on A.Key = B.Key where A.Key is NULL
select * from table A full outer join table B on A.Key = B.Key
select * from table full outer join table B on A.Key = B.Key where A.Key is NULL or B.Key is NULL
七种Join关系的编写:
- Inner join:
1 |
|
- 中间只有id相等的才同时出现嗷!!!我们获得是两者相等的公有部分嗷!!!
- left join:
1 |
|
- 左表全都有,右表没有的补上NULL
- right join:
1 |
|
- 右表全都有,左表没有的补上NULL。左右表如果有多个匹配上的要全排列嗷!!!
- 最好理解为加法,inner join加上左边或者右边没有匹配上的嗷!!!
- left join + null:
1 |
|
- right join + null:
1 |
|
- FULL OUTER JOIN:
- 吐了兄弟,上面这种语法Oracle支持,Mysql不支持嗷!!!
- 拼一拼呗~
1 |
|
- 相当于左边一整个圆加上右边一整个圆,然后把中间公共部分给去重而已嗷!!!Union就有这个功效嗷,天生自带拼接去重嗷!!!!!
- A独有的 + B独有的:
- 这个和上面这个十分类似嗷!!!用Union进行拼接而已嘛!!!
1 |
|
- 和拼积木一样嗷,第一条就是左半边,第三条就是右半边,第二条和胶水一样拼接起来嗷!!!
索引简介:
什么是索引?
- 官方定义:Index是帮助Mysql高效获取数据的数据结构。
- 例如查找mysql , 先找m,再找到y,接着找sql
- 可以理解为“排好序的快速查找数据结构”
- 索引两大功能:
- 查找
- 排序
- 二叉查找树:
- B树:
- 官方定义:Index是帮助Mysql高效获取数据的数据结构。
优势:
- 提高数据检索的效率,降低数据库的I/O成本。
- 对于数据进行了排序,降低数据排序的成本,降低了CPU的消耗。
劣势:
- 索引本质上也是一张表,要占空间的嗷!!!
- 降低了更新表的速度,因为要保存数据的同时,也要更新索引的字段。
- 花时间研究建立最优秀的索引嗷!!!
索引分类:
- 单值索引:
- 一个索引包含单个列,一个表可以有多个单列索引。(一张表建议不超过5个索引嗷!!!)
- 唯一索引:
- 索引列的值必须唯一,允许有控制。(unique)
- 复合索引:
- 一个索引包含多个列
- 单值索引:
基本语法:
Mysql索引结构:
- 结构分类:
- BTree索引
- Hash索引
- full-text全文索引
- R-Tree索引
- 磁盘索引原理:
就是操作系统里面介绍的,非叶子结点相当于存了参考值和指针。叶子结点存的才是数据嗷!!!
建索引:
- 主键自动建立唯一索引
- 频繁作为查询条件的字段
- 查询中和其他表关联的字段,外键关系建立索引
- 频繁更新的字段不适合建索引
- Where里面用不到的字段不创建索引嗷!
- 高并发下倾向于创建组合索引嗷!!!
- 排序字段如果通过索引取访问将大大提高排序速度嗷!!!
- 查询中统计或者分组字段(内部也要用到排序嗷!!!)
不建索引:
- 表记录太少(300万条数据出现性能下降)
- 表经常需要增删改查!
- 数据重复并且平均的表字段,注意,如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果嗷!!!(数据的差异率不高就没有必要建立嗷!!!)
性能分析:
- MySQL Query Optimizer
- MySQL常见瓶颈:
- CPU负担重
- IO负担重
- 服务器硬件的性能瓶颈
Explain:
- 用法:
explain + SQL语句
- 能干吗?
字段解释:
1 |
|
Id:
select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序
三种情况:
- ID相同,执行顺序是从上到下嗷!!!
- ID越大,越先被执行嗷!
- ID相同不同,同时存在:
遵循上面的规则,数字大的先走,数字相同的从上往下执行。
select_type:
- simple:简单查询,不包含子查询或者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
上面的比较常用嗷,下面这个看看就得了嗷!!!
一般来说至少达到range级别,最好达到ref
具体解释:
- system:表只有一行记录(相当于系统表),这是const类型的特例,平时不会出现,这个也可以忽略不记。
- const:通过一次索引就找到了,const用于比较primary key或者unique索引。因为只匹配一行数据,所以很快将主键置于where列表中,MySQL就能将查询转换为一个常量。
- eq_ref:唯一性索引扫描,每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描!!!
- ref:非唯一性索引扫描,本质上是一种索引匹配。它返回所有匹配某个单独值的行,然而,它可以找到多个符合条件的行,所以他应该属于查找和扫描的混合体。
- range:只检索给定范围的行,key列显示使用了哪个索引,一般就是你的where语句中出现了between , < , > , in等的查询。这种范围扫描这种范围扫描索引会比全表扫描要好,因为它只需要开始于索引的某一点,而结束语另外一点,不用扫描全部索引嗷!!!
- index:Index与ALL区别为index只遍历索引树,通常比ALL快。ALL和Index都是读整张表,Index是从索引中读取的,而All是从硬盘中读取的嗷!!!
这里要求select的东西是建立了索引的,不然就会默认从磁盘里去读取嗷!!!
- full:Full Table Scan,将遍历全表以找到匹配的行。
- 备注:一般来说,得保证查询至少要达到range级别,最好能达到ref。
possible_keys:显示可能应用在这张表中的索引,有一个或者多个。查询涉及到的字段上若存在的索引,则该索引被列出,但是不一定被查询实际使用嗷!!!
key:表示实际使用的索引,如果为NULL,则没有使用索引嗷!!!如果查询中使用了覆盖索引,则该索引仅仅出现在key列表中!!!
- 覆盖索引是啥?
- select查询的字段和我们建立的复合索引的个数和字段一一致。刚好Match上了嗷!!!刚好吻合的时候,就可以从索引上取嗷!!!
- 说白了possible_keys没有单独的col1和col2,复合主键col1和col2都不是单独的posstible_keys,所以possible_keys为NULL。但是!!!你符合我们复合索引的要求,因此通过复合索引来取得对应字段的值嗷!!!
key_len:
- 表示索引中使用的字节数,可以通过该列计算查询中使用的索引的长度。准确度一定的情况下,长度越短越好。
- key_len显示的值为索引字段的最大可能长度,并非实际使用长度。即key_len是根据表定义计算而得到,不是通过表内检查得出的嗷!!!
ref:
- 显示哪一列被使用了。如果可能的话,是一个常数。哪些列或者常量被用于索引列上的值。
rows:
- 根据表统计信息以及索引选用的情况,大致估算出找出所需的记录所需要读取的行数嗷!!!
- 用的越少越好嗷!!!
extra:
包含十分重要的额外信息。
分类:
- Using filesort:用不上索引,只好自己内部排序。很危险,九死一生。
Using temporary:十死无生。使用临时表处理数据,常见于group by 和 order by
- 一般来说哈,group by非常常用也非常容易拖慢性能,尽量按照我们索引建立的顺序来使用group by 嗷!!!
Using where:使用了覆盖索引,避免访问了表的数据行,效率不错。如果没有同时出现using where,表明索引用来读取数据而非执行查找动作嗷!!!
- Using join buffer:使用了连续缓存
- impossible where:出问题咯
- Select tables optimized away:
- distinct:
优化distinct操作,在找到第一匹配的元组后即停止找同样的值的动作嗷!!!
- 前三个是最重要的嗷!!!
索引优化:
索引分析:
单表:
1 |
|
明显看出,全表扫描和order by导致的using filesort都出现了,功能实现了但是性能上有欠缺嗷!!!
- 索引:
只有primary key自带的索引
建立索引:
where后面使用的条件要建立索引嗷!!!
mysql> create index idx_article_ccv on article(category_id,comments,viewws); Query OK, 0 rows affected (0.03 sec) Records: 0 Duplicates: 0 Warnings: 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
![image-20210824095219934](https://cdn.jsdelivr.net/gh/alexanderliu-creator/cloudimg/img/20210824095220.png)
- 建立后的结果:
![image-20210824095640597](https://cdn.jsdelivr.net/gh/alexanderliu-creator/cloudimg/img/20210824095641.png)
索引建立完成之后,范围可能会导致索引失效嗷!!!
但是上面是我们的业务,虽然解决了全表扫描问题,但是仍然性能不佳,不符合我们的选择嗷,我们删掉它。
![image-20210824100110352](https://cdn.jsdelivr.net/gh/alexanderliu-creator/cloudimg/img/20210824100110.png)
- 大胆假设:
- 既然中间的那个范围会让索引失效,那我们干脆绕开它康康???
- ```mysql
mysql> create index idx_article_cv on article(category_id,views);
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> explain select id,author_id from article where category_id=1 and comments > 1 order by views desc limit 1;
+----+-------------+---------+------------+------+----------------+----------------+---------+-------+------+----------+----------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+----------------+----------------+---------+-------+------+----------+----------------------------------+
| 1 | SIMPLE | article | NULL | ref | idx_article_cv | idx_article_cv | 4 | const | 1 | 33.33 | Using where; Backward index scan |
+----+-------------+---------+------------+------+----------------+----------------+---------+-------+------+----------+----------------------------------+
1 row in set, 1 warning (0.00 sec)芜湖,没有using filesort了嗷!!!
双表:
- 左外连接和右外连接,
1 |
|
重点在于全表扫描嗷!!!那么索引建在哪个表上呢???
- 建在右表的card上:
- 建在左表的card上:
- 结论:
左右连接相反加,因为左右连接的特性。比如左连接,左边一定都有的,重要的是从右边索引的特性嗷,所以要在右边建立索引。
- 左连接加右表上,右连接加左表上嗷!!!
三表:
- 三表SQL建在哪个字段上?
- 和双表一样的嗷!!!
- 结论:
类似于用书籍类别来驱动书籍。
索引优化重点:
避免索引失效:
1. 全值匹配我最爱:
- 复合索引是阶梯式的,Name -> Age -> Pos
- 只有Name都可以使用到一部分的复合索引,但是如果没有Name,直接上Pos或者Age。索引根本就用不上,索引失效了嗷!!!
- 由此引申出了下面的内容。
2. 最佳左前缀法则:
上面这个只用到了Name的所有,Pos用不到索引嗷!!!
索引多列的时候,最佳做前缀法则,查询从索引的最左前列开始,并且不跳过中间的索引。
第一句口诀:带头大哥不能死嗷!!!
第二句口诀:中间兄弟不能断嗷!!!
说白了,你想要借用复合索引,你就要根据我从前到后索引生效的顺序来。中间如果断了,我只保证前面的生效,后面的就不一定了嗷!!!
3. 不在索引列做任何操作:
- 包括:
- 计算
- 函数
- 自动或手动的类型转换
- 上面这些情况会导致索引失效而转向全表扫描。
4. 存储引擎不能使用索引中范围条件右边的列:
- 下面那个使用顺序很关键,Name用于检索查询,age被用于排序,之后的pos根本没被用上嗷!!!
- 范围之后,全失效!!!
5. 尽量覆盖索引,减少select *:
你查的东西最好和索引设置的一样,尽可能多去覆盖嗷!!!
6. 使用不等于的时候无法使用索引:
- 会导致全表扫描嗷!!!
7. IS [NOT] NULL也无法使用索引:
- 其实给了我们业务一个提示,就算某个值为空,也最好给一个默认的default值,为NULL的话会影响到查询和检索的效率嗷!!!
8. 模糊查询:
百分like放右边!!!
如何解决
%字符串%
时索引不被使用的方法???- 使用覆盖索引的方法,人为去建立复合索引
- 然后查询的时候,使用覆盖索引来查询嗷!!!
只要我们select查询的所有字段都在我们建立的索引的覆盖之下,或者实际上利用了我们的索引(例如利用索引去获取ID),都是覆盖所有嗷!!!
9. 字符串不加单引号导致索引失效:
- 重罪,Varchar类型不能时区单引号嗷!!
本质上,mysql底层自动帮助我们做了转型,这违反了上面的第3条。要避免出现内部对于数据类型发生的隐式的操作嗷!!!开发的时候,这是重罪!!!
10. 少用or来连接,会导致索引失效:
11. 小总结:
- 带头大哥不能死
- 中间兄弟不能断
- 索引列上不计算
- Like百分加右边
- 范围之后全失效
- 字符串里有引号
- 小练习:
- 题目练习:
最好是第一种嗷!!!上面这些都可以使得索引生效时因为,Mysql的优化器在底层做了调整嗷!!!我们可以按照规矩来写,尽量减少调整所花费的时间嗷!!!
优化器yyds,优化器会在底层自动帮助我们进行调优,调整顺序,尽可能去符合我们的索引嗷!!!
梯子没接上?就是这种感觉嗷!!!
上面这个东西哈,group by的法则和order by 非常类似,分组排序嘛!!!
group by基本上都需要进行排序,会有临时表的产生嗷!!!
- 范围还是有不一样的嗷!!!
- 口诀:
一般性建议:
查询截取分析:
查询优化:
小表驱动大表:
- in 和 exist 的使用都是根据情况来小表驱动大表。 查询结果都是一样样的嗷!!!
Order By排序优化:
- Order by尽量使用Index方式排序,避免使用FileSort方式排序。
- 现在的中心放在,在Order by后面使用什么,会不会出现FileSort嗷!!!
高高低低,梯子不平,走不顺,FileSort嗷!!!
FileSort和Index两种:
- 提高Order By的速度:
Mysql的两种排序方式:
- 文件排序
- 扫描有序索引排序
Mysql能够为排序和查询使用相同的索引嗷!!!
能够使用索引的情况:
- 不能使用索引的情况:
- summary:
不用记这么多的,其实就是可以康康,是不是“连起来”,比如下面的最后一个。a in (…)是一个range,无效嗷!!!a相当于裂开了,b,c就不得行嗷!!!
再比如上面的最后一个,a没问题,b>const裂开了,但是后面的是b,c。刚好和a构成了a,b,c,于是没有问题嗷!!!
Group By排序优化:
- 和上面的几乎没啥区别,就多了一个东西嗷!!!
慢查询日志:
- 记录运行时间超过long_query_time的sql,被记录到慢查询日志中去。
- 默认意思是运行查过10s以上的语句嗷!!!
- 可以查看哪些SQL超过了我们的最大忍耐时间,再结合explain,对于特定的SQL进行全面分析即可嗷!!!
- 默认慢查询日志没有开启,如果没有调优的需要的话,一般不建议启动该参数。开启慢日志或多或少会带来一定的性能影响。
- 哪些SQL会被记录呢?
这里记录的值,是判断大于long_query_time,而不是大于等于嗷!!!
设置以后查看发现还是10秒???
- 如何演示控制睡眠?
select sleep(n seconds)
- 那么如何使用配置文件来实现上述这些配置呢?
- 日志分析工具mysqldumpslow:
批量数据脚本:
- 定义函数插入数据:
Show Profile:
- mysql提供的可以用来分析当前会话中语句执行的资源消耗情况,可以用于SQL的调优测量。
- 默认处于关闭状态,保存最佳15次的运行结果。
- 查看mysql版本是否支持:
- 开启功能:
- 运行SQL并且验证:
这里就指的是上面的3号语句
- show profile所能用到的参数:
- 常见的大问题!!!
化验单例子:
- Summary:
全局查询日志;
只能够在测试环境用,不能在生产环境使用嗷!!!永远不要在生产开启这个功能嗷!!!
打开全局查询日志:
数据库锁理论:
- 锁可以对于有限的资源进行保护,解决隔离和并发的矛盾。
- 锁的分类:
- 数据操作:
- 读锁:共享锁
- 写锁:排它锁
- 数据操作:
- 表锁:偏向于MyISAM存储引擎,粒度大
- 行锁:
- 页锁
- 数据操作:
MyISAM表锁:
- 创建表:
- 手动增加表锁:
lock table 表名字 read(write), 表名字2 read(write)
- 查看表上加过的锁:
show open tables
- 手动释放锁:
unlock tables
上面这个可以释放所有表中的锁嗷!!!
案例1:
Session1加读锁,Session2不管,尝试实验。
和上面说的一样,共享锁可以共享读但是不能够写嗷!!!锁住了,别的人和自己都不能写嗷!!!
一个表加读锁,别的表的数据也读取不到嗷!!!你只有把当前的锁放了,你才能去读别的嗷!!!你把帐给我了结了,你再去干别的事儿嗷!!!
一个会话加了表级别的读锁,另外的会话如果想去向这个表该数据,阻塞了!!!,这就会导致出大问题嗷!!!
读,阻塞,写的例子嗷!!!
summary:
案例2:
Session1加写锁,Session2不管,尝试实验。
自己加锁,自己能读并且自己能改嗷!!!
自己加锁之后,读不了其他表嗷!!!
读取加了写锁的的表会阻塞嗷!!!
summary:
小总结:
- 分析锁定的表:
- MyISAM就是因为表锁的锁定,而适用于读取,而不是读取和修改嗷!!!
InnoDB行锁:
- 粒度小,并发高嗷!!!
- 与MyISAM最大的不同点:
- 支持事务的概念
- 采用了行级锁
- ACID:
- 原子性
- 一致性
- 隔离性
- 持久性
- 并发事务带来的问题:
- 脏读:
- 读取到了已经修改但是尚未提交的数据(中间数据)
- 更新丢失
- 不可重复读:
- 两次读取结果不一致
- 幻读:
- 两次读取,读取发现数据增多了
- 脏读:
- 默认隔离级别是可重复读嗷!!!
- 四个级别:
- 读未提交
- 读已提交
- 可重复读
- 序列化
- 默认即使RR -> 可重复读嗷!!!
案例:
- 多个Session去操作同一行,第一个操作的那个会获得锁,后面的其他Session都会阻塞嗷!!!
- 但是如果多个Session操作的是不同行,获得的是不同行的锁,就没问题嗷!!!
- AutoCommit的关闭而导致的,不commit的话,就没有办法及时获得别的Session更新后的结果的情况嗷!!!
WHY????? —–> 可重复读哇,在我的一个事务中,我读取的某个变量的值应该是一定的嗷!!!所有只有commit之后,开启了一条新的事务,才能够重新获得新的值嗷!!!
索引失效:
- 导致行锁升级为表锁嗷!!!
- 所以说,开发过程中,这一类的Bug是最难找的嗷!!!varchar类型赋值的时候写成数字,就会gg!!!是重罪!!!
- 只有左边提交了,右边的阻塞才能够结束嗷!!
间隙锁危害:
如何锁定一行:
- 这里经过测试之后证明,左边锁定了这一行之后,右边仍然可以读取这一行的数据,但是右边是不可以对于这一行做修改的嗷!!!
- 实现了行级锁定,虽然锁定机制的性能损耗可能高一些。但是在并发处理能力远远优于MyISAM的表级锁定嗷!!!但是如果使用不当,性能甚至可能比MyISAM更差哦!
- 一个命令:
show status like 'innodb_row_lock%';
比较重要的是:
- 优化建议:
- 建议所有数据检索通过索引来完成,避免无索引行锁升级为表锁。
- 合理设计索引,尽量缩小锁的范围。
- 避免间隙锁,减少检索条件。
- 尽量控制事务大小,减少锁定资源量和时间长度。
- 尽可能低级别的事务隔离。
主从复制:
- Window上面一台,Linux上面有一台
复制基本原理:
- slave会从master读取binlog来进行数据同步。
- 基本原则:
- 一个slave只有一个master
- 一个master可以有多个slave
- 每个slave只能有一个唯一的服务器ID
- 复制的问题:可能会有时延嗷!
一主一从:
- 要求:
- mysql版本一致且在后台以服务运行
- 统一网段,而且ping的通嗷!!!
- Windows做主机,Linux做从机(要求两边能够相互Ping通嗷!!!)
主机:
- 修改my.ini文件,[我的主机中,配置文件所在的位置](C:\ProgramData\MySQL\MySQL Server 8.0)
- 配置如下:
- 可选的可以都不用咋调整嗷!!!
- 配置完成之后重启嗷!!!
从机:
- 修改my.cnf配置文件嗷!!!
- 配置完成之后重启嗷!!!
主机建立账户并授权slave:
- 允许服务器以zhangsan用户连接并完成主从复制嗷!!!
- show master status:
第一个代表文件名称,第二个代表开始复制的位置嗷!!!Do没有,代表都要复制。Ignore代表忽略,不需要复制的数据库。
从机完成相应的配置:
- 第一行就是上面这一行嗷!!!主要是完成主机连接的一些设置而已嗷!!!
停止从机:
错误处理:
- 随着写入数据的变化,master status也会有变化,会出一些问题。
- 所以每次重新开始同步的时候,都需要先查看一下master status,再进行同步嗷!!!