数据库基础干货MySQL基础及慢查询优化实践
Posted 在路上的德尔菲
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据库基础干货MySQL基础及慢查询优化实践相关的知识,希望对你有一定的参考价值。
知识储备
内存数据库:速度快,成本高,索引:Map、平衡树、T树 ,应用Redis、Memchached
磁盘数据库:速度较快,成本适中(虚拟机,物理机),索引哈希,B+ ,应用:mysql、Oracle
分布式数据库:速度较慢,容量大,存储:竖表、HDFS
SQL执行阶段
- 客户端发送一个查询给服务器,到达连接器。
- 服务器先检查缓存,如果命中了缓存,立即返回结果。
- 到达分析器服务器端进行SQL的解析,生成解析树。
- 再由优化器生成对应的执行计划。
- 根据执行计划,调用具体存储引擎(InnoDB)将结果返回给客户端。
InnoDB引擎为什么要使用B+数据
1、每个节点(数据页)为16K,叶子节点存储数据。
2、多叉树,减少层高,减少IO,一般只需要3~4次IO即可。
3、叶子节点间存在顺序指针。
4、B+主索引存所有数据,B树辅助索引只存主键ID。
利用索引的好处是什么?
1、减少服务器扫描的数据量,提高查询效率
2、服务器避免排序和临时表
分析原因及解决套路
Explain用法
- table : 查询哪个表
- rows : 预估扫描的行数
- ref : 哪个字段和key一起使用
- key : 使用的索引
- possible_keys :可用的索引,不止一个
- key_len :索引中使用的字节数,长度越短越好
- type : ALL(扫描全表)、index(扫描全索引)、range(范围索引)、ref(索引,匹配单值)、const(主键或唯一键,只匹配一行)
- filtered :此查询条件所过滤数据百分比
- extra:额外信息eg: Using filesort(外部的索引排序而不是按照表内的索引顺序进行读取)、Using index condition 、Using where 、Using Temporary(临时表,常见于order by 和group by)
慢查询治理思路
索引层面
1、 没有建立索引
2、索引失效
select * from ShopTable where shopid > 10000000 VS where shopid > 50000000
虽然给shopid 加了一般索引,但执行where shopid > 10000000 没有使用到索引,回表(即从索引查出是哪一行,再回到表中取出所有字段返回)导致全表扫描当需要回表的数据量大于20%,MySQL会认为全表扫描会更快,故不走索引。
建议:
- 减少使用select * ,索引覆盖所需要返回的字段,即索引列和查询列一致
- 减少数据量,数据量过大时考虑分页
3 、使用索引不全面
where shopid > 234512 and update_days > -1 and buy_time > 300
只用到shopid的索引,索引效率低,查询到后还需要回表再过滤,导致效率严重下降
建议:
增加update_days 和 buy_time字段的索引
4、Order By /Group By /Distinct 避免临时表
SQL层面
避免不正确或低效的写法、避免复杂的SQL
1、limit 6000000 , 10
limit过大 ,limit A,B 会扫描 X+Y条数据,即当X过大时,扫描行数多导致慢查询
建议:
尽量使用id > xx LIMIT 10,建议分页遍历使用ID
2、
一般索引 KEY shopid
一般索引 KEY poiid
组合索引 KEY combined(shopid,poiid)
使用时只触发了索引merge i2 && KEY combined
建议:
删除Key shopid索引,减少索引占用空间
3、一个复杂SQL 还是多个简单SQL
表库层
1、查看表状况 show table status like ‘table_shop’
Rows : 180000000
Data_length : 30G
单表数据量过大,建议单表不超过1千万行
2、是否必须走主库
Master-Slave架构,主库单节点,容易成为资源瓶颈
3、范式:确保每列的原子性(不可分解)、唯一性
反范式:通过冗余字段,提高查询效率
应用层
换存储引擎、缓存
业务逻辑优化
基础规范
- 使用InnoDB存储引擎:支持事务、行级锁、并发性更好
- 数据表、数据字段必须加入中文注释,时间长了会忘记字段精确含义
- 禁止使用存储过程,视图,触发器,Event,数据库目的是存储和索引,计算逻辑应该放到服务层
- 禁止存储大文件和大照片,存储在文件系统中,数据库里存URI即可
建表规范
- 字段设计为NOT NULL,NULL的列使索引/索引统计/值比较都更加复杂,存储空间大,处理时需要用is null 、is not null
- 必须有主键,主键递增提高数据行写入效率,且主键选择较短数据类型,可以减少索引磁盘空间,建议增加xxx_create ,xxx_update字段
- 不要使用外键,外键会导致表与表之间耦合,更新、删除操作都会涉及到关联的表,不仅影响性能还可能造成死锁
- 使用varchar(20)存储手机号,因为涉及到国家会有符号(±),varchar支持模糊查询 ‘like 173’
- 单表大小不要超过1千万行
索引规范
- 单表索引数量控制,一般在5个以内
- 联合索引和覆盖索引是两个方面,联合索引是一种索引类型,覆盖索引是一种最优情况,不用回表
- 联合索引需要前导列,覆盖索引就是索引既数据即可,可能只用了一个字段的索引
联合索引
对(a,b,c)进行联合索引,判断是否走索引问题
a = 1 //true
a = 1 and b = 2 //true
b = 2 and a = 1 //true,Mysql有优化器会自动调整a,b的顺序与索引顺序一致,注意把区分度高的字段放在前面
b = 1 //false,
a = 1 and b = 2 and c > 3 and d = 4 //a,b,c三个字段能用到索引,而d就匹配不到,最左匹配原则遇到范围查询就停止匹配
如何建联合检索问题
SELECT * FROM table WHERE a = 1 and b = 2 and c = 3; // (a,b,c) (b,a,c) (c,a,b)都可以,将区分度高的字段放在前列
SELECT * FROM table WHERE a > 1 and b = 2; // (b,a)建立索引,优化器会帮我们调整where后a,b的顺序
SELECT * FROM `table` WHERE a > 1 and b = 2 and c > 3; // (b,a) 或(b,c)都可以的
SELECT * FROM `table` WHERE a = 1 ORDER BY b; // 对(a,b)建索引
SELECT * FROM `table` WHERE a IN (1,2,3) and b > 1; //对(a,b)建立索引,因为IN在这里可以视为等值引用,不会中止索引匹配
哪些情况不适合建索引
- 禁止在更新频繁、区分度不高的属性字段上建立索引,更新会改变B+树结构
- “性别”这种区分度不高的属性建立索引没有意义
- Where 条件用不到的字段不创建索引
- 表记录太少
- 经常增删的表
SQL注意事项
- 禁止在where条件的列名上使用函数、计算、类型转换
- 禁止使用左模糊或全模糊查询,如like ‘%abc’ ,like ‘%abc%’
- 少用or
- is null ,is not null 也会无法使用索引
- 禁止使用大偏移量的limit分页
- 禁止3张表以上做join
推荐学习:
https://ipu.sankuai.com/app/course/detail/1927
高性能SQL 和 MySQL官方文档(https://www.mysql.com)
以上是关于数据库基础干货MySQL基础及慢查询优化实践的主要内容,如果未能解决你的问题,请参考以下文章