【数据库基础干货】MySQL基础及慢查询优化实践

知识储备

内存数据库:速度快,成本高,索引:Map、平衡树、T树 ,应用Redis、Memchached
磁盘数据库:速度较快,成本适中(虚拟机,物理机),索引哈希,B+ ,应用:MySQL、Oracle
分布式数据库:速度较慢,容量大,存储:竖表、HDFS

SQL执行阶段

  1. 客户端发送一个查询给服务器,到达连接器
  2. 服务器先检查缓存,如果命中了缓存,立即返回结果。
  3. 到达分析器服务器端进行SQL的解析,生成解析树。
  4. 再由优化器生成对应的执行计划。
  5. 根据执行计划,调用具体存储引擎(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、范式:确保每列的原子性(不可分解)、唯一性
反范式:通过冗余字段,提高查询效率

应用层

换存储引擎、缓存
业务逻辑优化

基础规范

  1. 使用InnoDB存储引擎:支持事务、行级锁、并发性更好
  2. 数据表、数据字段必须加入中文注释,时间长了会忘记字段精确含义
  3. 禁止使用存储过程,视图,触发器,Event,数据库目的是存储和索引,计算逻辑应该放到服务层
  4. 禁止存储大文件和大照片,存储在文件系统中,数据库里存URI即可

建表规范

  1. 字段设计为NOT NULL,NULL的列使索引/索引统计/值比较都更加复杂,存储空间大,处理时需要用is null 、is not null
  2. 必须有主键,主键递增提高数据行写入效率,且主键选择较短数据类型,可以减少索引磁盘空间,建议增加xxx_create ,xxx_update字段
  3. 不要使用外键,外键会导致表与表之间耦合,更新、删除操作都会涉及到关联的表,不仅影响性能还可能造成死锁
  4. 使用varchar(20)存储手机号,因为涉及到国家会有符号(±),varchar支持模糊查询 ‘like 173’
  5. 单表大小不要超过1千万行

索引规范

  1. 单表索引数量控制,一般在5个以内
  2. 联合索引和覆盖索引是两个方面,联合索引是一种索引类型,覆盖索引是一种最优情况,不用回表
  3. 联合索引需要前导列,覆盖索引就是索引既数据即可,可能只用了一个字段的索引

联合索引

对(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在这里可以视为等值引用,不会中止索引匹配


哪些情况不适合建索引

  1. 禁止在更新频繁、区分度不高的属性字段上建立索引,更新会改变B+树结构
  2. “性别”这种区分度不高的属性建立索引没有意义
  3. Where 条件用不到的字段不创建索引
  4. 表记录太少
  5. 经常增删的表

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,索引)