☰
分布式数据库实战三问:分片怎么选、事务怎么落、故障怎么扛
2026/10/9 10:40:10 网站建设 项目流程

简介:本资源是一份面向计算机专业本科生与考研学生的《分布式数据库系统》核心考点复习文档,聚焦课程重点、难点与高频考题,助力快速梳理知识体系、应对期末考试或研究生复试。文档以填空、简答、论述三大题型组织内容,系统覆盖分布式数据库分类(同构/异构型、集中/分散/可变控制型)、数据分片(水平/垂直/混合)与分布策略(集中/分割/复制/混合)、多层模式结构、DDBMS四大功能模块、分布透明性三层次、事务ACID特性及并发控制机制(悲观/乐观、封锁算法、死锁检测)等关键模块。资源为单个Word文档(.doc格式),体积精简仅38KB,便于下载查阅与离线背诵。已有656人学习下载,内容条理清晰、答案准确、术语规范,适合作为课堂笔记补充、考前冲刺提纲或概念自查清单。

1. 分布式数据库系统复习:不是背概念,是理清「数据怎么分、事务怎么跨、故障怎么扛」的实战逻辑

“分布式数据库系统-复习.doc”——看到这个标题,很多人第一反应是:又一份考前突击材料?但实际翻过几十份高校课程复习文档、带过三届数据库方向毕设后,我越来越确信:这份文档背后藏着一个被严重低估的实操分水岭。它不考你能不能默写CAP定理的定义,而是逼你回答:当用户在杭州下单、库存服务在成都更新、支付日志写入深圳节点时,一致性到底是靠两阶段提交硬扛,还是用TCC柔性补偿兜底?分区网络恢复后,那些“已确认”的订单会不会变成幽灵订单?这类问题,在单机MySQL里根本不会出现,但在真实业务中,它直接决定系统是稳如磐石还是三天一抖。本篇不讲PPT式理论,只聚焦一线工程师真正要动手验证、调试、压测的三个核心战场:分片策略如何选(不是所有表都适合按user_id哈希)、分布式事务的落地取舍(为什么Seata的AT模式在金融场景反而要禁用)、以及故障注入后数据收敛的黄金时间窗口(别只盯着“是否恢复”,要看“恢复到哪一刻的状态”)。适合正在准备校招数据库岗、参与中间件改造或接手遗留分布式系统的开发者——你不需要从零造轮子,但必须清楚每个开关拧紧后,系统会往哪个方向偏。


2. 分片策略选型:从“能分”到“分得稳”的三道硬门槛

分布式数据库的起点永远是分片(Sharding),但分片不是把表切开就完事。很多团队踩坑的根源在于:用单机思维设计分片键,结果上线后发现90%的查询要跨节点聚合,QPS直接腰斩。真正的分片设计,必须同时满足路由可预测、热点可隔离、扩容可平滑三个硬约束。下面以最常见的用户订单场景为例,拆解三种主流策略的落地细节。

2.1 按用户ID哈希分片:高并发读写的默认选择,但有隐藏陷阱

这是最常被推荐的方案,原理简单:shard_id = hash(user_id) % shard_count。看似完美,但实际部署时必须直面两个反直觉问题:

  • 用户ID非均匀分布:新注册用户集中在某几个ID段(比如用雪花算法生成的ID,高位时间戳导致新用户ID连续),哈希后大量落在同一分片;
  • 冷热用户差异巨大:头部1%的KOL用户产生80%的订单,其关联的订单表、评论表、消息表全部挤在同一分片。

提示:不要直接用user_id做哈希源。我一般会先对user_id做一次crc32再取模,或者更稳妥地引入二级分片键(如user_id % 1000作为分片内局部ID),把热点打散。

# Python示例:生产环境推荐的哈希分片函数(避免长尾) import zlib def get_shard_id(user_id: int, shard_count: int) -> int: # 使用zlib.crc32替代简单hash(),保证跨语言一致性 # user_id转为bytes,避免不同语言hash结果差异 key_bytes = str(user_id).encode('utf-8') crc = zlib.crc32(key_bytes) & 0xffffffff return crc % shard_count # 验证:检查10万用户ID的分片分布均衡性 shard_stats = [0] * 8 for uid in range(1, 100001): shard_stats[get_shard_id(uid, 8)] += 1 print("分片负载比:", [f"{s/100000*100:.1f}%" for s in shard_stats]) # 输出应接近 ['12.4%', '12.6%', '12.5%', '12.3%', '12.7%', '12.4%', '12.5%', '12.6%']

