考虑以下表结构(77k 行和 237MB 转储大小,cart_data_json 列目前可以有 0.01k 到 50k 个字符)
CREATE TABLE `commerce_sales_test` (
`commerce_sales_id` varchar(32) NOT NULL,
`commerce_sales_date` bigint unsigned NOT NULL DEFAULT '0',
`total` bigint NOT NULL DEFAULT '0',
**`cart_data_json` mediumtext,**
PRIMARY KEY (`commerce_sales_id`),
KEY `fast_sdate` (`commerce_sales_date` DESC)
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED;
运行 COUNT(*) 非常快。
运行 SUM(total) 的执行速度确实很慢(慢 1-5 秒,具体取决于 WHERE 语句)。
使用 SQL_CALC_FOUND_ROWS 运行 Select 语句也非常慢(无论限制如何)。
无论 WHERE 语句如何,以下查询都会在 30 毫秒内运行(我可以完全删除 where 并且查询仍然运行得非常快)
SELECT COUNT(*)
FROM commerce_sales_test AS cs
WHERE cs.commerce_sales_date >= 1673222400
AND cs.commerce_sales_date <= 1699844800 ;
+----------+
| COUNT(*) |
+----------+
| 35884 |
+----------+
1 row in set (0.026 sec)
SELECT cs.commerce_sales_id, cs.cart_data_json, cs.total
FROM commerce_sales_test AS cs
WHERE cs.commerce_sales_date >= 1673222400
AND cs.commerce_sales_date <= 1699844800
LIMIT 0 , 15;
15 rows in set (0.003 sec)
以下查询将在 1-5 秒内运行,具体取决于 where 语句找到的行
SELECT COUNT(*), sum(cs.total)
FROM commerce_sales_test AS cs
WHERE cs.commerce_sales_date >= 1673222400
AND cs.commerce_sales_date <= 1699844800 ;
+----------+---------------+
| COUNT(*) | sum(cs.total) |
+----------+---------------+
| 35884 | 2631697732 |
+----------+---------------+
1 row in set (1.010 sec)
SELECT SQL_CALC_FOUND_ROWS cs.commerce_sales_id, cs.cart_data_json, cs.total FROM commerce_sales_test AS cs
WHERE cs.commerce_sales_date >= 1673222400 AND cs.commerce_sales_date <= 1699844800
LIMIT 0 , 15;
15 rows in set (1.879 sec)
我确实尝试将 cart_data_json 更改为 varchar(64000) ,但同样的问题仍然存在(对于我的情况,我可能需要超过 65k 的文本)
SELECT SQL_CALC_FOUND_ROWS cs.commerce_sales_id, cs.total
FROM commerce_sales_test AS cs
WHERE cs.commerce_sales_date >= 1673222400
AND cs.commerce_sales_date <= 1799844800
LIMIT 0 , 15;
+--------------------------+---------+
| commerce_sales_id | total |
+--------------------------+---------+
| 240109141243/5T47/4S5 | 183000 |
| 240107212543/5T28/9S386 | 224800 |
| 240107212533/5T29/13S384 | 52900 |
| 240107212405/5T29/13S384 | 1288000 |
| 240107212225/5T29/13S384 | 31300 |
| 240107212130/5T29/13S384 | 122500 |
| 240107212107/5T16/11S382 | 10700 |
| 240107211912/5T29/13S384 | 238700 |
| 240107211817/5T28/9S386 | 27000 |
| 240107211805/5T32/12S383 | 39700 |
| 240107211522/5T32/12S383 | 18900 |
| 240107211354/5T32/12S383 | 34300 |
| 240107211342/5T29/13S384 | 100100 |
| 240107211121/5T29/13S384 | 107300 |
| 240107210954/5T28/9S386 | 5000 |
+--------------------------+---------+
15 rows in set, 1 warning (1.556 sec)
我确实尝试在 cart_data_json MEDIUMTEXT 列上最多只放置 100 个字符,这极大地增加了所有查询。
目前我真正需要的是能够快速执行SUM,我该怎么办? 对于 SQL_CALC_FOUND_ROWS,我可以简单地运行单独的 count(*) 查询来检索总行数。
我猜测解决方案可能是增加一些内存设置?
非常感谢您的帮助。
这里有一些关于 InnoDB INDEXing 的课程:
SELECT COUNT(*) FROM ...
使用最小的 INDEX
(日期中的那个)。因此,从磁盘中获取的内容非常少(或者在 buffer_pool 中找到缓存)。SELECT ... big_column ...
需要为大(MEDIUMTEXT
中的json)进行额外的磁盘获取。这会增加很多时间。VARCHAR(64000)
没有影响,因为大型列的“非记录”存储仍然适用。SQL_CALC_FOUND_ROWS
根据 LIMIT
停止传送行,但随后继续获取(并丢弃)其余行。SQL_CALC_FOUND_ROWS
不应再使用——MySQL 8 已将其从语言中删除。所以,你的两个查询替代方案很好。此外(见上文),简单地执行 COUNT(*)
而不是列出列(包括 json)要快得多。LIMIT
和没有 SQL_CALC_FOUND_ROWS
最多有 15 个非记录查找。sum(cs.total)
的 LIMIT
)花费了中等时间,因为它必须在 INDEX
BTree 和数据 BTree 之间跳转 77K 次。通过仅在日期上提供当前索引的“覆盖”INDEX(commerce_sales_date, total)
而不是,可以大大加快速度。 “覆盖”消除了“来回弹跳”。innodb_buffer_pool_size
应设置为可用 RAM 的 70% 左右。如果当前小于 500M,那么您可能会受益于增加设置(并在必要时增加 RAM)。 (500M 是根据您的 237MB 粗略估计的。)