【问题标题】:Database performance checklist数据库性能清单
【发布时间】:2015-07-09 22:29:14
【问题描述】:

在数据库中,我有一个包含订单项目的表。该表包含大约 3600 万条记录。

运行这样的查询大约需要 3 分钟

SELECT COUNT(DISTINCT DATE(created_on), product_id) FROM order_items;

运行这样的查询大约需要 13 秒

SELECT COUNT(1) FROM order_items;

有人告诉我,3600 万条记录并没有那么多,而且两个查询的运行速度都很慢。

在这里开始调查性能问题的清单是什么?

我们正在使用 MySQL(实际上是它的 Clustrix 版本,MySQL 5.0.45-clustrix-6.0.1)。

编辑。添加更多信息:

/* SHOW CREATE TABLE order_items; */
CREATE TABLE `order_items` (
  `id` int(10) unsigned not null AUTO_INCREMENT,
  `state` enum('pending','sold_out','approved','declined','cancelled','processing','completed','expired') CHARACTER SET utf8 not null default 'pending',
  `order_id` int(10) unsigned not null,
  `product_id` int(10) unsigned not null,
  `quantity` smallint(5) unsigned not null,
  `price` decimal(10,2) unsigned not null,
  `total` decimal(10,2) unsigned not null,
  `created_on` datetime not null,
  `updated_on` datetime not null,
  `employee_id` int(11),
  `customer_id` int(11) unsigned not null,
  PRIMARY KEY (`id`) /*$ DISTRIBUTE=1 */,
  KEY `updated_on` (`updated_on`) /*$ DISTRIBUTE=1 */,
  KEY `state` (`state`,`quantity`) /*$ DISTRIBUTE=3 */,
  KEY `product_id` (`product_id`,`state`) /*$ DISTRIBUTE=2 */,
  KEY `product` (`product_id`) /*$ DISTRIBUTE=1 */,
  KEY `order_items_quantity` (`quantity`) /*$ DISTRIBUTE=2 */,
  KEY `order_id` (`order_id`,`state`,`created_on`) /*$ DISTRIBUTE=3 */,
  KEY `order` (`order_id`) /*$ DISTRIBUTE=1 */,
  KEY `index_order_items_on_employee_id` (`employee_id`) /*$ DISTRIBUTE=2 */,
  KEY `customer_id` (`customer_id`) /*$ DISTRIBUTE=2 */,
  KEY `created_at` (`created_on`) /*$ DISTRIBUTE=1 */,
) AUTO_INCREMENT=36943352 CHARACTER SET utf8 ENGINE=InnoDB /*$ REPLICAS=2 SLICES=12 */

还有:

/* SHOW VARIABLES LIKE '%buffer%'; */
+----------------------------------------+-------+
| Variable_name                          | Value |
+----------------------------------------+-------+
| backup_compression_buffer_size_bytes   | 8192  |
| backup_read_buffer_size_bytes          | 8192  |
| backup_write_buffer_size_bytes         | 8192  |
| mysql_master_trx_buffer_kb             | 256   |
| mysql_slave_session_buffer_size_events | 100   |
| net_buffer_length                      | 16384 |
| replication_master_buffer_kb           | 65536 |
+----------------------------------------+-------+

编辑 2。这是两个查询的EXPLAIN 语句:

mysql> EXPLAIN SELECT COUNT(1) FROM order_items;
+----------------------------------------------------------+-------------+-------------+
| Operation                                                | Est. Cost   | Est. Rows   |
+----------------------------------------------------------+-------------+-------------+
| row_count "expr1"                                        | 29740566.81 |        1.00 |
|   stream_combine                                         | 26444732.70 | 32958341.10 |
|     compute expr0 := param(0)                            |  1929074.80 |  2746528.43 |
|       filter isnotnull(param(0))                         |  1915342.16 |  2746528.43 |
|         index_scan 1 := order_items.order_items_quantity |  1854308.19 |  3051698.25 |
+----------------------------------------------------------+-------------+-------------+
5 rows in set (0.13 sec)

还有:

mysql> EXPLAIN SELECT COUNT(DISTINCT DATE(created_on), product_id) FROM order_items;
+----------------------------------------------------------------------------------+-------------+------------+
| Operation                                                                        | Est. Cost   | Est. Rows  |
+----------------------------------------------------------------------------------+-------------+------------+
| hash_aggregate_combine expr1 := count(DISTINCT (0 . "expr0"),(1 . "product_id")) | 10115923.36 | 4577547.38 |
|   hash_aggregate_partial GROUPBY((0 . "expr0"), (1 . "product_id"))              |  3707357.04 | 4577547.38 |
|     compute expr0 := cast(1.created_on, date)                                    |  2166388.20 | 3051698.25 |
|       index_scan 1 := order_items.__idx_order_items__PRIMARY                     |  2151129.71 | 3051698.25 |
+----------------------------------------------------------------------------------+-------------+------------+
4 rows in set (0.24 sec)