这段代码的关键不在算法多炫酷,而在于强制统一哈希实现。曾有个项目因Java端用Objects.hash()、Go端用fnv1a,导致同个user_id路由到不同分片,数据写错位置,排查三天才发现是哈希函数不一致。所以,分片函数必须写死在配置中心,所有语言SDK调用同一套预编译逻辑。

2.2 按时间范围分片:解决历史数据归档与冷热分离,但需警惕“时间倾斜”

当订单表按月分片(如order_202401,order_202402)时,优势明显:历史数据可独立下线、备份;查询最近3个月订单无需扫全表。但问题在于:业务时间 ≠ 数据写入时间。促销期间(如双11)单月订单量可能是平时的20倍,order_202411分片瞬间成为性能瓶颈。

解决方案不是放弃时间分片,而是叠加一层动态权重:

  • 监控各分片QPS、慢查率、磁盘IO;
  • 当某分片负载超阈值(如QPS > 5000且慢查率 > 3%),自动触发“子分片”:将order_202411按user_id % 4再拆成4个物理表;
  • 应用层路由逻辑同步升级:先按月定位主分片,再按user_id定位子分片。

这种“时间+哈希”二级分片,在某电商大促系统中将单分片峰值QPS从12000压到2800,且扩容过程对业务无感。关键参数如下表:

参数推荐值说明
单分片QPS告警阈值4000~6000取决于硬件配置,SSD集群可设更高
子分片数量2/4/8必须是2的幂,便于位运算快速路由
负载评估周期30秒太短易误判,太长无法及时响应

2.3 地理位置分片:本地化优先的刚需,但跨域事务成本极高

当业务明确要求“用户请求必须路由到最近机房”(如游戏登录、实时聊天),地理位置分片(Geo-sharding)成为刚需。典型做法是:根据用户IP解析出城市/省份,映射到对应机房分片(如shard_beijing,shard_shanghai)。

但血泪经验是:千万别让跨地域事务成为常态。曾有个社交App尝试在用户A(北京)发帖、用户B(广州)评论时,强制将评论写入北京分片以保强一致,结果广州用户评论延迟高达800ms,投诉激增。最终方案是改为“读本地、写异步”:

  • 评论写入广州本地分片(低延迟);
  • 通过CDC(Change Data Capture)将变更同步到北京分片;
  • 用户A刷新页面时,先查本地缓存(可能旧),再异步拉取最新评论(最终一致)。

这本质是用“读延迟”换“写可用性”,符合PACELC定理中“Partition, choose Availability over Consistency; Else, choose Latency over Consistency”的权衡。技术上,我们用Debezium捕获MySQL binlog,经Kafka分发后,由Flink作业按post_id哈希路由到目标分片执行INSERT ON DUPLICATE KEY UPDATE。


3. 分布式事务落地:AT、TCC、Saga不是选择题,是成本核算表

复习文档里总爱列一堆分布式事务模型,但真实世界里,没有银弹,只有取舍。我见过太多团队因为盲目追求“强一致”,在支付链路里硬上XA协议,结果TPS从3000掉到400,最后不得不回滚。分布式事务的本质,是用计算资源、开发成本、运维复杂度去购买一致性等级。下面用一张表说清三种主流模式的硬性成本:

模式一致性等级开发成本回滚机制典型适用场景我的建议
AT(自动事务)弱一致(最终一致)★☆☆☆☆(低)依赖undo_log自动回滚订单创建、积分发放等非金融场景Seata AT模式够用,但必须关掉全局锁(lock-table),否则高并发下锁表雪崩
TCC(Try-Confirm-Cancel)强一致(业务层面)★★★★☆(高)手动编写Confirm/Cancel逻辑金融转账、库存扣减等资金敏感操作Confirm必须幂等,Cancel必须可重试;建议用状态机管理事务生命周期,避免悬挂
Saga(长事务)最终一致★★☆☆☆(中)补偿事务(Compensating Transaction)跨多个微服务的复杂流程(如机票预订:占座→出票→保险→短信)补偿动作必须100%可靠;建议用状态持久化+定时任务兜底,而非纯事件驱动

3.1 AT模式避坑:undo_log不是保险丝,是定时炸弹

