1. 查询性能优化
1.1 优化数据访问
- 检查是否检索大量超过须要的数据.是否访问太多行或太多列,增长网络开销,消耗cpu和内存资源
- 检查服务器层是否在分析大量超过须要的数据行
1.2 重构查询的方式
1.2.1 切分查询
- 有时对于一个大查询咱们须要分而治之,切分红小查询每次只完成一部分
1.2.2 分解关联查询
- 缓存效率更高: 方便缓存单表查询结果
- 执行单个查询能够减小锁的竞争
- 在应用层作关联,更容易对数据库进行拆分,更容易作到高性能和可扩展
- 使用in 代替关联查询可能比随机的关联要高效
- 能够减小冗余记录的查询
1.3 查询执行的基础
1.3.1 查询流程
- 先检查缓存
- sql解析,预处理,优化器生成相应的执行计划
- 调用存储引擎的api执行查询
1.3.2 通讯协议
- 半双工,任何一时刻要么是服务器向客户端发送数据,要么是客户端向服务端发送数据
- 客户端从服务器获取数据时,实际是MySQL向客户端推送数据的过程
1.3.3 查询状态
- Sleep: 线程正在等待客户端发送新的请求
- Query: 线程正在执行查询或者正在将结果发送给客户端
- Locked: 服务器层,线程正在等待表锁
- Analyzing and statistics: 线程正在收集存储引擎统计信息,并生成查询的执行计划
- Copying to tmp table: 线程正在执行查询,而且将结果集复制到一个临时表中.常见group by或文件排序操做
- Sorting result: 线程正在对结果进行排序
- Sending data: 线程可能在多个状态之间传送数据或在生成结果集或向客户端返回数据
1.3.4 查询优化
1.3.4.1 语法解析器和预处理
- 经过关键字将sql语句进行解析,生成对应的解析树
- 解析器使用语法规则验证和解析查询
- 预处理器进一步检查解析树是否合法,验证权限
1.3.4.2 查询优化器
- 一条查询能够有多种执行方式,优化器找到其中最好的执行计划,MySQL使用基于成本的优化器
- 优化类型
- 从新定义关联表的顺序
- 外联结转化成内链接
- 使用等价变换规则
- 优化count, min, max函数
- 预估并转化为常数表达式
- 覆盖索引扫描
- 子查询优化
- 提早终止查询,如limit
- 等值传播
- In优化
1.3.4.3 关联查询
- 嵌套循环: 先在一个表中循环取出单条数据,而后再嵌套循环到下一个表中寻找匹配的行,若是最后一个联表没法找到更多的行,则返回上一层次关联表
- UNION查询和子查询时都会将临时结果存放到一个临时表中
1.3.4.4 执行计划
- MySQL生成一棵指令树,经过存储引擎执行完成并返回结果
1.3.4.5 排序优化
- 排序是一个成本很高的操做
- MySQL排序: 若是数据量小,则在内存中进行; 数据量大则先分块再排序再合并
- MySQL4.1后使用单次传输排序: 先读取查询所须要的全部列,再根据给定列排序
1.3.4.6 查询执行引擎
- 根据执行计划的指令逐步执行
1.3.4.7 返回结果给客户端
- 若是查询能够缓存,则缓存在这个阶段进行
- 返回结果的过程是一个增量逐步返回的过程,一旦开始生成第一条结果时就能够开始向客户端返回结果集
1.4 查询优化器的局限
- 子查询相对糟糕(不是绝对),如子查询用in
- 联表查询与子查询根据场景不一样有不一样优点
- MySQL没法将限制条件下推到子查询
- 索引合并优化
- MySQL没法利用多核特性并行执行查询
- MySQL并不支持哈希关联, MariaDB已经实现了真正的哈希关联
- 松散索引扫描,没法按照不连续的方式扫描一个索引
- 最大值最小值函数的优化通常
- 不容许同一张表上同时查询和更新, 如update set 等于 select 本身.解决方法,能够经过关联临时表
1.5 查询优化器的提示
- 设置查询优化器参数,能够阅读官方手册
- 通常除非须要,修改查询优化器参数会提升维护成本
1.6 优化特定类型的查询
- 关联查询: on的列加索引; 使用group by和order by 只使用一个表的列能够利用索引
- 优化LIMIT分页: 尽可能使用覆盖索引
- 子查询: 尽可能使用关联查询替换
- 静态查询分析: Percona Toolkit中的pt-query-advisor能解析查询日志,分析查询模式
- 使用用户自定义变量: 没法使用查询缓存,可能被优化器优化掉
2. MySQL高级特性
2.1 分区表
2.1.1 应用
- 表很是大没法所有放在内存中,或者只在表的最后部分有热点数据其余均是历史数据
- 分区表的数据更容易维护
- 分区表的数据能够分布在不一样的物理设备上
- 使用分区表避免某些瓶颈,如InnoDB单个索引的互斥访问
- 备份和恢复独立分区,对于大数据集效果较好
2.1.2限制
- 一个表最多1024个分区
- 分区表达式必须是整数或返回整数的表达式
- 若是分区字段有主键或惟一索引列,那么全部主键列和惟一索引都必须包含进来
- 分区表中没法使用外键约束
2.1.3 原理
- 分区表由多个相关的底层表实现,存储引擎管理它们跟管理普通表同样
- select 查询: 分区层打开并锁住全部底层表,优化器判断是否过滤分区,在调用存储引擎api访问各个分区数据
- insert: 分区层打开并锁住全部底层表,肯定分区,写入
- delete: 分区层打开并锁住全部底层表,肯定数据所在分区,删除
- update: 分区层打开并锁住全部底层表,肯定分区,取出数据,更新,肯定分区,写入
- 打开并锁住全部底层表: 若是存储引擎实现行级锁如InnoDB,则会在分区层释放表锁
2.1.4 分区表类型
- 根据范围进行分区: 每一个分区储存落在某个范围的记录
- 根据键值进行分区,减小InnoDB互斥量竞争
- 使用数学模函数进行分区,而后将数据轮询放入不一样的分区,适用于只想保留几天的数据
2.1.5 使用
- 问题回顾:数据量很大时,除非是索引覆盖查询,不然数据库须要根据索引扫描回表查询,产生大量的随机IO,数据库响应时间很大
- 全量扫描数据不要索引,根据分区定位数据位置
- 索引数据,分离热点. 将热点数据单独放在一个分区
- NULL值会使分区过滤无效: 分区表达式接收NULL值并将其放到第一个分区致使查询时多查分区.解决方法:建立第一个无用分区存放NULL值数据
- 分区列和索引列不匹配,查询没法进行分区过滤
- 选择分区成本高,插入大量数据时都须要扫描分区定义找到分区
- 打开并锁住全部底层表的成本可能很高
- 维护分区的成本很高,同alter同样建立临时表而后拷贝数据
- 全部分区都必须使用相同的存储引擎
2.1.6 查询优化
- 在where条件带入分区列
- 建立分区时可使用表达式,可是查询时只能在使用分区列自己进行比较时才能过滤分区,而不能根据表达式的值过滤分区
2.2 视图
视图自己是一个虚拟表,不存听任何数据,不能对视图建立触发器算法
2.2.1 算法
- 合并算法: 将存放的视图sql和用户发起的查询sql合并后执行
- 临时表算法: 由存放的视图sql先建立临时表后根据用户的查询sql查询返回
2.2.2 可更新视图
- 能够经过更新视图更新相关表, 全部临时表算法实现的视图都没法更新
2.2.3 视图对性能的影响
- 通常状况视图不能提高性能,在某些状况下能够帮助提高性能,须要作比较详细的测试
- 视图还能够实现基于列的权限控制不用真正建立列权限
2.2.4 视图的限制
- 不保存视图定义的原始sql语句
- 查看视图建立的语句,能够经过使用视图的.frm文件的最后一行获取一些信息
2.3 外键约束
- InnoDB强制外键使用索引
- 查询须要额外访问一些表,须要额外的锁容易致使一些死锁
- 若是使用外键作约束,一般在应用程序里实现会更好
2.4 内部存储代码
2.4.1 优势
- 离数据最近,节省带宽和网络延迟
- 帮助提高安全性,应用程序能够经过存储过程访问那些没有权限的表
- 服务器端能够缓存存储过程的执行计划
- 维护方便,便于分工
2.4.2 缺点
- 调试困难,难以定位问题
- 存储代码效率相对差
- 增长维护复杂性,存储过程会给数据库服务器增长额外压力
- 存在安全隐患,没有什么选项能够控制存储程序的资源消耗,因此一个小错误可能直接把服务器拖死
2.4.3 存储过程和函数
- 优化器没法评估存储函数的执行成本
- 每一个链接都有独立的存储过程的执行计划缓存,多个链接调用同一个存储过程会浪费缓存空间反复缓存一样的执行计划
2.4.4 触发器
- 每一个表的每一个事件只能一个
- MySQL只支持基于行的触发,若是变动的数据集很是庞大的化效率会很低
- 触发器的问题很难排查
- 可能致使死锁和锁等待
- 实现一些约束,系统维护任务及更新反范式化数据的时候会比较有用
2.4.5 事件
- 相似Linux的定时任务
2.5 游标
- MySQL在服务器端提供只读的,单向的游标
- 一个存储过程当中能够有多个游标,也能够嵌套
2.6 绑定变量
- 建立一个绑定变量sql时客户端向服务器发送了一个sql语句原型
- 服务器端解析并存储这个sql语句的部分执行计划返回客户端一个sql语句处理句柄
- 可使用问号做为sql的占位,在使用sql接口执行时赋予变量值
2.7 插件
- 存储过程插件
- 后台插件: 如Percona Server中包含的Handler-Socket
- INFORMATION_SCHEMA插件
- 全文解析插件: 能够对文档进行分词处理
- 审计插件: 能够用做记录事件日志
- 认证插件: 扩展认证功能
2.8 字符集和校对
- 字符集是指一种从二进制编码到某类字符符号的映射
- 校对是指一组用于某个字符集的排序规则
2.8.1 建立对象时的默认设置
- 服务器,数据库,表都有默认的字符集和校对规则,这是一个逐层继承的默认设置
- 建立数据库时根据character_set_server设置来设定默认字符集
2.8.2 服务器和客户端通讯设置
- 服务端老是假设客户端按照character_set_client设置的字符来传输数据和sql语句
- 服务器端收到sql语句后根据character_set_connection转换成字符串
- 服务器端返回数据时会将其转换为character_set_result
2.8.3 选择字符集和校对规则
- 极简原则: 先为服务器选择合理的字符集在根据实际状况让某些列选择合适的字符集
2.8.4 对查询的影响
- 不一样字符集和校对规则之间的转换会带来额外的开销
- 排序查询要求的字符集与服务器数据的字符集相同时才能利用索引进行排序
- 当两个字符集不一样列关联两个表时,MySQL会尝试转换其中一个列的字符集
2.9 全文索引
- 天然语言的全文索引: 相关度是基于匹配的关键词个数及关键词在文档中出现的次数,整个索引中出现次数越少的词语匹配的相关度越高
- 布尔全文索引: 只有MyISAM才能使用
- 平时没接触过,有兴趣者请自行google
2.10 分布式XA事务
- 事务协调器保证全部事务参与者完成工做,通知全部事务提交
- 内部XA事务: 存储引擎提交的同时,须要将提交的信息写入二进制日志
- 外部XA事务: XA事务是一种在多个服务器之间同步数据的方法,若是因为不能使用MySQL自己的复制或者性能并非瓶颈能够尝试使用
2.11 查询缓存
- 查询缓存系统会跟踪查询中涉及的每一个表,若是表发生变化则缓存数据失效
- 缓存存放在一个引用表中,经过一个哈希值引用,哈希值包括查询自己,查询数据库等信息
- 当sql语句和客户端发送过来的其余原始信息,任何字符上的不一样都会致使缓存不命中
- 打开查询缓存对读和写都会带来额外的消耗
- InnoDB事务修改表时,会将这个表对应的查询缓存都设置失效
- 查询缓存被发现是一个影响服务器扩展性的因素
- 若是缓存了大量的查询结果,那么失效操做可能会形成系统僵死.由于靠一个全局锁保护,全部该操做都要等锁
- 减小碎片, 选择合适的query_cache_min_res_unit能够减小内存浪费
- 对于写密集型的应用,直接禁用更好
- 高并发环境也不适合.只有明确缓存的好处才使用
- 查询缓存的替代方案: 客户端缓存