【问题标题】:Manage MySQL partitions with Hibernate使用 Hibernate 管理 MySQL 分区
【发布时间】:2015-10-14 13:23:10
【问题描述】:

我们目前正在评估我们的一个小型应用程序是否使用 MySQL 分区。该应用程序基本上只是位于消息队列的末尾,并使用 Hibernate 将我们的 API 请求(包括时间戳)记录到数据库中。不幸的是,我们收到了很多请求,查询数据库变得非常慢。

我们想做的是按时间戳(每月)对表进行分区,因为我们的常规查询模式类似于“在时间 A 和 B 之间获取某些请求”。如果 A 和 B 连续两个月,这在大多数情况下是正确的,那么这只会命中两个分区。

由于 MySQL 的范围分区必须手动创建,我想将此维护任务添加到我们的 Java 应用程序中,以便自动完成。这个想法是这样的:

  1. 拥有一个定期运行的实用线程(使用ScheduledExecutorService 或其他东西)
  2. 在线程中,检查下个月是否有分区
  3. 如果没有,请创建它

没关系,但我一直在尝试使用 Hibernate 获取 MySQL 的分区信息并创建分区。最好的方法是什么(如果这将是 MySQL 特定的,我可以)?

  • Hibernate 中是否有特定的 API 来获取表的 MySQL 分区信息以及创建分区?
  • 我应该使用原始 SQL(SHOW CREATE TABLE ...ALTER TABLE ... ADD PARTITION)并自己解析输出吗?

编辑:

表格如下(我删除了一些与问题无关的列):

CREATE TABLE `request` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `apikey` varchar(32) NOT NULL,
  `timestamp` datetime NOT NULL,
  `rows` int(11) DEFAULT NULL,
  `user_id` varchar(15) DEFAULT NULL
  PRIMARY KEY (`id`),
  KEY `apikey_idx` (`apikey`),
  KEY `timestamp_idx` (`timestamp`),
  KEY `apikey_timestamp_rows_idx` (`apikey`,`timestamp`,`rows`)
) ENGINE=InnoDB AUTO_INCREMENT=2190385211 DEFAULT CHARSET=utf8

慢查询是(显然是由 Doctrine 生成的):

SELECT 
  r0_.user_id AS user_id0, COUNT(r0_.id) AS sclr1
FROM
  request r0_
WHERE
  r0_.apikey = 'XXX' AND r0_.rows > 0 AND r0_.timestamp >= '2015-09-15 00:00:00' AND r0_.timestamp < '2015-10-15 00:00:00'
GROUP BY r0_.user_id
HAVING sclr1 > 0
ORDER BY sclr1 DESC
LIMIT 500

EXPLAIN查询MySQL说它正在使用apikey_timestamp_rows_idx索引。

一点上下文:我们想知道,对于给定的 API 密钥,每个用户在给定的时间段内发送了多少 rows &gt; 0 请求。

该表目前大约有 22 亿行。

【问题讨论】:

  • 让我们看看实际的查询,并 SHOW CREATE TABLE。分区不一定比复合索引做得更好。
  • 我将表架构和查询添加到我的问题中

标签: java mysql database hibernate partitioning


【解决方案1】:

我不知道任何处理表分区的休眠 API。

我认为您别无选择,只能使用本机 SQL。您可以在 Java 代码中包含 SQL(正如我认为您所建议的那样),也可以将其放入存储过程中。

您可以使用 Java 或 MySQL 进行安排。如果您使用应用服务器中的线程来执行此操作,那么您的每个应用服务器都会有这样的计划作业。这使得控制作业实际执行的频率变得困难(呃)。在这种情况下,这可能没什么大不了的,因为与分区相关的查询不是很繁重。

您也可以在 MySQL 中安排它(请参阅How to schedule a MySQL query?)。此选项可以提供对工作的更多可见性(例如,对您的 DBA)并且更易于管理和监控。

【讨论】:

    【解决方案2】:

    我认为分区没有帮助。您必须扫描 lot 行;这就是慢。

    KEY `apikey_idx` (`apikey`),
    KEY `apikey_timestamp_rows_idx` (`apikey`,`timestamp`,`rows`)
    

    第一个不需要,因为第二个。删除第一个。 (这将加快 INSERT。)

    apikey 闻起来像某种哈希;是吗?是十六进制的吗?您可以通过 UNHEXing 并将其存储到 BINARY(16) 中(在所有使用 apikey 的表中)来节省大量磁盘空间。 (更小 --> 更少 I/O --> 更快。​​)

    假设插入后行不会改变...我会建立一个“汇总表”来存储

    • 日期(来自timestamp
    • rows>0 与否
    • apikey
    • COUNT(*)

    从该汇总表中,等效的 SELECT 将运行快得多

    考虑为类似的其他查询构建(并逐步维护)汇总表。

    我认为 Hibernate 妨碍了思考存储和检索数据的最佳方式。

    【讨论】:

    • 查询汇总表当然会非常快,但是构建它们会花费很多时间,那有什么好处呢?我对分区的想法是这样的:表很大,但包含很多我们不关心的数据(目前)。所以如果我们关心的所有数据都在一个或两个分区中(最近两个月),那么相关的索引、表文件等会变得更小,从而更容易缓存等。这不正确吗?
    • 汇总表初始化后,增量增加它们。比如午夜,用INSERT INTO Summary SELECT DATE(timestamp), apikey, rows&gt;0, COUNT(*) FROM Fact WHERE timestamp &gt;= CURRENT_DATE() - INTERVAL 1 DAY AND timestamp &lt; CURRENT_DATE() GROUP BY 1,2;总结昨天的数据
    猜你喜欢
    • 2012-10-29
    • 2011-08-04
    • 2017-10-30
    • 2016-12-02
    • 1970-01-01
    • 2019-05-20
    • 1970-01-01
    • 2011-08-08
    • 2010-10-18
    相关资源
    最近更新 更多