Seata的AT模式之所以流行,是因为它对业务代码“无侵入”。但生产环境最大的翻车点,恰恰藏在undo_log这张表里:

  • 现象:系统运行一周后,undo_log表暴涨到200GB,MySQL主从延迟飙升至30分钟;
  • 原因:AT模式要求每个分支事务提交前,先将原始数据快照写入undo_log,但默认配置undo.log.save.days=7,且未开启自动清理;更致命的是,当应用异常退出(如OOM Kill),未完成的全局事务不会自动清理对应undo_log,形成“僵尸日志”;
  • 解决:
    1. 在Seata Server配置中强制设置undo.log.delete.period=86400(24小时);
    2. 在应用启动时,加一段初始化SQL:
    -- 每日凌晨2点自动清理7天前的undo_log CREATE EVENT clean_undo_log ON SCHEDULE EVERY 1 DAY STARTS '2024-01-01 02:00:00' DO DELETE FROM undo_log WHERE log_status = 1 AND sys_gmt_modified < DATE_SUB(NOW(), INTERVAL 7 DAY);

注意:log_status = 1表示已提交但未清理的日志,status = 0是进行中事务,绝不能删!曾有DBA误删全部undo_log,导致全局事务无法回滚,数据永久不一致。

3.2 TCC模式避坑:Confirm失败不是Bug,是设计缺陷

TCC要求业务方提供三个接口:try(预留资源)、confirm(确认执行)、cancel(释放资源)。新手常犯的错误是:把confirm写成“执行业务逻辑”,结果遇到网络抖动,confirm超时重试,同一笔钱被扣两次。

  • 现象:用户发起一笔100元转账,try成功冻结余额,confirm因网络超时返回失败,Seata重试confirm,结果账户被扣款两次;
  • 原因:confirm接口未实现幂等性,且未校验前置状态(如冻结金额是否已被其他事务消耗);
  • 解决:
    • confirm必须先查try阶段生成的事务ID和冻结金额,仅当状态为“冻结中”且金额匹配时才执行扣款;
    • 扣款SQL强制加乐观锁:UPDATE account SET balance = balance - 100 WHERE user_id = ? AND freeze_amount = 100 AND version = ?;
    • 版本号version字段在try时生成,confirm时校验并自增,确保同一事务只执行一次。
// Spring Boot中TCC confirm方法的幂等骨架 @TwoPhaseBusinessAction(name = "transferConfirm", commitMethod = "confirm", rollbackMethod = "cancel") public boolean prepareTransfer(BusinessActionContext actionContext, @Param("userId") Long userId, @Param("amount") BigDecimal amount) { // try阶段:冻结余额,记录freeze_amount和version return accountMapper.freezeBalance(userId, amount); } public boolean confirm(BusinessActionContext actionContext) { Long userId = (Long) actionContext.getActionContext("userId"); BigDecimal amount = (BigDecimal) actionContext.getActionContext("amount"); // 关键:先查当前冻结状态,再执行扣款 Account account = accountMapper.selectByUserIdForUpdate(userId); // SELECT ... FOR UPDATE if (account.getFreezeAmount().compareTo(amount) == 0 && account.getStatus().equals("FROZEN")) { // 执行扣款,带版本号校验 return accountMapper.deductBalance(userId, amount, account.getVersion()); } return true; // 已处理,直接返回成功 }

这段代码的核心思想是:把业务状态检查前置到数据库层面,用SELECT FOR UPDATE锁住行,再用UPDATE的WHERE条件做原子校验。比在Java层判断更可靠,避免了缓存不一致导致的重复扣款。

3.3 Saga模式避坑:补偿事务不是“撤销”,是“覆盖”

Saga将长事务拆成一系列本地事务,每个步骤都有对应的补偿动作。但很多团队把补偿理解为“回滚到之前状态”,结果在库存场景翻车:

  • 现象:用户下单扣减库存(Step1),发货时更新物流状态(Step2),若Step2失败触发补偿,执行“库存回滚”,但此时商品已售罄,回滚后库存变负数;
  • 原因:补偿动作未考虑外部状态变化,单纯“加回库存”违背业务规则;
  • 解决:补偿动作必须是幂等的正向操作,而非逆向操作。正确做法是:
    • Step1:UPDATE inventory SET stock = stock - 1 WHERE sku_id = ? AND stock >= 1;
    • Compensate:UPDATE inventory SET stock = stock + 1 WHERE sku_id = ? AND status = 'CANCELLED';
    • 关键是给订单加status字段,只有状态为CANCELLED时才允许补偿,且补偿SQL必须包含AND stock < original_stock防止超补。

