Summer Blog

SQL优化

索引

类型

索引的优点

  1. 大大减少服务器需要扫描的数据量
  2. 帮助服务器避免排序和临时表
  3. 将随机 I/O 变为顺序 I/O

高性能的索引策略

索引优化实际例子

  1. 联合索引“隔离列”优化,将联合索引中为被查找的列添加全部可能到查询中,可以使索引得以继续使用。若此列数据量太多,则不适合此方法,需建立额外索引处理。

聚集索引

Inno DB 的聚集索引规则:

B-Tree 索引

MySQL 在创建表时使用这个名字,但并不是每个存储引擎实际都使用这个存储结构,如 NDB 集群存储引擎内部实际是 T-Tree;InnoDB 则使用的是 B+Tree。

每个存储引擎以不同的方式使用 B-Tree 索引,性能也各有不同。如 MyISAM 使用前缀压缩技术使索引更小,InnoDB 按照元数据格式存储;MyISAM 索引通过数据的物理位置引用被索引的行,InnDB 则根据主键引用被索引的行。

结构

B-Tree 意味着所有的值都是顺序存储的,且每个叶子页到根的距离相同。

适用情况

不适用情况

Hash 索引

基于 hash 表实现,对所有索引列计算 hash 值,hash 索引将这些值存储起来,并且保存该 hash 值指向的数据。当 hash 碰撞时,索引会以链表方式存放多个记录到同一个 hash 条目中。在 MySQL 中只有Memory引擎支持。

适用情况

不适用情况

物理结构

共享池优化

绑定变量,节省解析时间

排查方式:

反例:

日志优化

Oracle 逻辑体系结构

logical : database --> table space --> segment --> extent --> data block
physical:              data file               -->            OS block

 oracle logic data structure  oracle logic data structure

最小单位 block

一个 block 能装多少行?

各种开销导致,每行最小长度大致是 11 字节,例如,一个 8k 块理论上最多存储不超过 8096/11 行

行迁移

行链接

优化点

段(segment)

建表产生表段,建索引产生索引段,空间申请以区(extend)为单位,最小单位是块(block)。extend 以若干个连续的 block 组成。随着记录变多,segment 包含的 extents 和 blocks 也增多。

segment 高水平位

优化点

表设计

优化数据类型

MySQL 数据字段选取原则

MySQL 数据类型

MySQL 标识符选择(identifier)

标识符更有可能和其他值比较(例如关联操作),或者通过标识符找寻其他列。标识符也可能作为其他表的外键,要确保在所有关联表中都使用同样的类型,混用会导致性能问题,或者隐式转换中难以发现的错误。

整数通常是最好的选择,因为它们很快,且可以使用 AUTO_INCREMENT。

如果可能,应该避免使用字符串类型作为标识符,很消耗空间,且通常比数字类型慢。尤其是使用 MyISAM 时,因为 MyISAM 会对字符串做压缩索引,会导致查询慢得多。

用完全随机生成的值作为标识符也会导致性能问题。INSERT 时由于值会插入到不同的位置,导致页分裂、磁盘随机访问,以及对于聚簇存储引起产生聚簇索引碎片。SELECT 语句也会变慢,因为可能逻辑上相近的数据分布在磁盘和内存不同的地方。还会导致缓存的效果变差,因为缓存的访问局部性原理失效(如果整个数据集都很“热”,则缓存部分没有好处;如果工作集比内存大,缓存将会有很多刷新和不命中)

如果用 UUID,应移除-。UUID 虽然分布也不均匀,但还是有一定顺序。但还是不如递增整数好。

MySQL schema 设计中的陷阱

  1. 太多列
  2. 太多的关联:如 EAV 设计,MySQL 限制了每个关联操作最多只能有 61 张表,通常情况下单个查询最好控制在 12 个表以内。
  3. 全能枚举:如 country enum(‘’,’0’,’1’,’2’,’3’….)
  4. 变相枚举:如 is_default enum(‘Y’, ‘N’)
  5. Not invent here 的 null:用其他形式的特定值表示 null,造成不必要的复杂运算

数据库设计范式

范式的优点

  1. 更新快:范式化较好的数据库,没有或者很少重复数据,所以修改更少的数据
  2. 表更小,更好的放在内存中,执行操作更快
  3. 检索时需要更少的 DISTINCT 或者 GROUP BY 语句

范式的缺点

  1. 关联操作变多,可能导致索引策略无效(列如,范式化可能将列放在不同的表中,而这些列如果在同一个表中本可以属于同一个索引)

反范式的优点

  1. 可以避免关联:不需关联时最坏情况(没有索引)也是全表扫描,当数据比内存大时这比关联快很多,因为避免了随机 I/O
  2. 可以创建更有效的索引策略

混用范式和反范式化

  1. 最常见的反范式化数据的方式是复制或缓存,在不同的表中存储相同的特定列
  2. 缓存衍生值也是有用的,如缓存一个用户参加了多少活动,每当用户加入一个新活动时更新这个值

分区表

分区表可以包括多个分区, 每个分区都是一个独立的段( SEGMENT),可以存放到不同的表空间中 。查询时可以通过查询表来访问各个分区中的数据,也可以通过在查询时直接指定分区的方法来进行查询

分区表有四种方式:范围 R(月,天),列表 L(地区,区号),哈希 H(负载均衡),组合:11g 前(RL,RH);11g 后(RR,LL,LH,LR)。

优点:

全局临时表

如果数据是临时的,也就是说用完即抛,需要频繁执行删除操作(删除操作会造成大量日志写入,占用服务)

缓存表与汇总表

有时可以通过在表中保存衍生数据提升性能。有时也需要创建一张汇总表或缓存表(特别是为满足检索的需求时)。

其他设计优化

计数器表

提高计数器表的并发,可以设置多个行,每次更新时随机更新一行,算总和时再合并。这些行可以预先生成,或者使用ON DUPLICATE KEY UPDATE,如果担心行数太多,也可以定时合并。

加快 MySQL ALTER TABLE速度

MySQL 查询优化

衡量查询的三个指标:

  1. 响应时间:最重要的指标,但要记住是表面的值。因为它是服务时间+排队时间,可能受当时服务器情况影响
  2. 扫描的行数:非常有用,但不够完美。因为并不是访问所有行的代价都一致
  3. 返回的行数

优化数据访问

对低效查询,可以通过两个步骤分析:

  1. 确认是否在检索大量超过需要的数据,这通常意味着访问了太多的行,但有时候也可能是访问了太多的列
    1. 多余的行,如只需要前10行,但查了100行
    2. 多余的列,如关联查询时返回全部列、总是查询全部列
    3. 重复查询相同的数据,可使用缓存优化
  2. 确认 MySQL 服务层是否在分析大量超过需要的数据行
    1. 使用覆盖索引,把所有需要的行都放在索引中,这样无需回表
    2. 改变表结构,如汇总表
    3. 重新SQL

重构查询方式

  1. 复杂查询还是简单查询:当性能足够的时候可以先用复杂,若发现明显性能问题可优化为简单查询
  2. 切分查询:如删除大量数据时,可以多次执行删除少量数据,直到删除完所有数据
  3. 分解关联查询
    1. 让缓存更高效
    2. 减少锁竞争
    3. 减少对数据库层的依赖,数据库拆分时更容易,更容易做到高性能和可拓展
    4. 查询有可能会提升,如in替代关联查询,从随机读变为顺序读
    5. 减少冗余记录的查询

优化点总结

减少全表扫描

其他


comments powered by Disqus