高并发场景下需要实现一个被频繁查询,读多写少的键值对(需要用键查询对应的值 & 知道前面还有多少个键值对是先于他创建且没有被删除的 ,所以数据结构需要是有序的),只需要先入先出(不需要从中间删元素),应该用什么比较好?

16 天前
 SeleiXi

目前想到的方案

  1. Redis List ,每个 element 对应一个 Hash Map
  2. 一个用 sync.RWMutex 实现共享锁的队列,每个 element 储存一个 Hash Map

但这两个还是得用 O(n)的时间复杂度去遍历,才能知道前面有多少个元素

[]{
    {"12345": true},
    {"23561": false},
    {"23562": false},
}

补充:业务场景储存的键本来就是有序的,且与创建时间相关。而值是一个 bool 值。感觉性能优化的难点还是在需要知道前面还有多少个键值对是先于他创建且没有被删除的,Redis List 的索引不会动态更新,因此没法在前面有成员被删除后知道前面实际还有多少个成员

1292 次点击
所在节点    程序员
12 条回复
sagaxu
16 天前
最差情况也就是内存里建 2 个索引,一个是 key 的,一个是时间的,读写都是 O(logN)
quickfox
16 天前
用 Hash 和 Sorted Set ,Hash 存 key 和 bool 值,再用 redis 的 soted list ,存 key ,时间作为分值,
添加:zadd zset_key "12345" {时间戳} 和 hset hash_key "12345" true
取第一个 key:zrange zset_key 0 0
删除指定的 key:zrem "12345" 和 hdel hash_key "12345"
sagaxu
16 天前
如果严格先入先出,可以设置一个自增 ID ,记录当前元素是第几个插入,再记录上一次取出的元素的 id ,相减就能算出前面有多少个元素
lesismal
16 天前
zset 足够了, timestamp 做 score, 没必要用其他的, 别想多了

> 要用键查询对应的值 & 知道前面还有多少个键值对是先于他创建且没有被删除的

zrank 查询对应的值的时候就会返回这个值的排序下标(整个 zset 排序下标从 0 开始), 默认 timestamp 升序, zrank 返回的值减 1 就知道前面有多少个是先于它了.
你保证每组操作原子性, 比如单次如果需要多个操作就 pipeline, 这样就能保证你每组操作在 redis 里是串行执行的, 所以你当前能查到的所有结果肯定就是对应的存在的未被删除的数据集的

> 所以数据结构需要是有序的),只需要先入先出(不需要从中间删元素)

zset 就是按 score 排序的, 你只要自己处理每次先删除第一个就行了
SeleiXi
16 天前
@lesismal !这个就是我想要的解法,一开始就是想这个方案想了好久,但是思考的时候默认把 score 和索引强绑定了,然后因为 score 不会动态更新所以就想着用其他方案了,感谢大佬
lesismal
16 天前
> 然后因为 score 不会动态更新所以就想着用其他方案了

@SeleiXi 更新用 pipeline zrem+zad, 先删再重新添加, 就可以了 🤣
SeleiXi
16 天前
@lesismal 不是这个问题 qwq 主要还是一开始不知道为啥想把 score 当成索引,但第一个一删那后面的索引全都得手动-1 了,忘了 zrank 能返回一个动态更新的 index
lesismal
16 天前
@SeleiXi #7 嗯嗯, 那就完全没问题了
ifplusor
16 天前
只用 zset 查不了 value 吧
ifplusor
16 天前
@ifplusor 又看了一下,value 是 bool 值,可以直接在时间戳后面加一位塞进去。
wei2629
15 天前
先入先出 不是个栈结构? []value 做栈 m[name]int 做索引 。 int 前面就是 有多少个。
kytrun
15 天前
精妙!

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://tanronggui.xyz/t/1107429

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX