根据我的从业经验, 用户对一个(分布式)数据库系统, 最看重这些特性:
- 多副本, 数据安全性
- 能提供强一致性更好, 但基于性能考虑, 最终一致性(异步复制)有广泛的用户需求
- 节点容灾, 部分服务器故障, 不影响整体系统的运行
- 方案选择, 倾向于增加无状态 proxy 层
- "伪装"成一个单机数据库实例, 底层细节对用户隐藏
- 平滑扩容, 加机器不影响业务, 不需要业务在代码和架构上做改变
- 数据全球同步, 最终一致
- 一个大区的日志序列是顺序 apply, 但多个大区的日志序列是乱序 apply
- TTL, 能设置数据的过期时间, 定期清理数据释放空间, 主要是运维需求, 节省用户的运维工作量
- 不提供一致性, 及时性, 只有运维性
- TTL是一种外部运维特性, 所以在存储系统之外向存储系统发送删除指令, 是运维操作, 不是内在特性
- 分页操作, 例如 MySQL 的 limit, Redis 的 zrange, 虽然技术上性能局限性比较突出, 但好用, 用户喜欢用
- 批量操作(性能), 例如 mget, mset, mdel, 主要是方便用户使用, 性能也有优势
- Pipeline 也算批量操作
- 批量操作如果不承诺原子性, 可以在客户端实现
- 可选持久化策略
- auto: 只需要 write(), 不要求 fsync()
- strong: 共识必须在磁盘 fsync() 之后
- 可选写一致性级别
- 默认是单大区内(raft组)强一致性
- 默认大区间是最终一致性(跨地域同步)
- 提供 sync_write() 让用户等待同步进度, 在写数据之后调用
- 可选读一致性级别
- 默认是单大区内(raft组)强一致性
- 默认大区间是最终一致性(跨地域同步)
- 用户可选 stale read: 读单机
- 提供 sync_read() 记用户等待同步进度, 在读之前调用
- 提供事务能力(锁+快照), 多个写操作之间有关联和依赖
- 悲观事务(读写, Transaction), 交互式 2PC, 有 commit point
- begin() -> commit()
- 原子写入/乐观事务(只写, AtomicWrite), 非交互式 2PC, 写操作之间无因果依赖关系
- atomic() -> commit()
- 若有参与者 prepare 失败(包括 CAS 冲突)则全部回滚
- 相比悲观事务, 可以并发 prepare
- 批量写入(只写, MultiWrite), 1PC, 忽略 CAS 冲突, 最终全部会被执行, LWW
- multi() -> exec()
- 真正执行时, 如果对象当操作的时间戳更新, 则直接丢弃操作(认为操作已经完成, 然后被覆盖)
- hdel 操作的 CAS 比对的是元素的 mtime
- hclear 操作的 CAS 比对的是容器的 ctime, 异步地遍历每一个元素检查 mtime 决定是否删除
- 提供 lock 指令
- 以事务启动时的时间戳作为所有参与者的 mtime
- 读操作忽略未决资源(最终原子性, read committed, 不是 monotonic atomic view)
- 悲观事务(读写, Transaction), 交互式 2PC, 有 commit point
- 提供 binlog 和 redo log 订阅接口
- CAS 操作, CAS+原子性, 可以取代锁, 提供事务一致性保证
- MVCC 底层数据保留多个版本, 但带来的问题非常多, 例如容器计数也要保留多个版本
相关文章:
binlog, redo log, 强一致性, 最终一致性
最终一致性的系统, 节点之间只能同步幂等指令.
hclear 清空容器指令本身不是幂等的, 但是, 如果 hclear 带着 mtime, 逐个删除元素, 那么便是幂等的. hclear/zclear 删除操作不保证是(不实现成)原子操作. hclear 指令将改变容器的 ctime 属性, 如果 hclear 执行得比 hset 早, 那么 hset 的时候可以比对 ctime.
incr 指令不是幂等的, 但是, 又不能把它转换成 set 指令再添加到 binlog(raft log), 所以, 只能在 apply 的时候, 转成 set 指令添加到 redo log 中, 可以使用和 binlog 相同的 index, 让 binlog 和 redo log 一一对应.
可以现场根据 binlog 即时生成 redo log, 例如, binlog:
set a 1; del a;
, 对应的 redo log 是:noop; del a;
.binlog 用于强一致性, redo log 用于最终一致性(更好的选择). binlog 必须严格按顺序 exactly once 重放, 而 redo log 不限顺序, 不限次数, 重放时比对旧数据的 mtime. redo log 不需要侵入系统, 使用外部工具进行重放, 系统本身不需要记录进度.
欧洲从亚洲同步过来 redo log 后, 使用正常的 client 请求欧洲的数据库节点, 执行 redo log. 欧洲节点针对这些 redo log, 不再产生 redo log.