【问题标题】:Simple Query MariaDB getting very slow简单查询 MariaDB 变得非常慢
【发布时间】:2020-11-21 22:38:00
【问题描述】:

我有这个简单的查询

SELECT  item_code, stock_value, name, warehouse
    FROM  `tabStock Ledger Entry` sle
    WHERE  posting_date <= '2020-08-01'
      AND  warehouse = 'bom'
    ORDER BY  timestamp(posting_date, posting_time) DESC,
              Creation DESC 

1000 条记录几乎需要 3 秒。如果没有 Order By 子句,查询整个表只需不到 1 秒。该表本身目前有 1M+ 条记录。

show processlist 总是卡在 State Creating Sort Index 上。

解释查询显示:

+------+-------------+-------+------+------------------------------+-----------+---------+-------+--------+----------------------------------------------------+
| id   | select_type | table | type | possible_keys                | key       | key_len | ref   | rows   | Extra                                              |
+------+-------------+-------+------+------------------------------+-----------+---------+-------+--------+----------------------------------------------------+
|    1 | SIMPLE      | sle   | ref  | posting_sort_index,warehouse | warehouse | 563     | const | 127740 | Using index condition; Using where; Using filesort |
+------+-------------+-------+------+------------------------------+-----------+---------+-------+--------+----------------------------------------------------+

表索引如下:

   +-----------------------+------------+-------------------------------+--------------+--------------+--
| Table                 | Non_unique | Key_name                      | Seq_in_index | Column_name  | C
+-----------------------+------------+-------------------------------+--------------+--------------+--
| tabStock Ledger Entry |          0 | PRIMARY                       |            1 | name         | A
| tabStock Ledger Entry |          1 | item_code                     |            1 | item_code    | A
| tabStock Ledger Entry |          1 | parent                        |            1 | parent       | A
| tabStock Ledger Entry |          1 | posting_sort_index            |            1 | posting_date | A
| tabStock Ledger Entry |          1 | posting_sort_index            |            2 | posting_time | A
| tabStock Ledger Entry |          1 | posting_sort_index            |            3 | name         | A
| tabStock Ledger Entry |          1 | voucher_no_voucher_type_index |            1 | voucher_no   | A
| tabStock Ledger Entry |          1 | voucher_no_voucher_type_index |            2 | voucher_type | A
| tabStock Ledger Entry |          1 | warehouse                     |            1 | warehouse    | A
| tabStock Ledger Entry |          1 | warehouse                     |            2 | posting_date | A
+-----------------------+------------+-------------------------------+--------------+--------------+--

为(posting_date,warehouse)或(warehouse,posting_date)添加了索引,但它没有帮助..查询似乎总是使用文件排序

innodb_buffer_pool_size 是 2G..out of 4G RAM。我认为它足够了..它不是一个巨型数据库

SHow Create table如下:

+-----------------------+---------------------------------------------------------------
| Table                 | Create Table
+-----------------------+---------------------------------------------------------------
| tabStock Ledger Entry | CREATE TABLE `tabStock Ledger Entry` (
  `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `creation` datetime(6) DEFAULT NULL,
  `modified` datetime(6) DEFAULT NULL,
  `modified_by` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `owner` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `docstatus` int(1) NOT NULL DEFAULT 0,
  `parent` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `parentfield` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `parenttype` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `idx` int(8) DEFAULT NULL,
  `actual_qty` decimal(18,6) NOT NULL DEFAULT 0.000000,
  `warehouse` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `qty_after_transaction` decimal(18,6) NOT NULL DEFAULT 0.000000,
  `fiscal_year` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `company` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `serial_no` longtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `incoming_rate` decimal(18,6) NOT NULL DEFAULT 0.000000,
  `stock_queue` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `item_code` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `voucher_detail_no` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `project` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `stock_value_difference` decimal(18,6) NOT NULL DEFAULT 0.000000,
  `stock_uom` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `voucher_type` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `batch_no` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `valuation_rate` decimal(18,6) NOT NULL DEFAULT 0.000000,
  `posting_date` date DEFAULT NULL,
  `voucher_no` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `stock_value` decimal(18,6) NOT NULL DEFAULT 0.000000,
  `is_cancelled` varchar(140) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `posting_time` time(6) DEFAULT NULL,
  `outgoing_rate` decimal(18,6) NOT NULL DEFAULT 0.000000,
  `_comments` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `_liked_by` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `_assign` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `_user_tags` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `to_rename` int(1) NOT NULL DEFAULT 1,
  PRIMARY KEY (`name`),
  KEY `item_code` (`item_code`),
  KEY `parent` (`parent`),
  KEY `posting_sort_index` (`posting_date`,`posting_time`,`name`),
  KEY `voucher_no_voucher_type_index` (`voucher_no`,`voucher_type`),
  KEY `warehouse` (`warehouse`,`posting_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPRESSED
+-----------------------+---------------------------------------------------------------

更多

我删除了时间戳功能和仅通过发布日期排序..从 mariadb 控制台运行查询..最后,加载超过 200.000 个结果行只用了

但是,如果我从 Web 应用程序运行相同的查询,ma​​riadb-slow.log 会向我显示不同的结果。相同的结果需要 > 4 秒。 什么可能导致这种情况?

# User@Host: _5839b22099d630d5[_5839b22099d630d5] @ localhost [127.0.0.1]
# Thread_id: 685  Schema: _5839b22099d630d5  QC_hit: No
# Query_time: 4.101652  Lock_time: 0.000055  Rows_sent: 226657  Rows_examined: 226657
# Rows_affected: 0
SET timestamp=1596384652;
SELECT item_code, stock_value, name, warehouse
                FROM `tabStock Ledger Entry` sle
                WHERE posting_date <= '2020-08-02'  AND warehouse = 'bom'
                ORDER BY posting_date DESC;

用 JSON 解释

{
  "query_block": {
    "select_id": 1,
    "table": {
      "table_name": "sle",
      "access_type": "range",
      "possible_keys": [
        "warehouse",
        "posting_date",
        "posting_sort_index",
        "warehouse_2"
      ],
      "key": "warehouse_2",
      "key_length": "567",
      "used_key_parts": ["warehouse", "posting_date"],
      "rows": 126605,
      "filtered": 100,
      "attached_condition": "sle.warehouse <=> 'bom' and sle.posting_date <= '2020-08-02' and sle.warehouse = 'bom'"
    }
  }

【问题讨论】:

  • 你能检查一下如果你只按两个语句之一订购会发生什么。也许时间戳的创建需要很长时间。为什么不直接按日期、时间排序,而不是将它们传递给时间戳函数?
  • 哪个版本的 MariaDB?
  • 10.2.31-MariaDB-1:10.2.31+m
  • 将 226657 行铲出门需要 4 秒。客户会用这么多东西做什么? Rows_examined 是花费总时间的一个重要因素。

标签: mysql indexing mariadb


【解决方案1】:

添加这个复合索引:

INDEX(warehouse, posting_date)

(并且,如果您当前有 INDEX(warehouse),请删除它。)

如需进一步讨论,请以文字(非图片)形式提供SHOW CREATE TABLE 和完整的EXPLAIN

有关索引创建的更多信息:http://mysql.rjweb.org/doc.php/index_cookbook_mysql

时间安排:

  • 可能存在缓存差异。运行计时时,运行两次查询。第一个可以做第二个不需要做的 I/O。

  • 当它使用索引时,它需要到达数据的 BTree 1000 次。这些随机提取可能是磁盘命中。

  • 其他...innodb_buffer_pool_size的值是多少;是表 InnoDB;你有多少内存;表有多大(以 GB 为单位)?

更多

timestamp(posting_date, posting_time) DESC, Creation DESC

在不是“sargable”。我怀疑这会更有效地为您提供相同的结果:

posting_date DESC, posting_time DESC, Creation DESC

一般来说,将日期时间分成两列是很笨拙的,而且在需要时从DATETIME(或TIMESTAMP)中提取部分要容易得多。我建议将posting_dateposting_time 组合成一个DATETIME(6)

之后,我建议添加

INDEX(warehouse, posting_datetime)

注意:posting_date &lt;= '2020-08-01' 包括 8 月 1 日的所有时间。posting_datetime microsecond of Aug 1. If you meant to exclude all of Aug 1, then changeto

优化器可能比我之前推荐的索引更喜欢这个。

2G..内存不足 4G

没错;确保你没有交换。

1000 条记录

你如何处理 1000 条记录?向网络用户展示的内容很多。

没有改善
查询全表仅需 0.1 秒

这些令人费解。请提供EXPLAIN FORMAT=JSON SELECT ...——这应该提供更多信息。

多拍2

Rows_sent:226657 Rows_examined:226657

这两个数字是相等的,因此查询部分几乎没有或根本没有浪费精力。它获取的每一行都被使用了。

还有一些事情……

  • 我认为这是避免排序,但是 EXPLAIN 没有那个细节。您能否运行 ANALYZE FORMAT=JSON SELECT ... 并尝试使用“优化器跟踪”(如果在您的版本中可用)。

  • 表相对于 buffer_pool_size 的大小仍有待研究。有了一个 2G 的 buffer_pool 和一个大小可能差不多的表,我担心查询是 I/O 绑定的。

要追逐最后一项,有两种可能性。每个人都旨在帮助这个查询,而不必帮助任何其他人。这个查询是否“更重要”?

  • 覆盖索引:INDEX(warehouse, posting_date, item_code, stock_value, name)。 (如果您尝试这样做,请使用EXPLAIN 确认正在使用该索引。)

  • PRIMARY KEY 和我的索引更改为:

      PRIMARY KEY(warehouse, posting_date, item_code, stock_value, name),
      UNIQUE(name)
    

当前的 PK(名称)仍然被索引,它的唯一性确认我的 PK 仍然是唯一的。但它也会“聚集”数据,这样查询就不会在表格中跳跃。

【讨论】:

  • 您好,谢谢回答.. 我已经用详细的解释结果、完整的表索引和 innodb_buffer 设置编辑了我的问题。根据您的建议添加索引(仓库,posting_date)但没有改进..如果我删除 Order By 子句,那么查询整个表(1M+ 记录)只需要 0.1 秒..那么如何加快 Order By 子句索引?再次感谢
  • @JonathanFLie - "Rows_sent: 226657" 需要几秒钟。我会看看我们是否可以加快速度一些。您将如何处理所有这些行?
  • 这是一个 web erp 系统,用于显示股票突变分类帐报告。我感到困惑的是,为什么查询似乎通过 mariadb 控制台运行得更快 4sec ..你知道吗?
  • 查询运行的两个时间是否相同? (检查时间戳。)我问这个是因为我想知道您报告的异常情况——(1)same 查询的不同时间与(2)相同的 SQL 在运行时运行得更慢或更快再次或(3)客户端导致不同的时间。
  • 相同的查询在相同的时间运行..如果我直接从 mariadb 控制台运行查询,它似乎更快。
猜你喜欢
  • 2017-09-02
  • 1970-01-01
  • 2016-03-23
  • 1970-01-01
  • 2019-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多