TiKV 是分布式事务型 KV 存储,设计目标是替代 MySQL 底层存储而非充当缓存;强行当 Redis 使用会导致数据错乱、锁残留、OOM 和写偏;正确分层应为 Redis(缓存)→ TiKV(主存),通过事件驱动解耦更新,保障强一致与可观测性。

TiKV 不是缓存,不能当 Redis 用;强行当缓存集成会导致数据错乱、锁残留、OOM 和不可观测的写偏。 它是分布式事务型 KV 存储,设计目标是替代 MySQL 的底层存储层,不是加速读的缓存层。你真正需要的,是「用 TiKV 保证跨服务写操作的强一致」,而不是「把它塞进 cache 层」。
为什么把 TiKV 当缓存用会出事
常见错误是看到 Get/Put 就以为能替代 redis.Client.Get —— 但 TiKV 的 RawKV 没有 TTL、不自动驱逐、不支持批量过期;而 TransactionalKV 又要求显式 Begin/Commit,每次读都开事务,QPS 上不去,还容易触发 tikverr.ErrWriteConflict。
- 用
RawKV直接存业务热点数据:key 写入后永远不删,磁盘涨满,Scan一次就 OOM - 用
TransactionalKV做“缓存读”:每个Get都走Begin→Get→Commit,延迟翻 3 倍,PD 压力飙升 - 混用
client.Get和txn.Get:绕过事务上下文,读到未提交脏数据或跳过锁检测 - 没设
WithTxnTTL就发长事务:锁卡住 10 分钟,后续所有写同 key 的请求全被 block
正确姿势:TiKV 作为「带事务语义的主存」,Redis 才是缓存
典型分层是:Redis(缓存)→ TiKV(主存)→ 应用逻辑。TiKV 存真实状态(如订单详情、账户余额),Redis 存派生视图(如用户首页聚合数据)。二者更新必须解耦,靠事件驱动同步,而非双写。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 写路径:应用先在 TiKV 中执行
txn.Put("order:123", data)→ 成功后发OrderCreatedEvent到 Kafka → Redis 消费者收到后SETEX user:456:summary ... 300 - 读路径:优先查 Redis;未命中则查 TiKV,再回填 Redis(注意加
SETNX防穿透) - 一致性保障:Redis 更新失败?靠 Kafka 重试 + 消费端幂等(如用
order_id做SETkey);TiKV 更新失败?走 Saga 补偿,不牵连缓存层 - 避免踩坑:不要给 TiKV key 设业务过期时间;不要在 TiKV 里存 session、token 这类短期数据;不要用
Scan做分页,改用Iter+Seek
Go 客户端初始化和事务控制的关键参数
用 github.com/tikv/client-go/v2,别用 v1 或 raw HTTP 调用。事务生命周期管理错一个字,就会锁残留。
立即学习“go语言免费学习笔记(深入)”;
- 初始化 client 必须传 PD 地址:
client, err := tikv.NewClient([]string{"http://pd1:2379", "http://pd2:2379"}) - 事务必须用
client.Begin()创建,不能 new;超时要显式设:client.WithTxnTimeOut(10 * time.Second) - 写完必须调
txn.Commit().Wait(),只调txn.Commit()是异步提交,error 会被吞掉 - 读多写少场景,用
client.NewTxnWithStartTS()构造只读事务,避免无谓锁竞争 - 捕获冲突后重试,不要无脑
for i := 0; i :用 <code>backoff.Retry加 jitter,退避 10–50ms
最易被忽略的一点:TiKV 的「强一致」只对它自己负责。如果你的应用层没做幂等、没控超时、没处理消息重复,那 TiKV 再强也兜不住上层逻辑错误。它不是银弹,是手术刀——用对了精准切开问题,用错了直接伤及主干。

















