SQL设计与优化



基本原理

MYSQL架构


SQL Layer,下层是各种各样的对上提供接口的存储引擎,被称为 Storage Engine Layer



SQL Layer



TODO


Storage Engine Layer



主要引擎




引擎

介绍

使用场景

InnoDB

采用MVVC支持高并发,实现四个标准隔离级别,默认是REPEATABLE READ,



表基于聚簇索引建立,支持行级锁

5.5+版本默认存储引擎,事务型存储引擎。能够符合MYSQL大多数使用场景

MyISAM

不支持事务和行级锁,崩溃后无法修复,

5.5以下版本默认引擎

Memory

支持hash索引,标记所,并发写入性能低,内存表,重启丢失

  • 用于查找映射表
  • 缓存周期性聚合数据结果
  • 保存数据分析产生中间数据





SQL执行流程



以查询语句为例



  1. 客户端发送一条查询给服务器
  2. 服务器先检查缓存,如果命中缓存,检查是否有权限,有权限返回存在缓存中的结果,否则进入下一个阶段
  3. 服务器执行SQL解析,预处理,再由优化器生成对应的执行计划
  4. MYSQL根据优化器生成的执行计划,调用存储引擎的API执行查询
  5. 将结果返回给客户端








SQL解析顺序

SELECTDISTINCT< select_list >FROM< left_table >< join_type >JOIN< right_table >ON< join_condition >WHERE< where_condition >GROUPBY< group_by_list >HAVING< having_condition >ORDERBY< order_by_condition >LIMIT< limit_number >复制代码




1. FROM
2. ON
3. JOIN
4. WHERE <where_condition> 
       
5. GROUP BY
6. HAVING
7. SELECT
8. DISTINCT <select_list 
        >
9. ORDER BY <order_by_condition> 
       
10. LIMIT <limit_number>


索引



索引是存储引擎用于快速找到记录的一种数据结构,索引优化时对查询性能优化最有效的手段,索引能够轻易将查询性能提高几个数量级,当表数据越来越大,索引对性能愈发重要,当数据量较少且负载较低是,不恰当的索引对性能影响不明显,但当数据量逐渐增大时,性能则急剧下降。



索引作用:



  • 随机IO转化为顺序IO
  • 减少服务器需要扫描的数据,减少IO
  • 减少内存计算(比较、排序、临时表)
索引类型




MYSQL索引简介.xmind(88.23 kB)



聚簇索引



B-Tree索引和数据行,当表有聚簇索引时,它的数据实际存储在索引的叶子页上,InnoDB是将通过主键聚簇数据(主键列),如果没有定义主键,InnoDB会选择一个唯一的非空索引代替。如果没有这样的索引,InnoDB会隐式定义一个主键作为聚簇索引。








优点:



  • 数据访问更快,聚簇索引和数据保存在B+Tree中,因此查找更快
  • 覆盖索引扫描直接使用页节点中的主键值
  • 相邻键值数据存放在一起
  • 提高I/O密集型应用的性能,假设将数据全部放内存上,则访问顺序没有那么重要,聚簇索引就没有优势

缺点:



  • 插入数据严重依赖插入顺序,如果不是按照主键顺序插入顺序,加载完成后使用OPTIMIZE TABLE重新组织下表
  • 更新聚簇列的代价很高,导致"页分裂"
  • 更新插入都可能导致"页分裂"
  • 可能导致全表扫描变慢,行稀疏,页分裂导致存储不连续的时候
  • 二级索引会比较大
  • 二级索引访问需要两次索引查找
聚簇索引和非聚簇索引对比






MYISAM按照数据插入顺序存储在磁盘上,内部维护一个行号,叶子节点=索引列值+ 行(内部行号+数据)




InnoDB 主键索引(聚簇索引)每一个叶子节点都包含主键值,事务ID,用于事务和MVCC的回滚指针和其他剩余列



二级索引叶子节点存储的是主键值









设计规范

优化法则



SQL优化实践关键点在于减少磁盘I/O,减少网络传输,降低CPU和内存开销和加机器






表结构设计



建议:



  • 更小通让更好,够用就好
  • 适当冗余字段,不要使用多表join查询
  • 选择合适的数据类型
  • 使用信息统计表

索引设计



规则:



  • 查询条件不包含索引最左列,无法使用索引
  • 对于范围查询,只能利用索引最左列
  • 对于order by A语句,在A上建立索引,可以避免排序
  • 对于group by A 语句,在A上建立索引,可以避免排序
  • 对于多列排序,需要所有列所有列顺序方向一致,才能利用索引

建议:



  • 选择过滤性高的字段建立索引 distinct(col)/count(*)
  • JOIN 查询中连接字段建立索引 避免全表扫描
  • 尽量使用覆盖索引,无需访问表,避免随机IO
  • 利用前缀索引 name varchar(128),index(name(16))

注意:MYSQL我发使用前缀索引做ORDER BY和GROUP BY,也无法使用前缀索引做覆盖查询,有排序、分组场景不适合使用前缀索引



  • 避免建重复索引,提高索引使用率 (a),(a,b),(a,b,c)

SQL写法



建议:



  • 建议读写都采用主键索引
  • 尽量利用索引排序,避免产生临时表 order by col1,col2 index(col1,col2)
  • 避免对查询字段进行(类型转换,计算) WHERE id*2》4 --》 id>2
  • 避免使用select * ,指明使用列 select a,b,c
  • 避免使用全木湖查询 LIKE '%xxx%' 避免前缀 LIKE '%xxx' 不使用索引,LIKE 'xxx%'可以使用索引
  • 多SQL综合考虑,保证核心SQL

实践

  • 高效分页

LIMIT 分页在MYSQL中实际上存储引擎层查出所有数据,然后在MYSQL服务端层进行切割丢弃不需要部分返回切割后结果,分页过深存在性能问题。解决方式:利用InnoDB聚簇索引(索引+数据存放在索引文件上)的特性加速分页



select col1,col2,col3 from(select id from table_a
            <where_condition>orderby id limit300000,10) a
leftjoin table_a b on a.id=b.id
复制代码





  • 分析查询性能利器

响应时间 2. 扫描行数 3. 返回行数



参考

  • 《高性能MYSQL》- 3.创建高性能索引 4.查询性能优化