假设一个聊天应用程序有1000万Firebase用户和数亿条消息。
我有一个Firestore集合,其中包含以时间序列中的文档表示的消息,其中每个消息最多可以被100个用户接收和查看。请注意,这些用户没有被组织在稳定的组中,因为每条消息可能有一组完全不同的用户接收它
我需要能够非常有效地(在时间和成本方面)找到,在某个特定时间之后的所有消息,指向某个特定用户。
我的第一次失败尝试是在recipients
数组字段中列出收件人用户,例如:
sender: user3567381
dateTime : 2019-01-24T20:37:28Z
recipients : [user1033029, user9273842, user8293413, user6273581]
然而,这将不允许我高效地进行查询。
作为第二次失败的尝试,由于Firestore是无计划的,我考虑让每个用户都有一个字段,如下所示:
sender: user3567381
dateTime : 2019-01-24T20:37:28Z
user1033029 : true
user9273842 : true
user8293413 : true
user6273581 : true
例如,如果我想知道今天下午3点之后用户8293413的所有消息,我可以这样做:
messages.where("user8293413", "==", true).where("dateTime", ">=", "2019-01-24T15:00:00Z")
这是一个复合索引查询,每个用户需要一个索引。不幸的是,每个数据库有200个复合索引的限制。
为了解决这个问题,我的当前尝试是将日期转换为用户字段的值,如下所示:
sender: user3567381
dateTime : 2019-01-24T20:37:28Z
user1033029 : 2019-01-24T20:37:28Z
user9273842 : 2019-01-24T20:37:28Z
user8293413 : 2019-01-24T20:37:28Z
user6273581 : 2019-01-24T20:37:28Z
现在,如果我想知道今天下午3点之后用户8293413的所有消息,我可以这样做:
messages.where("user8293413", ">=", "2019-01-24T15:00:00Z")
请注意,这现在是单个字段索引。
从文档中我知道Firestore将为所有字段创建单个字段索引,因此这意味着它将为user8293413创建特定的索引。这意味着搜索会很快,对吧?并且读取次数将保持在最低限度(每条消息读取一次)。
但是,由于我有1000万用户,Firestore将不得不为整个数据库创建1000万个单字段索引(假设所有用户都收到消息)。
从文件来看,Firestore有以下限制:
- 数据库的最大复合索引数:200
- 数据库的单个字段索引豁免的最大数目:200
- 每个文档的最大索引条目数:40000(索引条目数是文档的以下各项之和:单个字段索引条目数+复合索引条目数)
- 索引项的最大大小:7.5 KiB
- 文档索引项大小的最大总和:8 MiB(文档的总大小是以下各项的总和:文档的单个字段索引项大小之和+文档的组合索引项大小总和)
- 索引字段值的最大大小:1500字节(超过1500字节的字段值将被截断。涉及截断字段值的查询可能返回不一致的结果。)
通过阅读以上内容,这些内容引起了我的注意:
- 每个文档的最大索引条目数:40000
- 文档索引项大小的最大总和:8 MiB
但是,他们指出限制是针对每个文档的,而不是针对每个数据库的。我只有数百万个数据库索引,而不是每个文档的索引。
这是个问题吗?那么多索引会影响性能吗?所有这些索引的存储成本如何?Firebase是否为每个数据库的大量索引总数做好了准备?
尽管几个月后,对于任何未来的用户来说,第一次尝试似乎效果最好。
使用单个静态字段作为时间戳,使用单个静态域作为收件人,这意味着索引可以忽略不计,您不必考虑它们。
查找用户的所有消息,这似乎是你的目标:
例如,如果我想知道用户8293413在今天下午3点,我可以这样做:
这在伪代码:中看起来就像这样
firestore.collection('messages').where('recipient', 'array_contains', userId).where('time', '>', '3pm today'.get()
这在性能上应该很容易,Firebase针对其提供的运算符进行了优化,例如"=="、">="、"array_contents">