db 慢查询

mysql

慢查询

1
2
3
4
5
6
7
8
9
10
11
12
show full processlist;

kill id

# 批量kill超过 3 分钟的

select concat('kill ', id, ';')
from information_schema.processlist
where command != 'Sleep'
and time > 3*60
order by time desc;

  • Id: 就是这个线程的唯一标识,当我们发现这个线程有问题的时候,可以通过 kill 命令,加上这个Id值将这个线程杀掉。前面我们说了show processlist 显示的信息时来自information_schema.processlist 表,所以这个Id就是这个表的主键。
  • User: 就是指启动这个线程的用户。
  • Host: 记录了发送请求的客户端的 IP 和 端口号。通过这些信息在排查问题的时候,我们可以定位到是哪个客户端的哪个进程发送的请求。
  • DB: 当前执行的命令是在哪一个数据库上。如果没有指定数据库,则该值为 NULL 。
  • Command: 是指此刻该线程正在执行的命令。这个很复杂,下面单独解释
  • Time: 表示该线程处于当前状态的时间。
  • State: 线程的状态,和 Command 对应,下面单独解释。
  • Info: 一般记录的是线程执行的语句。默认只显示前100个字符,也就是你看到的语句可能是截断了的,要看全部信息,需要使用 show full processlist

state解析官方文档

查看锁


# 当前事务
select trx_state, trx_started, trx_mysql_thread_id, trx_query from information_schema.innodb_trx\G

trx_state: 事务状态,一般为 RUNNING
trx_started: 事务执行的起始时间,若时间较长,则要分析该事务是否合理
trx_mysql_thread_id: MySQL 的线程 ID,用于 kill
trx_query: 事务中的 sql
trx_rows_locked:事务锁住的行数


# 当前出现的锁
select * from information_schema.innodb_locks\G;

# 锁等待的对应关系 
select * from information_schema.innodb_lock_waits\G;
*************************** 1. row ***************************
requesting_trx_id: 613963             
requested_lock_id: 613963:460:3:4         #请求锁的锁ID
  blocking_trx_id: 613962                 #当前拥有锁的事务ID
 blocking_lock_id: 613962:460:3:4
1 row in set, 1 warning (0.00 sec)


# 查询是否锁表
show OPEN TABLES where In_use > 0;

+----------+-------+--------+-------------+
| Database | Table | In_use | Name_locked |
+----------+-------+--------+-------------+
| test     | tx1   |      1 |           0 |
+----------+-------+--------+-------------+
1 row in set (0.00 sec)

explain

mysql explain

es

高级调优:查找并修复 Elasticsearch 慢查询

Elasticsearch高级调优方法论之——根治慢查询

为什么Elasticsearch查询变得这么慢了?

线程池存在大量的“rejected”

1
GET /_cat/thread_pool/search?v&h=node_name,name,active,rejected,completed

file

  • 索引下分片太多,超过了集群中的核心数。就会在搜索线程池中造成排队任务,从而导致搜索拒绝

  • 磁盘I/O速度慢或在某些情况下完全饱和的CPU导致搜索排队

优化方法

  • 创建索引时采用1主分片&1副本模型
    • 使用索引模板是在创建索引阶段做好设置是个好方法。(7.0及更高版本默认1主1副)。
  • Elasticsearch 5.1 或更高版本支持搜索任务取消,这对于取消任务管理 API 中出现的慢查询任务非常有用。
    • GET _tasks?nodes=nodeId1,nodeId2 任务管理
    • POST _tasks/oTUltX4IQMOUUVeiohTt8A:12345/_cancel取消任务
  • 若要改进磁盘 I/O,请查看存储建议,并确保使用推荐的硬件以获得最佳性能。

非活动状态下资源利用率也很高

每个分片都消耗资源(CPU /内存)。即使没有索引/搜索请求,分片的存在也会消耗集群开销。

集群中的分片太多,以至于任何查询的执行速度看起来都很慢。一个好的经验法则是:确保对于每个节点上已配置的每个 GB ,将非冻结的分片数量保持在 20 以下。

