redis排序集上的事务看起来不是原子的

gt0wga4j  于 2021-06-09  发布在  Redis
关注(0)|答案(1)|浏览(413)

我在redis中的一个流水线/事务中遇到了一些奇怪的行为,这让我怀疑代码实际上是在一个事务中执行的:

class RedisThread:

    KEY = "threading_test"

    def __init__(self, id:int):
        self._redis = Redis(host="localhost", port=6379, db=0)
        self._id    = id
        self._redis.delete(self.KEY)

    def __call__(self, *args,**kwargs):
        results = []
        for i in range(3):
            # Transaction
            pipe = self._redis.pipeline(transaction=True)

            # ZADD current time as score
            pipe.zadd(self.KEY, {f"{self._id}-{i}": time.time()})

            # ZRANK
            pipe.zrank(self.KEY, f"{self._id}-{i}")

            # Commit and get result of ZRANK
            results.append(str(pipe.execute()[1]))

        print(", ".join(results))

    threads = [
        threading.Thread(target=RedisThread(1)),
        threading.Thread(target=RedisThread(2)),
        threading.Thread(target=RedisThread(3)),
        threading.Thread(target=RedisThread(4)),
        threading.Thread(target=RedisThread(5)),
    ]

    for t in threads:
        t.start()

    for t in threads:
        t.join()

当我运行此代码时,结果如下:

1, 5, 9
3, 6, 10
0, 3, 13
4, 11, 12
1, 4, 13

请注意,线程之间存在重复的值。因为我正在做一个zrank,并且我添加到集合中的值(通过zadd)是基于时间的(因此值总是在增加),所以我不应该看到任何重复的,但是有重复的。。。

oxcyiej7

oxcyiej71#

redis事务是原子的。问题是你用 time() 作为得分。
因为程序运行得很快,所以 time() 对于多个调用可能返回相同的结果。换句话说,在排序集中设置具有相同分数的成员。
如果成员的分数相同,则按词典顺序排列。
现在,让我们看看为什么会有重复的值:

thread1: ZADD key same-time 1-0
thread1: ZRANK key 1-0 ----------> 0

thread2: ZADD key same-time 2-0
thread2: ZRANK key 2-0 ----------> 1

thread1: ZADD key same-time 1-1
thread1: ZRANK key 1-1 ----------> 1

thread1的第二个事务在thread2的第一个事务之后执行,并且 1-1 小于 2-0 按词典顺序。所以当thread1的第二个事务执行时, 1-1 将在之前插入 2-0 ,这就是为什么他们都获得了这个级别: 1 .

相关问题