【问题标题】:Big MySQL product history table partitioning?大 MySQL 产品历史表分区?
【发布时间】:2018-04-03 13:38:14
【问题描述】:

我正在开发用php,laravel框架,mariadb编写的仓库控制系统。要获取有关每个产品的所有信息,我们使用产品“历史”表,该表记录了对特定产品采取的所有操作。该表开始快速扩展,现在我们有大约 1500 万行 innoDB 表开始运行缓慢,尤其是在运行函数时,它需要对销售、创建、丢弃的产品数量等进行全面分析,然后它需要所有 1500 万行在一个查询上.. 所以我开始寻找方法,如何使用这个大表进行管理,因为索引不再起作用。 我开始考虑按日期拆分/分区此表,也许是行动?所以也许有人有这方面的经验,可以和我分享一些建议吗?非常感谢您的帮助!

CREATE TABLE `history` ( `id` int(11) NOT NULL AUTO_INCREMENT, `barcode` varchar(100) DEFAULT NULL, `bag` varchar(100) DEFAULT NULL, `action` int(10) unsigned DEFAULT NULL, `place` int(10) unsigned DEFAULT NULL, `price` decimal(10,2) DEFAULT NULL, `old_price` decimal(10,2) DEFAULT NULL, `user` int(11) DEFAULT NULL, `amount` int(10) DEFAULT NULL, `rotation` int(10) unsigned DEFAULT NULL, `discount` decimal(10,2) DEFAULT NULL, `discount_type` tinyint(2) unsigned DEFAULT NULL, `original` int(10) unsigned DEFAULT NULL, `was_in_shop` int(10) unsigned DEFAULT NULL, `cate` int(10) unsigned DEFAULT NULL COMMENT 'grupe', `sub_cate` int(10) unsigned DEFAULT NULL, `comment` varchar(255) DEFAULT NULL, `helper` varchar(255) DEFAULT NULL, `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, `deleted_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), KEY `barcode` (`barcode`), KEY `action` (`action`), KEY `original` (`original`), KEY `created_at` (`created_at`), KEY `bag` (`bag`) ) ENGINE=InnoDB AUTO_INCREMENT=16274267 DEFAULT CHARSET=utf8

例如查询:

select  cate,
SUM(amount) AS amount, SUM(IF(discount>0,(price*amount)-discount,
                    (price*amount))) AS sum, SUM(IF(discount>0,IF(discount_type=1,
                                            (discount*price)/100,discount),0)
   ) AS discount from  history
    where  (history.action = '4'
              and  history.created_at >= '2017-11-01 00:00:00'
              and  history.created_at <= '2017-11-23 23:59:59'
           )
      and  LENGTH(barcode) > 7
      and  history.deleted_at is null
    group by  cate

此查询用于获取有关已售产品的金额、总和、折扣信息(操作 4),在此示例中它是 2017-11-01 和 2017-11-23 之间的信息,EXPLAIN 给了我这个:

id - 1 select_type - SIMPLE table - history type - ref possible_keys - action,created_at key - action key_len - 5 ref - const rows - 1444272 Extra - Using where; Using temporary; Using filesort

所以它需要 150 万行,其中包含从 2017-01-01 到现在的数据的表,所以 2 年后它将需要 300 万行等等......当我只需要获取 2017-11 产品销售信息时.我还有很多类似的查询。

【问题讨论】:

  • “索引不再起作用”是什么意思?
  • 我已经有 5 个索引(附件图片右侧的表格)添加更多索引不再加快速度。
  • 在您的慢查询中添加EXPLAIN 是否支持这一假设?
  • 截图在这里几乎没用。请使用SHOW CREATE TABLE 描述您的情况并显示所涉及的查询。
  • 用附加信息更新了主要问题

标签: php mysql mariadb partitioning


【解决方案1】:
  • 使用较小的数据类型(缩小表大小有助于提高性能)INT 占用 4 个字节;也可提供其他尺寸。
  • PARTITIONing本质上提供任何性能。
  • history.deleted_at is null -- 考虑实际删除行。
  • 了解“复合”索引,例如 INDEX(action, created_at) 。 (一次只使用一个索引。)

最大的改进来自于构建和维护汇总表;见http://mysql.rjweb.org/doc.php/summarytables。然后对它们运行查询。而且大多数索引都可以消失。

修复其中一些;那么我可以进一步帮助你。

更多

评论询问如何以两种不同的方式维护汇总表 ID。两者都可行,具体取决于更多尚未指定的细节:

  • INSERT INTO Fact 表,并立即使用 IODKU 插入或更新 Summary 表。
  • “按需”进行汇总——当用户请求数据时,首先运行INSERT .. SELECT .. 以捕获尚未汇总的行并将计数/小计放入汇总表中。李>

后一种选择有效,但有两点需要注意:

  • 如果很长一段时间没有用户出现,那么汇总的成本可能会很高。简单的解决方法是定期“赶上”一个 cron 作业。确保将代码互锁,以使 cron 和用户不会同时更新相同的行。
  • 如果摘要表有一个“自然”PRIMARY KEY,例如日期(天或小时)和几个维度值,那么您可能会遇到INSERT 的问题。要么避免将其作为 PK(从而导致多行,这不是“坏”),要么使用 INSERT ... ON DUPLICATE KEY ... SELECT ... GROUP BY ...; 形式的 IODKU 并使用 VALUES(xx) 函数。

【讨论】:

  • 我已阅读有关汇总表的信息。我从这篇文章中了解到,我需要从历史记录到汇总表中插入...选择。但就我而言,我需要“最新”报告,因为所有统计报告都是“实时”的,因此推荐“按需”类型。所以我想问一下,每次刷新页面时从“历史”表中插入/更新汇总表,然后从这个新的汇总表中选择所有信息,而不是直接从“历史”表中选择该信息,是否更好?或者也许还有其他方法可以做到这一点?
  • @Tomas - 我提供了更多提示。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-10
  • 1970-01-01
  • 1970-01-01
  • 2017-07-23
  • 1970-01-01
  • 2015-11-11
  • 2014-02-26
相关资源
最近更新 更多