## 优化方式 数据量 - SQL 执行后返回给客户端的数据量的大小 - 数据量越大需要扫描的 I/O 次数越多,数据库服务器的 IO 瓶颈 取数据的方式 - 数据在缓存还是磁盘 - 能否通过全局索引快速寻址 - 是否结合谓词条件命中全局索引加速扫描 数据加工的方式 - 排序、子查询、聚合、关联等需要将数据取到临时表中,再对数据加工 - 对于数据量多的计算,会消耗大量 CPU 资源 - 是否选择了合适的 join 方式 ## 优化思路 减少数据扫描(磁盘访问) - 尽量在查询中加入一些可以提前过滤数据的位于条件,减少数据扫描量,对查询更加友好 - 扫描大表数据时是否可以命中索引,减少回表,避免全表扫描 返回更少数据(减少网络传输或磁盘访问) 减少交互次数(减少网络传输) - 将数据存放在更快的地方 - 某条查询涉及大表,无法进一步优化,如果返回数据量不大但访问频率很高,可以将返回的数据放在应用端缓存或 redis 缓存中,提高存取速度 减少服务器开销(CPU 和内存) 避免大事务 利用更多资源 ## 优化案例 - 数据分页优化 ```sql select * from table_name where type = ? limit ?,?; ``` 偏移 id ```sql lastId = 0 or min(id) do { select * from table_name where type = ? and id > {#lastId} limit ?; lastId = max(id) } while (isNotEmpty) ``` 分段查询 优点:可并行查询,分段互不依赖 缺点:依赖连续性,数据过于分散代价较高 ```sql minId = min(id) maxId = max(id) for(int i = minId; i <= maxId; i+=pageSize){ select * from table_name where type = ? and id between i and i+ pageSize; } ``` - 优化 groupBy 将不需要的数据在 groupBy 之前过滤掉 ```sql select job, avg(sal) from table_name group by job having job = 'manager' ``` ```sql select job, avg(sal) from table_name where job = 'manager' group by job ``` - 范围查询 联合索引中如果某个列存在范围查询(大于小于),其右边的列是否还有意义 设计时需要结合业务需求 - 优化 order by 索引 ```sql KEY `idx_account_trade_date_time` (`account_number`,`trade_date_time`) KEY `idx_trade_date_time` (`trade_date_time`) KEY `idx_create_time` (`create_time`) ``` 慢 SQL ```sql select id,,,creator,modifier,create_time,update_time from satatement where (account_number = 'XXX' and create_time > '2024-04-24 00:00:00' and create_time < '2024-04-25 00:00:00' and dc_flag = 'C') order by trade_date_time desc, id desc limit 0,1000; ``` ```sql select id,,,creator,modifier,create_time,update_time from satatement where (account_number = 'XXX' and create_time > '2024-04-24 00:00:00' and create_time < '2024-04-25 00:00:00' and dc_flag = 'C') order by create_time desc, id desc limit 0,1000; ``` MySQL 使不使用索引与所查列无关,只与索引本身,where 条件,order by group by 字段有关,索引的作用为查找和排序 - 业务拆分 ```sql select * from order where status = 'S' and update_time < now-5min limit 500 ``` 业务数据增长,status 无法走索引,结合业务特性,对数据获取按日期拆分 ```sql date = now; minDate = now - 10 day while(date > minDate) { select * from order where order_date = #{date} and status = 'S' and update_time < now-5min limit 500 date = date + 1 } ``` ## 数据库结构优化 - 范式优化:表的设计合理化(3 NF),消除冗余(节省空间) - 反范式优化:适当添加冗余(减少 join) - 拆分表:分区将数据在物理上分隔开,不同分区数据表存储于不同磁盘中,查询时只需到表分区中扫描,无需全表扫描,同时分散了磁盘 IO ## SQL 语句优化 1. 避免子查询 2. 用 in 替换 or 3. 读取适当的记录 limit M,N,不要读取多余的记录 4. 禁止不必要的 order by 排序 5. 总和查询禁止排重用 union all 6. 避免随机取数据 7. 将多次插入换成批量插入 8. 只返回必要的列,select 具体字段 9. 区分 in 和 exist 10. 优化 group by 11. 尽量使用数字型字段 12. 优化 join 语句 ## 大表优化 - 分库分表 - 读写分离 - 数据定期归档 ## 索引的优缺点 优点 - 提高查询语句执行效率,减少 IO 操作次数 - 创建唯一性索引,保证数据库中每一行数据的唯一性 - 加索引的列会进行排序,使用分组和排序子句可以减少时间 缺点 - 索引需要占用一定空间 - 创建索引和维护索引需要时间 - 对表中数据进行修改时,索引也要动态维护,降低了更新效率 ## 索引结构 ## 数据行格式 MySQL 有 4 种存储格式: 1. Compact 2. Redundant(5.0 版本前,已废弃) 3. Dynamic(MySQL 5.7 默认格式) 4. Compressed Dynamic 下,对于处理行溢出,仅存放溢出页内存地址 ## 索引设计原则 哪些情况适合建索引 - 数据有唯一性限制 - 频繁 where 条件 - 进场 group by 和 order by 字段,都有时建议联合索引 - 经常 distinct 字段 - 多表连接时的字段 - 数量最好不要超过 3 张 - 多表查询时 where 条件建索引 - 对连接字段建索引,且数据类型保持一致 - 确定数据范围情况下使用数据类型较小的字段,索引也会占空间 - 字符串建索引使用前缀 - 节省空间 - 可定位到相同前缀,然后通过主键查询完整字符串 - 区分度高的字段(散列性高)的字段 - 多个字段需要建索引,联合索引优于单值索引 不需要索引 - where 条件中用不到 - 数据量小,小于 1000 条 - 大量重复数据的列 - 避免在经常更新的表或字段 - 不建议主键使用无序的值 - 不定义冗余或重复索引 - 已有联合索引不要单独再建单索引 ## 索引优化之 MRR ## 索引下推 idx (name, age) ```sql select * from tuser where name like '张%' and age=10; ``` 直接在联合索引里面过滤过滤两次完后在回表 条件 - 只能用于 range、ref、eq_ref、ref_or_null 访问方法 - 只使用 InnoDB 和 MyISAM - 对存储引擎来说,只适用于二级索引(辅助索引) - 引用了子查询的条件不能下推 - 引用了存储函数的条件不能下推,存储引擎无法调用存储函数