V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
SeleiXi
V2EX  ›  程序员

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

  •  
  •   SeleiXi ·
    SeleiXi · Jan 23, 2025 · 2391 views
    This topic created in 458 days ago, the information mentioned may be changed or developed.

    目前想到的方案

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

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

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

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

    12 replies    2025-01-25 02:01:34 +08:00
    sagaxu
        1
    sagaxu  
       Jan 23, 2025
    最差情况也就是内存里建 2 个索引,一个是 key 的,一个是时间的,读写都是 O(logN)
    quickfox
        2
    quickfox  
       Jan 23, 2025
    用 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
        3
    sagaxu  
       Jan 23, 2025
    如果严格先入先出,可以设置一个自增 ID ,记录当前元素是第几个插入,再记录上一次取出的元素的 id ,相减就能算出前面有多少个元素
    lesismal
        4
    lesismal  
       Jan 23, 2025   ❤️ 1
    zset 足够了, timestamp 做 score, 没必要用其他的, 别想多了

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

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

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

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

    @SeleiXi 更新用 pipeline zrem+zad, 先删再重新添加, 就可以了 🤣
    SeleiXi
        7
    SeleiXi  
    OP
       Jan 24, 2025
    @lesismal 不是这个问题 qwq 主要还是一开始不知道为啥想把 score 当成索引,但第一个一删那后面的索引全都得手动-1 了,忘了 zrank 能返回一个动态更新的 index
    lesismal
        8
    lesismal  
       Jan 24, 2025
    @SeleiXi #7 嗯嗯, 那就完全没问题了
    ifplusor
        9
    ifplusor  
       Jan 24, 2025 via iPhone
    只用 zset 查不了 value 吧
    ifplusor
        10
    ifplusor  
       Jan 24, 2025 via iPhone
    @ifplusor 又看了一下,value 是 bool 值,可以直接在时间戳后面加一位塞进去。
    wei2629
        11
    wei2629  
       Jan 24, 2025
    先入先出 不是个栈结构? []value 做栈 m[name]int 做索引 。 int 前面就是 有多少个。
    kytrun
        12
    kytrun  
       Jan 25, 2025 via Android
    精妙!
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   3384 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 55ms · UTC 12:53 · PVG 20:53 · LAX 05:53 · JFK 08:53
    ♥ Do have faith in what you're doing.