访问给定哈希对象的随机项(或第一个或最后一个)没有技巧。
如果您需要迭代哈希对象,您有几种可能性:
第一个是用另一种可以切片的数据结构(如列表或 zset)补充散列。如果您只在散列中添加项目(并迭代删除它们),那么一个列表就足够了。如果您可以添加/删除/更新项目(并迭代删除它们),则需要一个 zset(将时间戳作为分数)。zset 的两个列表都可以切片(lrange、zrange、zrangebyscore),因此很容易逐块迭代它们,并保持两个数据结构同步。
第二个是用另一种支持类似pop操作的数据结构来补充散列,例如列表或集合(lpop、rpop、spop)。您可以从二级结构中弹出所有对象并相应地维护哈希对象,而不是迭代哈希对象。同样,两种数据结构都需要保持同步。
第三种是将散列对象拆分为多个部分。这实际上是节省内存的,因为您的密钥只存储一次,并且 Redis 可以利用ziplist 内存优化。
因此,不要将您的哈希存储为:
myobject -> { key1:xxxx, key2:yyyyy, key3:zzzz }
你可以存储:
myobject:<hashcode1> -> { key1:xxxx, key3:zzzz }
myobject:<hashcode2> -> { key2:yyyy }
...
要计算额外的哈希码,您可以在密钥上应用任何哈希函数,以提供良好的分布。在上面的例子中,我们假设 key1 和 key3 具有相同的 hashcode1 值,而 key2 具有 hashcode2 值。
您可以在此处找到有关此类数据结构的更多信息:
Redis 内存使用量比数据多 10 倍
应计算散列函数的输出基数,以便将每个散列对象的项目数限制为给定值。例如,如果我们选择每个散列对象有 100 个项目,并且我们需要存储 1M 项目,我们将需要 10K 的基数。为了限制基数,简单地对通用散列函数使用模运算就足够了。
好处是它将在内存中紧凑(使用 ziplist),并且您可以通过在所有对象上流水线化 hgetall+del 轻松地对哈希对象进行破坏性迭代:
hgetall myobject:0
... at most 100 items will be returned, process them ...
del myobject:0
hgetall myobject:1
... at most 100 items will be returned, process them ...
del myobject:1
...
因此,您可以使用由散列函数的输出基数确定的粒度逐块迭代。