我必须优化我的小数据库,因为它太慢了,也许我们会一起找到另一个解决方案。
首先让我们谈谈存储在数据库中的数据。有两个对象:users
,让我们说messages
用户的
有类似的东西:
+----+---------+-------+-----+
| id | user_id | login | etc |
+----+---------+-------+-----+
| 1 | 100001 | A | ....|
| 2 | 100002 | B | ....|
| 3 | 100003 | C | ....|
|... | ...... | ... | ....|
+----+---------+-------+-----+
此表内没有问题。 (不要害怕id
和user_id
。user_id
被其他应用程序使用,因此它必须在这里。)
消息
第二个表有一些问题。每个用户都有这样的消息:
+----+---------+------+----+
| id | user_id | from | to |
+----+---------+------+----+
| 1 | 1 | aab | bbc|
| 2 | 2 | vfd | gfg|
| 3 | 1 | aab | bbc|
| 4 | 1 | fge | gfg|
| 5 | 3 | aab | gdf|
|... | ...... | ... | ...|
+----+---------+------+----+
不需要edit
条消息,但应该有机会更新用户的消息列表。例如,外部服务将所有用户的消息发送到数据库,并且必须更新列表。
最重要的是,大约有30 Mio用户,普通用户有500多条消息。我必须搜索字段from
并计算匹配数的另一个问题。我设计了一个带连接的简单SQL查询,但获取数据需要花费太多时间。
所以......它的数据量非常大。我决定不使用RDS(我使用Postgresql)并决定转移到Clickhouse
等数据库。
但是我遇到了一个问题,例如Clickhouse
不支持UPDATE
语句。
要解决此问题,我决定将邮件存储为一行。因此表Messages
应该是这样的:
Here I'd like to store messages in JSON format
{"from":"aaa", "to":bbe"}
{"from":"ret", "to":fdd"}
{"from":"gfd", "to":dgf"}
||
\/
+----+---------+----------+------+ And there I'd like to store the
| id | user_id | messages | hash | <= hash of the messages.
+----+---------+----------+------+
我认为messages
列中的全文搜索会节省一些时间资源等等。
你有什么想法吗? :)
答案 0 :(得分:2)
在ClickHouse中,最佳方式是将数据存储在&#34;大平台&#34;中。 因此,您将每条消息存储在一个单独的行中。 即使在单个节点上,也可以为ClickHouse提供150亿行。
此外,让每个用户直接在消息表(预加入)中属性是合理的,因此您不需要进行JOIN。如果用户属性未更新,则适合。
这些属性将为每个用户提供重复的值。消息 - 它很好,因为ClickHouse可以很好地压缩数据,特别是重复的值。
如果用户&#39;属性更新,考虑将用户表存储在单独的数据库中并使用“外部词典”。加入它的功能。
如果消息已更新,请不要更新。将具有修改后的消息的另一行写入表中,并保留旧消息。
为您的桌子拥有正确的主键非常重要。您应该使用MergeTree系列中的表,该表不断按主键重新排序数据,因此可以保持范围查询的效率。主键不需要是唯一的,例如,如果您经常编写&#34; from = ...&#34;并且必须在短时间内处理这些查询,您可以将主键定义为(from)
您可以使用user_id作为主键:如果用户ID的查询频繁且必须尽可能快地处理,但随后在&#39;中使用谓词进行查询。将扫描整个表(请注意ClickHouse有效地进行全面扫描)。
如果您需要通过许多不同的属性快速查找,您可以只使用不同的主键复制表。通常情况下,该表格将被压缩得足够好,您可以为不同范围的查询提供具有不同顺序的少量副本数据。
答案 1 :(得分:0)
首先,当我们有这么大的数据集时,如果可能的话,from
和to
列应该是整数,因为它们的比较速度更快。
其次,您应该考虑创建适当的索引。由于每个用户的记录相对较少(500个与总共30M相比),因此它可以为您带来巨大的性能优势。
如果其他一切都失败了,请考虑使用分区:
https://www.postgresql.org/docs/9.1/static/ddl-partitioning.html
在你的情况下,它们将是动态的,并且阻碍第一次插入非常大,所以我认为它们只是最后一次,如果非常有效,那么。