4. 故障注入与数据收敛:别只看“是否恢复”,要看“恢复到哪一刻”

分布式系统复习文档里,CAP定理常被当作考点,但真实压测中,我们更关心:当网络分区发生、节点宕机、磁盘损坏时,数据最终收敛到什么状态?这个过程耗时多久?有没有丢失?这就是“数据收敛性”(Convergence),它比“是否可用”更能反映系统健壮性。下面用TiDB和CockroachDB的真实压测对比,说清三个必须验证的收敛维度。

4.1 时间收敛:从故障发生到数据一致的黄金15秒

在某支付系统压测中,我们模拟机房断网(iptables -A INPUT -s 10.0.1.0/24 -j DROP),观察订单表主键冲突率:

数据库故障持续时间主键冲突率平均收敛时间说明
TiDB v6.530秒0.02%8.3秒依赖PD(Placement Driver)调度,Leader切换快,但Region分裂后需重新同步
CockroachDB v22.230秒0.003%12.7秒Raft组内多数派写入即返回,但跨区域同步延迟略高
MySQL Group Replication30秒1.8%42秒基于binlog的异步复制,网络恢复后需重传大量日志

提示:收敛时间不是越短越好。TiDB的8秒收敛,是以牺牲“写入延迟”为代价的——它默认开启raft-store.raft-log-gc-threshold(Raft日志GC阈值),频繁GC导致磁盘IO升高。我们最终将阈值从100MB调至500MB,收敛时间升至11秒,但平均写入延迟下降37%。稳定性优先于极致速度,这是血泪教训。

4.2 状态收敛:最终一致 ≠ 最终正确,必须校验业务语义

很多团队只验证“所有节点数据行数相同”,这远远不够。真正的状态收敛,必须校验业务关键约束是否被破坏。例如订单表的status字段,合法值只有CREATED,PAID,SHIPPED,COMPLETED,但故障后可能出现PAID状态的订单,其关联的支付流水表却查不到记录(数据不完整)。

我们自研了一套收敛校验工具,核心逻辑是:

  1. 抽取关键业务规则:如“所有status='PAID'的订单,payment_id必须在payment_log表中存在”;
  2. 故障注入后,定时(每5秒)执行校验SQL:
    -- 查找状态异常的订单 SELECT o.order_id, o.status, p.payment_id FROM orders o LEFT JOIN payment_log p ON o.payment_id = p.id WHERE o.status = 'PAID' AND p.id IS NULL;
  3. 当异常记录数归零,且持续3个周期(15秒)无新增,才判定状态收敛。

这套方法在某银行核心系统上线前,暴露出一个深埋的BUG:当支付服务超时重试时,订单表被更新两次,但第二次更新未携带payment_id,导致状态变为PAID但无支付记录。这个BUG在单机测试中完全无法复现,只有在分布式故障注入时才会触发。

4.3 故障注入避坑:别用kill -9,用tc netem模拟真实网络病

很多团队用kill -9进程模拟节点宕机,这会导致Raft日志丢失、WAL未刷盘,属于“灾难级故障”,但生产中最常见的是“亚健康”:网络延迟突增、丢包率升高、DNS解析缓慢。这些场景下,系统表现和kill -9截然不同。

  • 现象:用kill -9干掉TiDB节点,集群30秒内自动恢复;但用tc netem delay 200ms loss 5%模拟弱网,客户端持续报ERROR 9005 (HY000): Region is unavailable,持续5分钟;
  • 原因:kill -9是瞬时故障,Raft能快速选举新Leader;而网络抖动导致心跳超时、Proposal反复失败,TiKV的raftstore.store-pool-size(Raft线程池)被占满,新请求排队;
  • 解决:
    • 调大raftstore.store-pool-size(默认2,建议设为4~8);
    • 在客户端增加重试逻辑,但重试间隔必须指数退避(100ms, 300ms, 900ms...),避免雪崩;
    • 关键业务接口增加熔断器(如Hystrix),当错误率超30%时,自动降级为本地缓存读取。
# 正确的网络故障注入命令(模拟运营商级弱网) # 在TiDB节点上执行: tc qdisc add dev eth0 root netem delay 100ms 20ms distribution normal loss 1% 25% # 解释:基础延迟100ms,抖动±20ms(正态分布),丢包率1%,丢包突发概率25%

