我们有一个报告数据表,其中有数百万条记录。数据将增量添加到表中。使数据的获取和插入达到最佳状态。目前表中我们有 1 个公司和 10 个用户的大约 100K 测试数据行。
我们正在测试不同的选项。
对 company_id 和 user_id 列应用单独的索引。 (查询执行时间:687ms)
对列(company_id、user_id)应用组合索引。 (查询执行时间:1.1s)
应用主分区(company_id、user_id、id)以及company_id和user_id上的两个单独索引。 (查询执行时间:2.6s)
我们已在工单中附上表格和“解释”查询结果。
理论上,分区结果应该比普通索引是最优的,但正如您在下面的结果中看到的,分区遍历的行数比没有分区的要高得多,我们猜测这是造成这种情况的主要原因 分区性能缓慢。
我们已点击此链接分区参考来了解和应用分区。
有人可以指导我们对于大量数据(例如数据)是否真的需要分区吗? 3000万条记录或者索引可以吗?
(1)
CREATE TABLE `table1` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`date` date DEFAULT NULL,
`time_group` timestamp NULL DEFAULT NULL,
`value` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_c_id` (`company_id`),
KEY `idx_u_id` (`user_id`)
)
EXPLAIN
select company_id, sum(value) as result
from table1
where company_id = 55
and user_id in (127, 128, 129, 130, 132, 133)
and (time_group between '2024-01-01 00:00:00' and '2024-01-30 23:59:59')
group by company_id
order by result desc;
(2)
(3)
CREATE TABLE `table2` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`company_id` int(11) NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`date` date DEFAULT NULL,
`time_group` timestamp NULL DEFAULT NULL,
`value` int(11) DEFAULT NULL,
PRIMARY KEY (`company_id`, `user_id`, `id`),
KEY `idx_id` (`id`)
) PARTITION BY HASH(company_id) PARTITIONS 11;
EXPLAIN
select company_id, sum(value) as result
from table2
where company_id = 55
and user_id in (127, 128, 129, 130, 132, 133)
and (time_group between '2024-01-01 00:00:00' and '2024-01-30 23:59:59')
group by company_id
order by result desc;
我们已遵循此分区参考中提供的解决方案。但与上面解释的简单索引相比,分区的结果相对较慢。我们错过了什么吗?
此外,带有分区的索引大小很大。
SELECT
database_name,
table_name,
index_name,
ROUND(stat_value * @@innodb_page_size / 1024 / 1024, 2) size_in_mb
FROM mysql.innodb_index_stats
WHERE stat_name = 'size'
AND index_name != 'PRIMARY'
and database_name = 'db'
ORDER BY size_in_mb DESC;
我推荐这个不分区:
ADD TABLE table2
DROP INDEX `idx_c_id`, -- in the way
ADD INDEX(company_id, user_id);
(另请参阅我的分区博客。)
如果(且仅当)您的大部分活动确实在月份范围内,那么
PARTITION BY RANGE(TO_DAYS(time_group)) ...
和
ADD TABLE table2
DROP INDEX `idx_c_id`, -- in the way
ADD INDEX(company_id, user_id), -- Optimizer may pick this
您的 MySQL 版本有多旧?我希望您的
EXLAIN
显示它使用(或未使用)的分区。对于旧版本,请使用 EXPLAIN EXTENDED SELECT ...
。
我推荐“月”范围的这种表述。 (没有性能差异;只是其他好处)
time_group >= '2024-01-01'
AND time_group < '2024-01-01' + INTERVAL 1 MONTH