有人删了千万级的数据,结果导致频繁的慢查询。
线上收到大量慢查询告警,于是检查慢查询的SQL,发现不是啥复杂SQL,这些SQL主要针对一个表,基本都是单行查询,看起来应该不会有慢查询。这种SQL基本上都是直接根据索引查找出来的,性能应该极高。
是否可能慢查询不是SQL问题,而是MySQL生产服务器的问题?特殊情况下,MySQL出现慢查询还真不是SQL问题,而是他自己生产服务器的负载太高,导致SQL语句执行慢。比如现在MySQL服务器的
磁盘I/O负载高,每秒执行大量高负载的随机I/O,但磁盘本身每秒能执行的随机I/O有限,导致正常SQL在磁盘执行时,若跑一些随机IO,你的磁盘太忙,顾不上你了,导致你本来很快的一个SQL,要等很久才能执行完毕,这时就可能导致正常SQL也变成慢查询。
也许网络负载高,导致你一个SQL语句要发到MySQL,光是等待获取一个和MySQL的连接,都很难,要等很久或MySQL自己网络负载太高,带宽打满,带宽打满后,你一个SQL也许执行很快,但其查出来的数据返回给你,网络都送不出去,也会变成慢查询。
若CPU负载过高,也会导致CPU过于繁忙去执行别的任务,没时间执行你的SQL。
所以慢查询不一定是SQL本身导致,若觉得SQL不应该会慢查询,结果他那个时间段跑这个SQL 就是慢,应排查当时MySQL服务器的负载,尤其看看磁盘、网络及 CPU 的负载,是否正常。
当某个离线作业瞬间大批量把数据往MySQL里灌入的时,他一瞬间服务器磁盘、网络以及CPU的负载会超高。
此时你一个正常SQL执行下去,短时间内一定会慢查询,类似问题,优化手段更多是控制你导致MySQL负载过高的那些行为,比如灌入大量数据,最好在业务低峰期灌入,别影响高峰期的线上系统运行。
但看了下MySQL服务器的磁盘、网络以及CPU负载,一切正常,似乎也不是这问题导致。看起来无解了?
慢 SQL 的头两步排查手段:
这两种办法都不奏效之后,第三步:用MySQL profilling工具去细致的分析SQL语句的执行过程和耗时。
这个工具可以对SQL语句的执行耗时进行非常深入和细致的分析
打开profiling,使用
接着MySQL就会自动记录查询语句的profiling信息。此时若执行show profiles,就会给你列出各种查询语句的profiling信息,会记录下来每个查询语句的query id,所以你要针对你需要分析的query找到对他的query id,我们当时就是针对慢查询的那个SQL语句找到了query id。
然后针对单个查询语句,看其profiling信息,使用show profile cpu, block io for query xx,这里的xx是数字,此时就可以看到具体的profile信息。
除了cpu以及block io以外,还能指定去看这个SQL语句执行时候的其他各项负载和耗时。
会给你展示出来SQL语句执行时候的各种耗时,比如磁盘IO的耗时,CPU等待耗时,发送数据耗时,拷贝数据到临时表的耗时等,SQL执行过程中的各种耗时都会展示。
检查该SQL语句的profiling信息后,发现问题,其Sending Data耗时最高,几乎使用1s,占据SQL执行耗时的99%!其他环节耗时低可以理解,毕竟这种简单SQL执行速度真的很快,基本就是10ms级别,结果跑成1s,那肯定Sending Data就是问题根源!
这Sending Data在干啥呢?
MySQL官方释义:为一个SELECT语句读取和处理数据行,同时发送数据给客户端的过程,简单来说就是为你的SELECT语句把数据读出来,同时发送给客户端。
但这过程为啥这么慢?profiling确实是提供给我们更多的线索了,但似乎还是没法解决问题。但已经捕获到异常关键点,就是Sending Data的耗时很高!
接着:
看innodb存储引擎的一些状态,此时发现一个奇怪的指标:history list length,值特别高,达到上万。
MVCC就是多个事务在对同一个数据, 有人写,有人读,此时可以有多种隔离级别,对一个数据有个多版本快照链条,才能实现MVCC和各种隔离级别。
所以当你有大量事务执行时,就会构建这种undo多版本快照链条,此时history list length就会很高。然后在事务提交后,会有一个多版本快照链条的自动purge清理机制,清理了,该值就会降低。一般该值不应过高,所以注意到第二个线索:history list length过高,即大量的undo多版本链条数据没有清理。推测可能有的事务长时间运行,所以其多版本快照不能被purge清理,进而导致history list length过高。
经过这俩线索推测,在大量简单SQL变成慢查询时,SQL因为Sending Data环节异常,耗时过高;同时此时出现一些长事务长时间运行,大量的频繁更新数据,导致有大量undo多版本快照链条,还无法purge清理。
因为发现有大量的更新语句在活跃,而且有那种长期活跃的长事务一直在跑而没有结束,问了下系统负责人,在后台跑了个定时任务:他居然开了一个事务,然后在一个事务里删除上千万数据,导致该事务一直在运行。
这种长事务的运行会导致你删除时,仅只是对数据加了一个删除标记,事实上并没有彻底删除。此时你若和长事务同时运行的其它事务里再查询,他在查询时可能会把那上千万被标记为删除的数据都扫描一遍。因为每次扫描到一批数据,都发现标记为删除了,接着就会再继续往下扫描,所以才导致一些查询语句很慢。
那为何你启动一个事务,在事务里查询,凭什么就要去扫描之前那个长事务标记为删除状态的上千万的垃圾数据?讲道理,那些数据都被删了,跟你没关系了呀,你可以不去扫描他们 嘛!
而问题症结在于,那个 删除千万级数据的事务是个长事务 !即当你启动新事务查询时,那个删除千万级数据的长事务一直在运行,它是活跃的!结合MVCC的Read View机制,当你启动一个新事务查询时,会生成一个Read View。你的新事务查询时,会根据ReadView去判断哪些数据可见及可见的数据版本号,因为每个数据都有个版本链条,有时你能可见的仅是这个数据的一个 历史 版本。
所以正是因为该长事务一直在运行,还在删除大量数据,而且这些数据仅是逻辑删除,所以此时你新开事务的查询还是会读到所有逻辑删除数据,也就会出现千万级的数据扫描,导致了慢查询!
所以禁止在业务高峰期运行那种删除大量数据的语句,因为这可能导致一些正常的SQL都变慢查询,因为那些SQL也许会不断扫描你标记为删除的大量数据,好不容易扫描到一批数据,结果发现是标记为删除的,于是继续扫描下去,导致慢查询!
直接kill那个正在删除千万级数据的长事务,所有SQL很快恢复正常。此后,大量数据清理全部放在凌晨执行,那个时候就没什么人使用系统了,所以查询也很少。
在我们使用MySQL数据库时,比较常用也是查询,包括基本查询,关联查询,条件查询等等,对于同一个 *** 作,SQL语句的实现有很多种写法,但是不同的写法查询的性能可能会有很大的差异。这里主要介绍下select查询优化的要点。1. 使用慢查询日志去发现慢查询。
2. 使用执行计划去判断查询是否正常运行。
3. 总是去测试你的查询看看是否他们运行在最佳状态下 –久而久之性能总会变化。
4. 避免在整个表上使用count(*),它可能锁住整张表。
5. 使查询保持一致以便后续相似的查询可以使用查询缓存。
6. 在适当的情形下使用GROUP BY而不是DISTINCT。
7. 在WHERE, GROUP BY和ORDER BY子句中使用有索引的列。
8. 保持索引简单,不在多个索引中包含同一个列。
9. 有时候MySQL会使用错误的索引,对于这种情况使用USE INDEX。
10. 检查使用SQL_MODE=STRICT的问题。
11.对于记录数小于5的索引字段,在UNION的时候使用LIMIT不是是用OR.
12. 为了 避免在更新前SELECT,使用INSERT ON DUPLICATE KEY或者INSERT IGNORE ,不要用UPDATE去实现。
3. 不要使用 MAX,使用索引字段和ORDER BY子句。
14. 避免使用ORDER BY RAND().
15. LIMIT M,N实际上可以减缓查询在某些情况下,有节制地使用。
16. 在WHERE子句中使用UNION代替子查询。
17. 对于UPDATES(更新),使用 SHARE MODE(共享模式),以防止独占锁。
18. 在重新启动的MySQL,记得来温暖你的数据库,以确保您的数据在内存和查询速度快。
19. 使用DROP TABLE,CREATE TABLE DELETE FROM从表中删除所有数据。
20. 最小化的数据在查询你需要的数据,使用*消耗大量的时间。
21. 考虑持久连接,而不是多个连接,以减少开销。
22. 基准查询,包括使用服务器上的负载,有时一个简单的查询可以影响其他查询。
23. 当负载增加您的服务器上,使用SHOW PROCESSLIST查看慢的和有问题的查询。
24. 在开发环境中产生的镜像数据中 测试的所有可疑的查询。
来源:PHP程序员雷雪松的博客
数据库优化一方面是找出系统的瓶颈,提高MySQL数据库的整体性能,而另一方面需要合理的结构设计和参数调整,以提高用户的相应速度,同时还要尽可能的节约系统资源,以便让系统提供更大的负荷.
1. 优化一览图
2. 优化
笔者将优化分为了两大类,软优化和硬优化,软优化一般是 *** 作数据库即可,而硬优化则是 *** 作服务器硬件及参数设置.
2.1 软优化
2.1.1 查询语句优化
1.首先我们可以用EXPLAIN或DESCRIBE(简写:DESC)命令分析一条查询语句的执行信息.
2.例:
显示:
其中会显示索引和查询数据读取数据条数等信息.
2.1.2 优化子查询
在MySQL中,尽量使用JOIN来代替子查询.因为子查询需要嵌套查询,嵌套查询时会建立一张临时表,临时表的建立和删除都会有较大的系统开销,而连接查询不会创建临时表,因此效率比嵌套子查询高.
2.1.3 使用索引
索引是提高数据库查询速度最重要的方法之一,关于索引可以参高笔者<MySQL数据库索引>一文,介绍比较详细,此处记录使用索引的三大注意事项:
2.1.4 分解表
对于字段较多的表,如果某些字段使用频率较低,此时应当,将其分离出来从而形成新的表,
2.1.5 中间表
对于将大量连接查询的表可以创建中间表,从而减少在查询时造成的连接耗时.
2.1.6 增加冗余字段
类似于创建中间表,增加冗余也是为了减少连接查询.
2.1.7 分析表,,检查表,优化表
分析表主要是分析表中关键字的分布,检查表主要是检查表中是否存在错误,优化表主要是消除删除或更新造成的表空间浪费.
1. 分析表: 使用 ANALYZE 关键字,如ANALYZE TABLE user
2. 检查表: 使用 CHECK关键字,如CHECK TABLE user [option]
option 只对MyISAM有效,共五个参数值:
3. 优化表:使用OPTIMIZE关键字,如OPTIMIZE [LOCAL|NO_WRITE_TO_BINLOG] TABLE user
LOCAL|NO_WRITE_TO_BINLOG都是表示不写入日志.,优化表只对VARCHAR,BLOB和TEXT有效,通过OPTIMIZE TABLE语句可以消除文件碎片,在执行过程中会加上只读锁.
2.2 硬优化
2.2.1 硬件三件套
1.配置多核心和频率高的cpu,多核心可以执行多个线程.
2.配置大内存,提高内存,即可提高缓存区容量,因此能减少磁盘I/O时间,从而提高响应速度.
3.配置高速磁盘或合理分布磁盘:高速磁盘提高I/O,分布磁盘能提高并行 *** 作的能力.
2.2.2 优化数据库参数
优化数据库参数可以提高资源利用率,从而提高MySQL服务器性能.MySQL服务的配置参数都在my.cnf或my.ini,下面列出性能影响较大的几个参数.
2.2.3 分库分表
因为数据库压力过大,首先一个问题就是高峰期系统性能可能会降低,因为数据库负载过高对性能会有影响。另外一个,压力过大把你的数据库给搞挂了怎么办?所以此时你必须得对系统做分库分表 + 读写分离,也就是把一个库拆分为多个库,部署在多个数据库服务上,这时作为主库承载写入请求。然后每个主库都挂载至少一个从库,由从库来承载读请求。
2.2.4 缓存集群
如果用户量越来越大,此时你可以不停的加机器,比如说系统层面不停加机器,就可以承载更高的并发请求。然后数据库层面如果写入并发越来越高,就扩容加数据库服务器,通过分库分表是可以支持扩容机器的,如果数据库层面的读并发越来越高,就扩容加更多的从库。但是这里有一个很大的问题:数据库其实本身不是用来承载高并发请求的,所以通常来说,数据库单机每秒承载的并发就在几千的数量级,而且数据库使用的机器都是比较高配置,比较昂贵的机器,成本很高。如果你就是简单的不停的加机器,其实是不对的。所以在高并发架构里通常都有缓存这个环节,缓存系统的设计就是为了承载高并发而生。所以单机承载的并发量都在每秒几万,甚至每秒数十万,对高并发的承载能力比数据库系统要高出一到两个数量级。所以你完全可以根据系统的业务特性,对那种写少读多的请求,引入缓存集群。具体来说,就是在写数据库的时候同时写一份数据到缓存集群里,然后用缓存集群来承载大部分的读请求。这样的话,通过缓存集群,就可以用更少的机器资源承载更高的并发。
一个完整而复杂的高并发系统架构中,一定会包含:各种复杂的自研基础架构系统。各种精妙的架构设计.因此一篇小文顶多具有抛砖引玉的效果,但是数据库优化的思想差不多就这些了.
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)