【问题讨论】:

    标签: mysql database performance database-performance


    【解决方案1】:

    第一个查询必须遍历整个数据库,检查表中的每一行。 created_on 和 product_id 上的索引可能会显着加快速度。如果您不了解索引,http://use-the-index-luke.com 是一个很好的起点。

    第二个查询在我看来应该是即时的,因为它只需要检查表元数据,不需要检查任何行。

    【讨论】:

      【解决方案2】:

      您应该发布查询计划,但我怀疑要处理查询 MySQL 必须遍历 product_id 和 created_on 索引。对于 created_on 字段,它还必须聚合值(该字段是日期时间,但您想按日期分组)。如果您需要速度,我会添加额外的字段 created_on_date,只有日期,我会在 product_id 和created_on_date。它应该使您的查询更快。 当然 count(1) 查询更快,因为它根本不读取表,并且可以使用索引元数据。

      【讨论】:

        【解决方案3】:

        注意事项:

        • 如果添加INDEX(product_id, created_on),第一个查询应该运行得更快,因为它是一个“覆盖索引”。 (字段的顺序可以相反。)

        • 按给定的顺序运行这两个查询可能会导致信息被缓存,从而使 second 查询运行得更快。

        • SELECT COUNT(*) FROM tbl 将使用最小的索引。 (在 InnoDB 中。)

        • 如果您有足够的 RAM,并且如果innodb_buffer_pool_size 大于表,则其中一项或其他操作可能完全在 RAM 中执行。 RAM 比磁盘快很多。

        请提供SHOW CREATE TABLE order_items; 我猜的太多了。
        请提供SHOW VARIABLES LIKE '%buffer%';。你有多少内存?

        编辑

        因为它是 Clustrix,所以可能会发生完全不同的事情。这是一个猜测:

        • SELECT COUNT(1) ... 大概可以分发到节点上;每个节点都会得到一个小计;然后可以(非常快速地)添加小计。
        • SELECT COUNT(DISTINCT ...)... 真的必须以一种或另一种方式查看所有行。也就是说,努力不能被分配。 也许 发生的情况是所有行都被铲到一个节点上进行处理。我猜它是几 GB 的东西。

        Clustrix 中是否有某种方法可以获取EXPLAIN?我很想看看它对每个SELECTs 的描述。 (以及它是否支持我的猜测。)

        我预计 GROUP BYDISTINCT 在“分片”系统(例如 Clustrix)中效率低下。

        【讨论】:

        • 嗯?没有innodb_buffer_pool_size? (一旦我看到你有engine=InnoDB,我就去寻找那个设置。)
        • 嗯...我不熟悉 Clustrix 的细节——也许它们会覆盖 InnoDB?
        • Rick,感谢您进一步调查。我已经添加了两个 EXPLAIN 语句。
        • 嗯...我会假设 stream_combine 在执行 COUNT 之前合并所有 3300 万行。这似乎非常低效且“容易”优化。此查询的成本也更高。然而你的时间显示它要快得多。
        • 也许 hash_aggregate_partial 和/或 hash_aggregate_combine 比 Clustrix 的成本模型认为的成本更高。建议您将此线程显示给 Clustrix。
        【解决方案4】:
        • 计数(1)

        在 Plan 中,使用了 stream_combine。它只读取了索引 (order_items_quantity (quantity))

        • COUNT(DISTINCT DATE(created_on), product_id)

        一般来说 COUNT(DISTINCT...) 在 RDB 中可能效率低下,NewSQL Scale-Out RDB 更是如此,这是因为难以减少节点间流量(很多情况下很多数据要转发到 GTM 节点) )。所以 Clustrix 需要 'dist_stream_aggregate' 和正确的索引(列和列顺序)

        在计划中,显示了 hash_aggregate_partial。它已经扫描了 FULL TABLE (__idx_order_items__PRIMARY) 并花费了很多时间(更大的尺寸) 对于并行性,对于所有可用的 cpu,它可能不够多。 (即切片 = 12)。我想知道每个节点有多少个节点和 CPU(?)

        由于 DATE(created_on),索引 created_at (created_on) 将不起作用。优化器(计划)认为 FULL TABLE SCAN 比查找 INDEX(created_at) 然后访问 TABLE (__idx_order_items__PRIMARY) 更有效。

        对于这种情况,我建议进行如下测试。

        • 添加栏目create_on_date_type
        • order_items(create_on_date_type, productid) 上创建索引new_index 关于分发=? & slices=?,应该对您的数据集进行测试。(切片的数量可能会影响 cpu 并行度的工作量)
        • 您必须确保该计划有dist_stream_aggregatedist_stream_aggregate 只能使用您的查询的“new_index”列有效地工作。

        我相信你会得到更好的表现。

        【讨论】:

          猜你喜欢
          • 2020-06-29
          • 1970-01-01
          • 1970-01-01
          • 2013-09-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多