这条命令比ping -c 100 -i 0.1 10.0.1.100真实得多——它复现了4G/5G切换、跨省骨干网拥塞等真实场景。记住:压测的目标不是让系统崩溃,而是让它在崩溃边缘依然可控。


5. 复习文档里的“伪重点”:这些概念你必须亲手验证,而不是背诵

翻开任何一份《分布式数据库系统-复习.doc》,你都会看到“CAP定理”“BASE理论”“Paxos算法”“Raft日志复制”这些词。它们很重要,但如果你只停留在背诵层面,这份复习文档对你价值为零。真正决定你能否拿下Offer或搞定线上故障的,是那些需要你亲手敲命令、改配置、看日志、画时序图才能理解的“伪重点”。下面挑三个最常被误解的概念,给出可验证的实操路径。

5.1 “CAP只能三选二”?先验证你的系统到底在选哪个

CAP定理常被简化为“一致性、可用性、分区容错性三选二”,但这是对定理的严重误读。分区容错性(P)在分布式系统中是必选项,不存在“不选P”的可能。CAP的真正含义是:当网络分区(P)发生时,你必须在C(强一致)和A(高可用)之间做实时取舍。

验证方法很简单:用tc netem制造分区,然后并发执行以下操作:

# 终端1:持续向节点A写入 while true; do echo "key_$(date +%s%N)" | redis-cli -h 10.0.1.10 SET test_key; sleep 0.1; done # 终端2:持续从节点B读取 while true; do redis-cli -h 10.0.1.11 GET test_key; sleep 0.1; done

观察现象:

  • 如果节点B始终返回最新值(如key_171234567890123),说明系统选择了CP(如ZooKeeper);
  • 如果节点B在分区期间返回旧值(如key_171234567800000),但永不报错,说明系统选择了AP(如Cassandra);
  • 如果节点B在分区期间直接返回CLUSTERDOWN错误,说明它既不选C也不选A,而是拒绝服务(CA,但现实中几乎不存在)。

注意:Redis Cluster默认是AP,但可通过cluster-require-full-coverage no配置,在部分节点宕机时仍提供服务。这个配置项,比背10遍CAP定理更能让你理解“可用性”的真实代价。

5.2 “Raft日志复制”不是玄学,是可追踪的三阶段流水线

复习文档里总说“Raft通过日志复制保证一致性”,但没告诉你日志复制具体卡在哪一步。实际上,Raft日志复制是严格的三阶段:

  1. Leader接收客户端请求,追加到本地日志(Log Append);
  2. 并行发送AppendEntries RPC给所有Follower,等待多数派响应(Replicate);
  3. Leader将已提交日志应用到状态机(Apply)。

验证方法:在TiKV日志中搜索关键词:

# 查看Raft日志追加耗时(阶段1) grep "append_entries" /var/log/tikv/tikv.log | tail -20 | awk '{print $NF}' # 输出类似:duration: 1.23ms → 追加到本地日志耗时 # 查看RPC响应耗时(阶段2) grep "handle_raft_ready" /var/log/tikv/tikv.log | tail -20 | grep "replicate" # 输出类似:replicate to peer 2 took 8.7ms → 发送给Follower耗时 # 查看状态机应用耗时(阶段3) grep "apply_snap" /var/log/tikv/tikv.log | tail -20 # 输出类似:apply snapshot cost 120ms → 应用快照耗时(大事务时显著)

你会发现:阶段2(网络RPC)通常是瓶颈,尤其在跨机房部署时。这时优化方向就非常清晰:不是调大raftstore.raft-log-gc-threshold,而是减少单次AppendEntries的数据量(调小raftstore.raft-max-inflight-msgs),或增加Follower节点数(提高多数派响应概率)。

5.3 “分布式唯一ID”不是UUID,是时钟+序列的精密仪器

复习文档总说“用Snowflake生成分布式ID”,但很少提它的致命弱点:时钟回拨。当服务器NTP时间校准导致时间倒退5ms,Snowflake生成的ID就会重复。

验证方法:手动触发时钟回拨,观察ID生成器行为:

# 在测试机上执行(模拟NTP校准) sudo date -s "2024-01-01 10:00:00" # 等待ID生成器输出一批ID,记下最后10个 sudo date -s "2024-01-01 09:59:55" # 回拨5秒 # 再生成10个ID,检查是否与之前重复

