最终一致的读,用户也会碰到“我刚改的怎么没了”“怎么刷新一下又变旧了”“先看到回复后看到帖子”这些怪事。读己之写、单调读、因果一致这三个会话保证怎么实现?各自要付出什么?这篇用一主两从的 Redis 和客户端自己的实现,把每种异常和对策量出来。
读法 | 读到不是自己刚写的值(旧值) | 由 replica 服务的读占比 | 单次读平均耗时 ---------------------------------------------------+----------------+------------------+-------- 随机选一个 replica 读 | 184 / 1500 | 100% | 0.30 ms 写后 50 ms 内读 master,之后读 replica | 0 / 1500 | 40% | 0.33 ms 偏移量令牌:replica 的偏移量 ≥ 写入时 master 的偏移量才读它,否则读 master | 0 / 1500 | 90% | 0.49 ms 随机选 replica:慢 replica(R2)落后约 30 ms,写后 0、20 ms 时读它必然是旧的,读 R1 也可能旧;“写后 50 ms 内读 master”要求复制延迟小于 50 ms,否则仍会读到旧值(R2 延迟 30 ms,所以这里恰好够);令牌法不依赖延迟上界,代价是每次读多问一次 replica 的偏移量
写完在一段时间里读 master,或带上写入时的偏移量令牌、只读已追上的 replica;前者依赖延迟上界,后者多问一次偏移量。
读法 | 读到的值比上一次小(倒退)的次数 | 读到的值平均比 master 当前值落后多少 | 改读 master 的次数 ----------------------------+------------------+------------------------+-------------- 每次随机选一个 replica | 99 | 2.9 | 0 会话固定读同一个 replica(这次随机抽到 R2) | 0 | 5.9 | 0 记住读到过的最大值,读到更旧的就改读 master | 0 | 0.2 | 189 随机选 replica 会让会话在快慢两个 replica 之间来回跳,值倒退;固定到一个 replica 就不会倒退(同一个 replica 内部是有序的),但固定到慢的那个会一直更旧,而且这个 replica 挂了要换;“记住最大值”的做法不倒退,代价是读到旧值时要多读一次 master
随机在快慢 replica 之间换会让值倒退;固定一个 replica 保顺序但可能一直更旧;记住读到的最大值、更旧时改读 master,兼顾顺序与新鲜度。
读法 | 看到回复却读不到它依赖的帖子的次数 | 等待依赖的次数 | Carol 读两个值平均耗时 -----------------------+-------------------+---------+--------------- 不检查依赖:读到回复就直接去读帖子 | 57 / 300 | 0 | 0.54 ms 检查依赖:读到回复后,帖子的版本不够就等到够 | 0 / 300 | 65 | 2.77 ms 两个分片各自复制、延迟不同,互相之间没有顺序;因果关系要靠回复里带的依赖版本(这里是帖子号)在读的时候检查,等不到才继续读,代价是在慢 replica 上等
两个分片各自复制,先写的帖子可能比后写的回复晚到;回复里带上依赖的帖子版本,读的时候检查,不够就等。