控制分片数量
  • 分片的数量和节点和内存有一定的关系。
  • 最理想的分片数量应该依赖于节点的数量。 数量是节点数量的1.5到3倍。
  • 每个节点上可以存储的分片数量,和堆内存成正比。官方推荐:1GB 的内存,分片配置最好不要超过20。

  • 减少分片计数,实施冻结索引和/或添加附加节点来实现负载平衡。
  • 考虑结合使用 Elasticsearch 中的热/温架构(非常适合基于时间的索引)以及滚动/收缩功能,以高效管理分片计数。要想顺利完成部署,

  • 最好先执行适当的容量计划,以帮助确定适合每个搜索用例的最佳分片数。

高 CPU 使用率和索引延迟

指标相关性表明,当集群不堪重负时,CPU 使用率和索引延迟都会很高。

写入数据量大(索引化)会影响搜索性能。

优化方法

  • 调大刷新频率
    • index.refresh_ interval 的值(从文档被索引到其变为可见的时间间隔)增加到 30 秒,通常有助于提高索引性能。实际业务情境中可能会有所不同,因此测试是关键。这可以确保分片不必因为每 1 秒默认创建一个新分段而造成工作负载增大。
  • 对于索引量大的用例,请查看索引调优建议,以优化索引和搜索性能。
    • 数据初始化阶段refresh设置 -1 副本设置为 0,以提升写入速度 写入完毕后复原。
    • 关闭swapping。 the operating system is not swapping out the java process
    • 使用文件系统缓存。
    • 使用自动生成ID。

副本增加后延时增大

在增加副本分片计数(例如,从1到2)之后可以观察到查询等待时间。如果存在更多数据,则缓存的数据将很快被逐出,导致操作系统层面页面错误增加。

文件系统缓存没有足够的内存来缓存索引中经常查询的部分。Elasticsearch 的 查询缓存实现了 LRU 逐出策略:当缓存变满时,将逐出最近使用最少的数据,以便为新数据让路。

优化方法

  • 为文件系统缓存留出至少 50% 的物理 RAM

    • 内存越多,缓存的空间就越大,尤其是当集群遇到 I/O 问题时。 假设堆大小已正确配置,任何剩余的可用于文件系统缓存的物理 RAM 都会大大提高搜索性能。堆内存大小配置建议:Min(32 GB,物理机器内存 / 2)。

    • 文件系统缓存,Elasticsearch 还使用查询缓存和请求缓存来提高搜索速度。

      • index.queries.cache.enabled 节点级别的query缓存默认是开启的

      • 请求缓存默认是开启的,如果被强制关闭了,可以动态设置开启

      • 1
        2
        PUT /my_index/_settings
        { "index.requests.cache.enable": true }
        
    • 所有这些缓存都可以使用搜索请求首选项进行优化,以便每次都将某些搜索请求路由到同一组分片,而不是在不同的可用副本之间进行交替。这将更好地利用请求缓存、节点查询缓存和文件系统缓存。

共享资源时利用率高

操作系统显示出持续的高 CPU/磁盘 I/O 利用率。停止第三方应用程序后,可以看到性能会提高。

  • 给Elasticsearch隔离的硬件环境或虚拟环境。

  • 避免在共享硬件上与其他资源密集型应用程序一起运行Elasticsearch。

聚合N多唯一值引起的高内存使用率

聚合在高基数(high-cardinality)字段上运行,需要大量资源来获取许多存储桶。还可能存在涉及嵌套字段和/或联接字段的嵌套聚合。

注解:high-cardinality中文解读为高基数不好理解。举个例子:

  • 高基数——列中有很多唯一值(),如主键
  • 低基数——与之相反,如性别列(只有男、女)。

优化方式

偶发慢查询

一般来说,可以采用一些索引调优/搜索调优建议来解决偶尔或间歇出现的慢查询问题。偶发的慢查询应该与这些监测指标中的一个或多个密切相关:

