本文已参与「新人创作礼」活动,一起开启掘金创作之路。
到这篇文章,mysql基本上算是分析完了,作为一个开发人员,有一句话是肯定听不得的,你的接口为什么响应这么慢? 那这篇文章应该能很好的帮助你解决这个问题。
Mysql优化从何下手?
从开发到运维的角度应该是这样:
- sql和索引优化
- 表结构和存储引擎优化
- 根据真实的项目情况选择合适的存储引擎
- 表结构,细分,可以适当冗余
- 架构优化
- 缓存
- 基于主从复制实现的读写分离
- 分表分库
- 操作系统,Mysql的参数配置
- 硬件方面,CPU,内存等
作为开发人员,接下来我们来看一张图,优先从这张图上优化
接下来从这张图来一步一步分析优化之路。
1 连接
服务端:
- Mysql max_connections,默认最大连接数是151个,可相应的调整,根据服务器可调整最大10W
- Mysql wait_timeout,回收连接的时候
客户端:
- 使用池化技术,hikari、durid
2 缓存
- redis,mongo等。合理使用缓存,减少数据库连接
3 慢查询日志监控
show variables like '%_query%'
- slow_query_log,是否开启慢查询日志,默认是关闭
- long_query_time,超过这个时间就记录到慢查询日志里面,默认10秒
- slow_query_log_file,存储慢查询日志的路径
当前服务修改指令:set @@global.slow_query_log = 1;,服务重启后复原
永久修改需要到Mysql配置文件my.cnf中配置
查看慢查询日志:
- 直接到服务器查询慢查询日志文件
- 使用工具mysqldumpslow查询慢sql详细
4 执行计划 EXPLAIN/DESC
//用法explain 、 desc都可
explain select * from xxx where id=10
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | SIMPLE | xxx | const | PRIMARY | PRIMARY | 8 | const | 1 | 100.00 |
- id:执行序号
- select_type:查询类型
- table:表名,也可以是临时表名
- partitions:分区表
- type:针对单表的访问方式
- system:非InnoDB引擎,最快的
- const:主键,唯一索引
- eq_ref:join了唯一索引
- ref:非唯一索引
- range:索引的范围查询
- index(full index scan):根据索引全部查询
- ALL:全表扫描
- possible_keys:可能用到的多个索引
- key:单个索引
- ken_len:索引字段的字节
- ref:筛选数据索引方式的参考
- row:预估扫描的行数
- filtered:存储引擎计算出的有效数据和需要扫描数据的百分比
- Extra:额外信息
- using index 覆盖索引
- using where 需要过滤,没有经过索引
- index 索引条件下推
- using filesort 不是根据索引排序
- using temporary 使用到临时表
sql的执行计划中的type理论上应该至少到range,否则就是需要优化的
5 高并发优化
关系型数据库存在的性能问题:
- 表数量过大
- sql查询复杂
- sql查询没走索引
- 数据库服务器性能过低
表数据过大解决方案:
阿里开发手册:单表行数超过500W或者单表数据容量超过2G InnoDB最大适合1017列 优化方案:
- 分表分库
- 冷热数据分离
- 历史数据归档,例如:order order_month order_history
6 数据库的分表分库
具体案例:kafka -> 数据分片,redis-cluster ->slot数据不同的槽位,mysql -> 分片
6.1 水平拆分
数据拆分,例如根据用户ID取模计算出来的订单表
6.2 垂直拆分
表细分,例如订单表分为订单主表和明细表
6.3 策略
- 一般都是采用取模算法,但是表扩容时需要宕机
- hash一致性算法,顺时针找到第一个目标表存储
- 范围分片,根据不同的特征或群体进行分片
6.4 全局唯一ID
数据库自增ID uuid,但长度长且全是字符,占用大 雪花算法,64位二进制,从第一位至41位存时间戳,10位存工作机器ID,12位序列位,一毫秒内可以获取4千多个id redis原子递增
6.5 分表分库带来的问题
分片键查询
- 非分片键和分片键的映射关系
- k -v 存储映射维护
实现多个库分离
- 读写分离
- 可读视图
数据迁移
- 新老库双写
- 定时任务来同步数据并做数据校验
- 数据迁移完了,不要马上删掉,要有回滚预案
跨库查询
- 冗余字段
- 绑定表
- k - v存储映射
- 可读视图
分页排序
- 可读视图
- 业务做聚合,存储缓存
6.5 分库分表解决方案
添加中间件,分担单表的性能压力
- Sharding-sphere
- Sharding-JDBC 客服端代理
- Sharding-Proxy 服务端代理
- Mycat 服务端代理
7 总结
这么说吧,我觉得在服务运行保证响应快的同时还要跑的久。 作为开发人员,在这里分享几点经验:
- 不要无脑的添加索引
- 尽量减少非必要的关联查询,在多张大数据量表不用做分页的情况下,宁愿多次查询
- 查询尽量走索引,长一点的sql一定要先看执行计划
- 统计尽量做读写分离,没这个条件的尽量异步定时跑,尽量不用实时。
- 统计表尽量单表查询,不要一条sql查询整个统计(随着数据量增大数据库压力越来越大,而且还伴随着定期优化)
- 经常调用的数据可以维护一套到redis,减少数据库查询次数
- 高并发数据不要直接入库,可以借助redis,Mq等中间件保证分布式锁以后再入库
- 定期清理无用的数据
- 定期移动历史数据(会伴随着查询的修改,一般是表数据量大,且历史久远的才会)
最重要的来了:定期重启数据库,redis已经服务,能有效的释放连接,释放内存
以上就是本章的全部内容了。
上一篇:mysql第六话 - mysql事务与锁详解 下一篇:mysql第八话 - mysql的其它懒人用法
东隅已逝,桑榆非晚