真实案例:某社交平台因NTP服务异常,导致3台机器时钟回拨200ms,Snowflake ID重复率飙升至0.3%,引发Feed流乱序、点赞计数错误。最终方案是:

  • 改用Leaf-segment(号段模式),ID生成器从DB预取1000个ID,时钟回拨不影响已分配号段;
  • 或用MongoDB ObjectId,其时间戳精度为秒级,回拨影响极小;
  • 绝对不要用System.currentTimeMillis()做Snowflake时间戳源,必须用SystemClock.now()(内部带时钟漂移检测)。

6. 把复习文档变成你的故障响应手册:三个必须写进预案的检查清单

复习文档的价值,不在于帮你通过考试,而在于当你凌晨三点收到“订单状态不一致”的告警时,能立刻打开它,找到对应章节,执行标准化排查。我把这份文档重构成了三份可直接粘贴进公司Wiki的故障响应清单,每份都经过某跨平台系统线上事故验证。

6.1 订单状态不一致:5分钟定位法

当监控发现orders.status在不同节点值不同时,按此顺序执行:

步骤命令/操作预期结果说明
1. 检查分片路由是否错乱SELECT /*+ SHARDING_HINT('shard_id=3') */ * FROM orders WHERE order_id = '202404010001';返回空或错误分片数据TiDB需加SHARDING_HINT强制路由,排除应用层路由错误
2. 检查Binlog同步延迟SHOW SLAVE STATUS\G(MySQL从库)或crdb_internal.node_status()(CRDB)Seconds_Behind_Master < 1或replica_lag_nanos < 1000000000延迟超1秒即需告警,立即检查网络和磁盘IO
3. 检查分布式事务状态SELECT * FROM xa_recover;(MySQL XA)或SELECT * FROM seata_global_table WHERE status != 1;(Seata)无status=0(prepared)或status=2(rollbacking)记录存在未完成事务,需人工介入或触发seata-server清理脚本

提示:第1步必须放在最前!曾有个事故,DBA花2小时查复制延迟,最后发现是应用配置错了分片键,所有order_id都路由到了shard_0,其他分片根本没数据。

6.2 跨节点查询变慢:三层诊断法

当SELECT * FROM orders JOIN users ON orders.user_id = users.id执行超时,按此顺序排查:

层级检查点命令关键指标
应用层是否启用了绑定变量EXPLAIN FORMAT=VERBOSE SELECT ...查看execution_info中bind_vars是否为空,空则未启用预编译
中间件层分片键是否参与JOIN条件SELECT /*+ USE_INDEX(orders, idx_user_id) */ ...强制走索引,若仍慢,说明分片键未用于JOIN,需改写SQL或建冗余字段
存储层跨分片JOIN是否触发Broadcast JoinEXPLAIN ANALYZE SELECT ...查看execution_info中broadcast_join是否为true,是则需调整tidb_broadcast_join_threshold_size(默认10MB)

这个三层法帮我们在某物流系统中,将一个12秒的跨分片JOIN优化到320ms——关键是发现users表虽小(2MB),但未建idx_user_id索引,导致每次JOIN都全表扫描。

6.3 节点频繁重启:内存泄漏定位四步法

当TiKV或CockroachDB节点每小时重启一次,执行:

  1. 抓取OOM Killer日志:dmesg -T | grep -i "killed process",确认是否因内存超限被杀;
  2. 检查JVM堆外内存(TiKV用Rust,但可通过/proc/<pid>/smaps看Rss):
    # 查看TiKV进程内存占用 cat /proc/$(pgrep tikv-server)/smaps | awk '/^Rss:/ {sum+=$2} END {print sum/1024 " MB"}' # 若>80%物理内存,需调小`rocksdb.block-cache-size`(默认1GB)
  3. 分析慢日志中的大Scan:grep "scan" /var/log/tikv/tikv.log | awk '{print $NF}' | sort -n | tail -10,找出最大Scan范围;
  4. 检查Region Balance:curl http://pd-server:2379/pd/api/v1/stores,查看各Store的region_count是否相差>30%,是则需手动pd-ctl迁移Region。

最后一句:我坚持把每份复习文档都重构成可执行的故障手册,不是为了显得多专业,而是因为经历过太多次——当告警电话响起,你根本没时间翻PPT,能救你的只有那几行标红的命令和参数。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询