好的,我对此进行了一些思考。这种情况有几件棘手的事情:
序列号进来,并被分配(但不是马上)。随着每年 100 万个的增长,每天大约有 2800 个新的序列号。将它们排队(当它们进入时将它们插入并在分配它们时将它们删除)将创建大量墓碑(基本上每天 2800 个)。
如果有 50 个 UPC 与 1000 万个序列号相关,那么每个 UPC 有 200k 个序列号(假设分布均匀)。这意味着我们无法将 UPC 与序列号的关系存储在一个集合中(最大大小为 65536 个项目)。
我假设您希望能够弄清楚哪些序列号与哪些型号相关联,以及哪些型号具有哪些序列号。为此,我会使用两个查找表:
CREATE TABLE serialNumbersByUPC (
modelUPC uuid,
insertTime timeuuid,
serialNumber text,
PRIMARY KEY (modelUPC,insertTime))
WITH CLUSTERING ORDER BY (insertTime DESC);
CREATE TABLE UPCsBySerialNumbers (
modelUPC uuid
insertTime timeuuid,
serialNumber text,
PRIMARY KEY (serialNumber));
请注意,您还可以使用serialNumber 作为集群键(而不是insertTime)来键入serialNumbersByUPC。但是timeuuids 是独一无二的(所以serialNumbers 上不会发生冲突),并且insertTime 的聚类具有允许您按日期/时间排序的额外好处。当然,在将序列号分配给 UPC 时,您需要确保对这两个表都进行了 upsert。
对于未分配的序列号,最好使用HornetQ 或RabbitMQ 等排队系统。这样您就可以从队列中提取新的序列号,并根据需要分配它们。我建议这样做的原因是,使用 Cassandra 对瞬态数据进行排队已被确定为一种反模式。
当然,您可以决定不注意上述警告,并坚持使用 Cassandra 来实现该功能。如果是这样,那么 this 就是我在 Cassandra 中存储未分配序列号的方式:
CREATE TABLE unassignedSerialNumbers (
dateBucket bigint,
serialNumber text,
insertTime timeuuid,
PRIMARY KEY ((dateBucket),insertTime))
WITH compaction = {'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy'}
AND gc_grace_seconds = 86400;
关于此解决方案的几点说明:
我在datebucket 上进行分区,因为我不确定您分配每天收到的 2800 个序列号的速度有多快。您可能只想查询今天或昨天进来的数字。我已将其创建为 bigint,但您可以使用任何大小的存储桶(例如:“20150416”会将 2015 年 4 月 16 日传入的序列号划分在一起)。
如果您发现您分配序列号的速度足够快,以至于您不需要按datebucket 进行分区,那么我不会担心该表会变得足够大而影响查询性能。当然,您的删除操作会创建您查询必须处理的墓碑,但这应该有助于我的最后两点。
我在insertTime 上进行聚类,原因与我在serialNumbersByUPC 表中所做的相同。
我正在为这张桌子使用DateTieredCompactionStrategy。此策略将同时写入磁盘上相同的 SSTABLE 文件中的行。当您删除和写入新数据时,这对性能很重要。
gc_grace_seconds 被设置为 1 天而不是 10 天。这将强制每天对墓碑行进行垃圾收集。此设置的缺点是,如果您有一个节点宕机,您需要在节点宕机后 1 天内将其恢复以获取删除。如果您不这样做,您将需要进行全面修复,否则您删除的序列号可能会“起死回生”。
您还需要阅读DateTieredCompactionStrategy。可能还有一些其他选项可能对您有意义。
如果您有任何问题,或者我遗漏了什么,请告诉我。