2016-10-22 66 views
2

我们正在开发的应用程序每天要编写大约4-5百万行数据。而且,我们需要在过去90天内保存这些数据。存储旧数据以更快访问的更好方式

user_data具有以下结构(简化):

id INT PRIMARY AUTOINCREMENT 
dt TIMESTAMP CURRENT_TIMESTAMP 
user_id varchar(20) 
data varchar(20) 

关于应用程序:

  • 数据是旧超过7天将不会被写入/更新。
  • 数据大多基于user_id访问(即所有查询将具有WHERE user_id = XXX
  • 目前大约有13000个用户。
  • 用户仍然可以访问较旧的数据。但是,在访问旧数据时,我们可以限制他/她只能获取全天数据而不是时间范围。 (例如,如果用户试图获取2016-10-01的数据,他/她将获取全天的数据,并且无法获取2016-10-01 13:00 - 2016-10的数据-01 14:00)。

目前,我们正在使用MySQL InnoDB存储的最新数据(即7天,较新的),它工作正常,并在innodb_buffer_pool适合。

至于较旧的数据,我们以user_data_YYYYMMDD的形式创建了较小的表格。过了一段时间,我们发现这些表格不适合innodb_buffer_pool,它开始放慢速度。

我们认为基于日期分离/分片,基于user_ids的分片会更好(即使用基于用户和日期的较小数据集,例如user_data_[YYYYMMDD]_[USER_ID])。这将使桌子保持更小的数量(最多只有10K左右)。

围绕研究后,我们发现有出有几个选项:

  • 使用MySQL表每日期的用户(即user_data_[YYYYMMDD]_[USER_ID])来存储。
  • 使用MongoDB的集合每个user_data_[YYYYMMDD]_[USER_ID]
  • 写旧数据(JSON编码)到[USER_ID]/[YYYYMMDD].txt

最大的骗子我在这看到的是,我们将拥有的表/收藏/文件数量巨大的时候,我们这样做(即13000 x 90 = 1.170.000)。我想知道我们是否在未来的可扩展性方面接近正确的方式。或者,如果有其他标准化的解决方案。

回答

0

100万个表格听起来像一个坏主意。在运行时通过应用程序代码通过动态表命名进行分片对于我来说也不是一个有利的模式。我对这类问题的第一次尝试是分区。您可能不希望单个未分区表中的400M +行。在MySQL 5.7中,你甚至可以进行子分区(但这会变得更复杂)。我首先会在日期字段上划分分区,每天分区一次。在user_id上索引。如果你在5.7版本并且想要涉及子分区,我会建议按日期进行范围分区,然后通过user_id散列子分区。作为一个起点,尝试16到32个散列桶。仍然索引user_id字段。

编辑:这里的东西一起玩:

CREATE TABLE user_data (
    id INT AUTO_INCREMENT 
    , dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP 
    , user_id VARCHAR(20) 
    , data varchar(20) 
    , PRIMARY KEY (id, user_id, dt) 
    , KEY (user_id, dt) 
) PARTITION BY RANGE (UNIX_TIMESTAMP(dt)) 
    SUBPARTITION BY KEY (user_id) 
    SUBPARTITIONS 16 (
    PARTITION p1 VALUES LESS THAN (UNIX_TIMESTAMP('2016-10-25')), 
    PARTITION p2 VALUES LESS THAN (UNIX_TIMESTAMP('2016-10-26')), 
    PARTITION p3 VALUES LESS THAN (UNIX_TIMESTAMP('2016-10-27')), 
    PARTITION p4 VALUES LESS THAN (UNIX_TIMESTAMP('2016-10-28')), 
    PARTITION pMax VALUES LESS THAN MAXVALUE 
); 

-- View the metadata if you're interested 
SELECT * FROM information_schema.partitions WHERE table_name='user_data'; 
+0

谢谢,约书亚。一定会尝试探索更多关于PARTITION的内容。 –

1

缩放数据库是一个独特的问题到应用程序。大多数时候别人的方法都不能使用,因为几乎所有的应用程序都以自己的方式写入数据。所以你必须弄清楚你将如何管理你的数据。

话虽如此,如果你的数据继续增长,最好的解决办法是shadring在那里你可以在不同的服务器上分配数据。只要绑定到单个服务器上,像创建不同的表,就会受到内存,存储和处理能力等资源限制的影响。那些不能无限增加的方式。

如何分配数据,你必须根据自己的业务使用情况弄清楚。正如你所提到的,如果你没有对旧数据提出更多请求,那就是按日期分发数据库的最佳方式。像2016年的数据库,2015年的数据库等。稍后,您可以清除或关闭拥有更多旧数据的服务器。

0

这是一张大桌子,但不是难以管理。

如果USER_ID + DT是独一无二的,使之成为主键,摆脱如果id,从而节省了空间。 (更多在一分钟内...)

user_id标准化为SMALLINT UNSIGNED(2字节)或更安全MEDIUMINT UNSIGNED(3字节)。这将节省大量的空间。

节省空间对于大型表格的速度(I/O)很重要。

PARTITION BY RANGE(TO_DAYS(dt)) 

与92分区 - 你需要的90,加上1等待DROPped和一个正在填补。看详情here

ENGINE=InnoDB 

得到PRIMARY KEY集群。

PRIMARY KEY(user_id, dt) 

如果这是“唯一”,那么它允许对单个用户的任何时间范围进行有效访问。注意:您可以删除“只需一天”的限制。但是,您必须必须制定查询而不隐藏dt在函数中。我建议:

WHERE user_id = ? 
    AND dt >= ? 
    AND dt < ? + INTERVAL 1 DAY 

此外,

PRIMARY KEY(user_id, dt, id), 
INDEX(id) 

也将是有效的,即使(USER_ID,DT)不是唯一的。 PK的加入id就是让它独一无二; INDEX(id)的补充是保持AUTO_INCREMENT高兴。 (不,UNIQUE(id)不是必需的。)

INT --> BIGINT UNSIGNED ?? 

INT(这是SIGNED)将在大约2十亿排在前列。这将在几年内发生。这可以吗?如果不是,您可能需要BIGINT(8字节与4)。

此分区设计不关心您的7天规则。您可以选择保留规则并在您的应用中执行该规则。

BY HASH 

工作为好。

SUBPARTITION 

一般没用。

还有其他疑问吗?如果是这样,他们必须同时考虑

如果单个服务器的流量过多,则通过user_id进行分片将非常有用。 MySQL本身并不具备分片解决方案。

+0

谢谢你的详细解释。我一定会考虑PARTITION。我很好奇,如果所有的用户都在一个表中(PARTITION),当同时阅读说同一日期范围内的不同用户时,它会有什么样的锁定? –

+0

锁定在InnoDB的行级。因此,对单独用户的查询之间没有干扰(除了整个系统忙)。 –

+0

在使用InnoDB时,我正在寻找表格大小(每天大约2GB x 90 = 180GB)不能适应'innodb_buffer_pool'的可能性。这是否会影响分区的查询速度,因为我需要基于'user_id'在'dt'上进行搜索? –

相关问题