【问题标题】:partitioning or separating a very large table in mysql在mysql中分区或分离一个非常大的表
【发布时间】:2014-03-01 17:07:23
【问题描述】:

我们在 mysql 中有一个非常大的表,其中有 500,000,000 条记录,每秒有 100 个请求 (SELECT)。
这是架构:

标识(整数), user_id (int), 内容(文本), 日期(日期时间)

因为多达 90% 的请求是在过去 6 个月内完成的。我的问题是关于提高性能。 将这些记录与另一个表中的过去 6 个月的记录分开并从中选择是一个好主意,或者使用分区方法快速获取过去 6 个月的所有记录。

或者如果有更好的方法......

例如,查询是 this。

SELECT content,user_id FROM log
JOIN users ON users.id = log.user_id
WHERE date > DATE_SUB(CURDATE(), INTERVAL 180 DAY)
LIMIT 15

user_id,日期索引在表Log
Users有200万用户。

【问题讨论】:

  • 如果你要拆分表,你将如何处理剩下的 10%?特别是,当日期范围同时属于两个时间范围时,一张表无法涵盖它。
  • MERGE 存储引擎怎么样??
  • 你有日期索引吗?
  • 是的,我有一个日期索引,但仍然需要很长时间才能阅读。 10 秒的简单选择。
  • @Alexander 我想我可以为每次插入写两个表。并在辅助表上创建一个 EVENT 以自动删除日期 > 6 个月的记录,您怎么看?

标签: mysql sql


【解决方案1】:

您的编辑表明您以每小时百万分之一的速度使用此类查询。

 SELECT content,user_id 
   FROM log
   JOIN users ON users.id = log.user_id
  WHERE date > DATE_SUB(CURDATE(), INTERVAL 180 DAY)
  LIMIT 15

我将冒昧地重写此查询以完全限定您的列选择。

 SELECT log.content,
        log.user_id 
   FROM log                                  /* one half gigarow table */
   JOIN users ON users.id = log.user_id      /* two megarow table */
  WHERE log.date > DATE_SUB(CURDATE(), INTERVAL 180 DAY)
  LIMIT 15

(如果这不正确,请考虑更新您的问题。)

您为什么要在此查询中加入users 表?你的结果似乎都不是来自它。为什么这个查询不能满足你的需要?

 SELECT log.content,
        log.user_id 
   FROM log                                  /* one half gigarow table */
  WHERE log.date > DATE_SUB(CURDATE(), INTERVAL 180 DAY)
  LIMIT 15

如果您想让这个查询更快,请在(date,user_id, content) 上放置一个复合覆盖索引。该覆盖索引将支持范围扫描和快速检索。如果您的content 列实际上是TEXT 类型(一个LOB)类型,您只需将(date,user_id) 放入覆盖索引中,您的检索会慢一些。

您是否使用JOIN 来确保返回的日志条目与users? 中的条目匹配如果是,请更好地解释您的查询。

您绝对可以根据日期范围对表进行分区。但是您将需要更改您的表,或者重新创建并重新填充它,这将导致停机或巨大的混乱。

http://dev.mysql.com/doc/refman/5.6/en/partitioning-range.html

这样的 DDL 应该可以为您解决问题

CREATE TABLE LOG (
  id         INT NOT NULL AUTO_INCREMENT,  /*maybe BIGINT? */
  user_id    INT NOT NULL,
  `date`     DATETIME NOT NULL,
  content    TEXT,
  UNIQUE KEY (id, `date`),
  KEY covering (`date`,user_id)
) 
PARTITION BY RANGE COLUMNS(`date`) (
    PARTITION p0 VALUES LESS THAN ('2012-01-01'),
    PARTITION p1 VALUES LESS THAN ('2012-07-01'),
    PARTITION p2 VALUES LESS THAN ('2013-01-01'),
    PARTITION p3 VALUES LESS THAN ('2013-07-01'),
    PARTITION p4 VALUES LESS THAN ('2014-01-01'),
    PARTITION p5 VALUES LESS THAN ('2014-07-01'),
    PARTITION p6 VALUES LESS THAN ('2015-01-01'),
    PARTITION p7 VALUES LESS THAN ('2015-07-01')
);

请注意,UNIQUE KEY 存在一些问题。进入分区函数的列也需要出现在所谓的主键中。

稍后,当 2015 年 7 月(分区 p7 的截止日期)临近时,您可以运行此语句为接下来的六个月时间段添加分区。

   ALTER TABLE `log` 
 ADD PARTITION (PARTITION p8 VALUES LESS THAN ('2016-01-01'))

但是,说真的,如果您的查询有不必要的连接或索引覆盖率低,那么这些分区垃圾都不会有太大帮助。这将使您的数据库管理更加复杂。

【讨论】:

  • 好吧,我joinusers表从users表中获取每一行的user_id信息。事实上,我不确定SELECT & JOIN 在一起更快还是SELECT user_id,content FROM log 填充users_ids 列表,然后再做一个SELECT information FROM users 来获取那些users 信息。
  • 你可以在应用程序中做到这一点吗?取决于用户数量,但我的大多数应用程序都有按 ID 定期更新的用户缓存,因为它们是静态的(当 id 不在缓存中时,缓存会命中数据库)。无需将这些东西推送到数据库 - 但这在很大程度上取决于您拥有的用户类型(facebook 与较小的公司会计系统不同)。我使用大量静态查找数据(其中一些长达一百万行)来做到这一点。
  • 那么,您没有展示您运行的实际查询的示例吗?如果你有几十行,那没关系。 LIMIT 15 的目的是什么?你实际上想要完成什么?因为你在 gigarow 范围内,所以这一切都很重要。
  • 不,我写了我的查询,我有超过 5 亿行。我在这篇文章的最后评论中提到过,我不知道有什么区别。你的意思是我不应该加入这么大的桌子?
  • 没有从不这样的东西。如果您要加入它们,两个表上的索引都很重要。存储引擎(InnoDB?)和其他细节也是如此。为什么要在 180 天前的午夜之后选择任意的 15 行日志行?你告诉的越多,这里的人就越能帮助你。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-06-26
  • 2023-04-05
  • 2023-03-07
  • 2010-10-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多