Elasticsearch 还有另一个有用的功能,称为自适应副本选择 (ARS),它允许协调节点了解数据节点上的负载,并允许它选择最佳分片副本来执行搜索,从而提高搜索吞吐量并降低延迟。通过在查询期间更均匀地分配负载,ARS 对于偶发的减速有很大帮助。在 Elasticsearch 7.0 及更高版本中,默认情况下将启用 ARS。

持续性慢查询

拆解DSL排查慢查询根源

  • 没有高亮显示,它是否仍然很慢?
  • 没有聚合,它是否仍然很慢?
  • 如果大小设为 0,它是否仍然慢?(当大小设为 0 时,Elasticsearch 会缓存搜索请求的结果,以便更快地进行搜索)
  • “搜索调优”的建议是否有用

其他排除

  • Profile API 是一种调试工具,它会显着增加搜索执行的开销。

    启用profile:true

    1
    2
    3
    4
    5
    6
    7
    GET /twitter/_search
    {
      "profile": true,
      "query" : {
        "match" : { "message" : "some number" }
      }
    }
    
  • 查看节点的热点线程,了解CPU时间的使用情况

    1
    GET /_nodes/hot_threads
    
  • kibana可视化profile分析工具

    https://www.elastic.co/guide/en/kibana/7.0/xpack-profiler.html

    它提供了各个搜索组件的完美的可视化效果表征各个分解阶段以及各阶段查询的时间消耗。 同样,这允许您轻松选择查询的问题区域。

    640

捕获慢查询、耗费资源查询

有时,在像 Elasticsearch 这样的分布式应用程序中,当同时处理不同的请求/线程时,很难捕获慢查询或耗费资源的查询。如果对运行耗费资源查询的用户不加以控制,情况就会变得愈加复杂,这些查询会降低集群性能(例如,较长的垃圾收集 (GC) 周期),甚至更糟糕地会出现内存不足 (OOM) 的情况。

慢速日志

slow log es 设置

Slowlogs专门用于分片级别,这意味着只应用数据节点。仅协调 Coordinating-only/客户client节点不具备慢日志分析功能,因为它们不保存数据(索引/分片)。

slowlogs 有助于回答如下问题:

  • 查询花费了多长时间?
  • 查询请求正文的内容是什么?

索引慢速日志(index slow logs )和搜索慢速日志( search slow logs)。

查询和获取阶段

1
2
3
4
5
6
7
8
9
10
11
index.search.slowlog.threshold.query.warn: 10s
index.search.slowlog.threshold.query.info: 5s
index.search.slowlog.threshold.query.debug: 2s
index.search.slowlog.threshold.query.trace: 500ms

index.search.slowlog.threshold.fetch.warn: 1s
index.search.slowlog.threshold.fetch.info: 800ms
index.search.slowlog.threshold.fetch.debug: 500ms
index.search.slowlog.threshold.fetch.trace: 200ms

index.search.slowlog.level: info

可以动态设置

1
2
3
4
5
6
7
8
9
10
11
12
PUT /my-index-000001/_settings
{
  "index.search.slowlog.threshold.query.warn": "10s",
  "index.search.slowlog.threshold.query.info": "5s",
  "index.search.slowlog.threshold.query.debug": "2s",
  "index.search.slowlog.threshold.query.trace": "500ms",
  "index.search.slowlog.threshold.fetch.warn": "1s",
  "index.search.slowlog.threshold.fetch.info": "800ms",
  "index.search.slowlog.threshold.fetch.debug": "500ms",
  "index.search.slowlog.threshold.fetch.trace": "200ms",
  "index.search.slowlog.level": "info"
}
1
2
3
4
5
6
7
slowlog 输出示例:

[2019-02-11T16:47:39,882][TRACE][index.search.slowlog.query] [2g1yKIZ] [logstash-20190211][4] took[10.4s], took_millis[10459], total_hits[16160], types[], stats[],
search_type[QUERY_THEN_FETCH], total_shards[10], source[{"size":0,"query":{"bool":{"must":[{"range":{"timestamp":{"from":1549266459837,"to":1549871259837,"include_lower":true,
"include_upper":true,"format":"epoch_millis","boost":1.0}}}],"adjust_pure_negative":true,"boost":1.0}},"_source":{"includes":[],"excludes":[]},"stored_fields":"*","docvalue_fields":
[{"field":"timestamp","format":"date_time"},{"field":"utc_time","format":"date_time"}],"script_fields":{"hour_of_day":{"script":{"source":"doc['timestamp'].value.getHourOfDay()",
"lang":"painless"},"ignore_failure":false}},"aggregations":{"maxAgg":{"max":{"field":"bytes"}},"minAgg":{"min":{"field":"bytes"}}}}], id[]],

slowlog 消息拆解:

拆分项 说明
[2019-02-11T16:47:39,882] 查询日期
[TRACE] 日志级别
[index.search.slowlog.query] 搜索 slowlog 的查询阶段
[2g1yKIZ] 节点名称
[logstash-20190211] 索引名称
[4] 查询执行的分片序号
took[10.4s] 在分片 [4] 上花费的处理时间。注意:在查看 slowlog 时,我们需要避免将来自不同分片的所有时间相加,因为每个分片可能并行执行。
took_millis[10459] 花费的时间(毫秒)
total_hits[16160] 总命中数
search_type[QUERY_THEN_FETCH] 搜索类型
total_shards[10] 索引的总分片数
source[] 执行的查询正文

其他优化

避免使用script查询

避免使用脚本查询来计算匹配。 推荐:建立索引时存储计算字段。

例如,我们有一个包含大量用户信息的索引,我们需要查询编号以“1234”开头的所有用户。

您可能希望运行类似“source”的脚本查询:

doc [‘num’].value.startsWith(’1234’)

此查询非常耗费资源并且会降低整个系统的速度。 合理的建议:考虑在索引时添加名为“num_prefix”的字段。 然后我们可以查询

“name_prefix”:“1234”。

避免使用wildcard查询

主要原因: wildcard类似mysql中的like,和分词完全没有了关系。

出现错误: 用户输入的字符串长度没有做限制,导致首尾通配符中间可能是很长的一个字符串。 后果就是对应的wildcard Query执行非常慢,非常消耗CPU。

根本原因: 为了加速通配符和正则表达式的匹配速度,Lucene4.0开始会将输入的字符串模式构建成一个DFA (Deterministic Finite Automaton),带有通配符的pattern构造出来的DFA可能会很复杂,开销很大。

可能的优化方案:

  1. wildcard query应杜绝使用通配符打头,实在不得已要这么做,就一定需要限制用户输入的字符串长度。
  2. 最好换一种实现方式,通过在index time做文章,选用合适的分词器,比如nGram tokenizer预处理数据,然后使用更廉价的term query来实现同等的模糊搜索功能。
  3. 对于部分输入即提示的应用场景,可以考虑优先使用completion suggester, phrase/term/suggeter一类性能更好,模糊程度略差的方式查询,待suggester没有匹配结果的时候,再fall back到更模糊但性能较差的wildcard, regex, fuzzy一类的查询。

详尽原理参考:https://elasticsearch.cn/article/171

合理使用keyword类型

ES5.x里对数值型字段做TermQuery可能会很慢。

在ES5.x+里,一定要注意数值类型是否需要做范围查询,看似数值,但其实只用于Term或者Terms这类精确匹配的,应该定义为keyword类型。

典型的例子就是索引web日志时常见的HTTP Status code。

详尽原理参考:https://elasticsearch.cn/article/446

控制字段的返回

一是:数据建模规划的时候,在Mapping节点对于仅存储、是否构建倒排索引通过enabled、index参数进行优化。

二是:_source控制返回,不必要的字段不需要返回,举例:采集的原文章详情内容页,根据需要决定是否返回。

让Elasticsearch干它擅长的事情

在检索/聚合结果后,业务系统还有没有做其他复杂的操作,花费了多少时间?

这块是最容易忽视的时间耗费担当。

Elasticsearch显然更擅长检索、全文检索,其他不擅长的事情,尽量不要ES处理。比如:频繁更新、确保数据的ACID特性等操作。