news 2026/9/14 19:02:31

TiKV 事务与存储层(src/storage)维护指南:架构、调度、MVCC、动态配置与运维信号

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TiKV 事务与存储层(src/storage)维护指南:架构、调度、MVCC、动态配置与运维信号

TiKV 事务与存储层(src/storage)维护指南:架构、调度、MVCC、动态配置与运维信号

【免费下载链接】tikvDistributed transactional key-value database, originally created to complement TiDB项目地址: https://gitcode.com/GitHub_Trending/ti/tikv

本文以 TiKV 仓库的 storage 维护指南 为核心骨架,系统讲解src/storage这一事务与存储层的职责边界、分层架构、请求执行链路、生命周期、数据模型契约、关键不变量、可观测信号以及变更管理方法。读完本文,你将掌握 TiKV 存储层的代码地图与必读顺序、StorageAPI 与TxnScheduler的协作原理、storage 相关配置参数(含动态下发机制)的完整语义,以及排查回调丢失、latch 卡死、MVCC 跨 CF 漂移、动态配置未生效等典型故障的入手点。

目的与范围:src/storage 是什么

src/storage是 TiKV 的事务与存储层,也是仓库中最重要的维护面之一。从 模块文档 可以看到,它承担了"把高层事务命令降低为对持久存储的底层(raw key-value)交互"这一核心职责。它拥有并维护:

  • StorageAPI:面向 RPC 层的顶层事务/存储入口;
  • 事务命令调度:命令准入、latch 串行化、任务追踪与 worker 交接;
  • MVCC(多版本并发控制):读取、扫描、冲突检测与写入侧变更处理;
  • raw KV 操作:raw 键值 API 及其可选的 MVCC 封装;
  • latches 与锁等待:调度器侧冲突串行化、存储侧锁等待契约;
  • 流控(flow control):写压力管理;
  • 存储相关动态配置:在线配置的解析、校验与运行时副作用下发。

从架构归属看,src/storage向下通过Engine抽象(见 engine_traits)与持久化引擎交互,向上被 src/server/service/kv.rs 这一主要调用方承接,写路径经 raftkv 桥接 进入 raftstore。

架构视图

分层视图

自顶向下,src/storage分为五层:

  1. 顶层StorageAPI(mod.rs);
  2. 命令调度器(txn/scheduler.rs);
  3. MVCC 读写逻辑(mvcc/);
  4. raw KV 逻辑(raw/);
  5. 锁等待与流控(lock_manager/ 与 txn/flow_controller/)。

请求执行视图

一次请求的典型执行链路为:

  • RPC 边界调用Storage(主要调用方是 src/server/service/kv.rs);
  • 调度器(scheduler)准入命令:获取 latch、登记任务、按优先级与资源组策略排队;
  • MVCC/raw 执行发生在快照(snapshot)与引擎(engine)之上;
  • 回调(callback)最终完成并返回 RPC 或更高层。

从 txn/scheduler.rs 的模块注释可以印证:每个 store 有一个TxnScheduler,运行在单线程事件循环中,命令执行被委托给 worker 线程池;scheduler 只保证命令级别对重叠行的串行访问,事务级语义由客户端库实现的事务协议保证。

进程生命周期与启动时序

Storage在服务器启动时构造,且必须满足依赖顺序:引擎(engines)、读池(read pools)、并发管理器(concurrency manager)、配额限流器(quota limiter)以及相关运行时辅助组件先就绪,之后才构建Storage。启动后,运行时配置管理器(runtime config managers)通过 config_manager.rs 更新存储侧行为。关闭阶段必须保持回调与 worker 的所有权安全,直到所有强引用消失。

代码层面有几个具体的运行时锚点:

  • storage 构造Storage::from_engine(mod.rs)接收 engine、config、read_pool 等构建整个协调层;Storage 结构体 持有engineschedTxnScheduler)、read_poolconcurrency_managerquota_limiterresource_managerresource_tag_factory等成员,并通过refs: Arc<AtomicUsize>做引用计数——线程池与 worker 只有在引用归零后才停止(见Clone/Drop实现,mod.rs)。
  • scheduler 执行:位于 txn/scheduler.rs。
  • 动态配置副作用:位于 config_manager.rs 的dispatch方法。

数据模型与元数据契约

src/storage依赖若干严格的数据与元数据契约:

  • MVCC 数据模型:横跨 default、lock、write 三个列族(CF)。lock CF 记录未提交的锁,write CF 记录已提交写入的版本指针与提交时间戳,default CF 保存实际数据或短值(SHORT_VALUE_MAX_LEN,见 mvcc/mod.rs)。
  • 调度器任务元数据与 latch 所有权TaskContext持有lockcb(回调)、prProcessResult)以及一个owned: AtomicBool,用于保证回调与结果只能被一个线程安全取走(scheduler.rs)。
  • 锁等待元数据与 tokenLockDigestDiagnosticContextKeyLockWaitInfoWaitTimeout定义在 lock_manager/mod.rs,超时在 protobuf 中编码为 i64:0 表示默认超时,负值表示不等待。
  • raw KV 编码与 API 版本:raw 层按 API 版本选择存储实现,RawStoreV1/V1Ttl/V2三个变体的枚举(raw/store.rs),分别对应ApiV1ApiV1TtlApiV2KvFormat
  • 资源控制元数据:调度过程中消费资源组(resource group)与任务元数据(TaskMetadataResourceTagFactory)。

高风险契约

维护时最需要小心的契约包括:

  • MVCC 跨 default/lock/write CF 的关系一致性;
  • ProcessResult与回调完成语义:ProcessResult定义在 txn/mod.rs,包含ResMultiResPrewriteResultMvccKeyLocksTxnStatusNextCommandFailedPessimisticLockResSecondaryLocksStatusRawCompareAndSwapRes等变体,是调度执行结果回传的唯一通道;
  • TxnStatusCache与 max-ts 相关假设(容量默认40_000 * 128,见 config.rs);
  • raw KV 的 API 版本与 TTL 规则(定义在 config.rs 的api_version/enable_ttl校验中)。

代码地图:Start Here 与必读顺序

文档给出的"从这开始"清单(均以仓库根目录为起点):

  • src/storage/mod.rs
  • src/storage/txn/scheduler.rs
  • src/storage/txn/mod.rs
  • src/storage/txn/commands/mod.rs
  • src/storage/mvcc/mod.rs
  • src/storage/txn/store.rs
  • src/storage/raw/store.rs
  • src/storage/config.rs
  • src/storage/config_manager.rs

推荐的必读文件顺序为:

  1. src/storage/mod.rs
  2. src/storage/config.rs
  3. src/storage/txn/mod.rs
  4. src/storage/txn/scheduler.rs
  5. src/storage/txn/commands/mod.rs
  6. src/storage/mvcc/mod.rs
  7. src/storage/mvcc/reader/reader.rs
  8. src/storage/txn/store.rs
  9. src/storage/lock_manager/mod.rs
  10. src/storage/config_manager.rs

内部结构详解

顶层 API:Storage

mod.rs 定义了Storage与大量公开操作。从Storage的成员可以看到它的协调者角色:它把 scheduler(TxnScheduler)、read pool、concurrency manager、quota limiter、resource control 组装在一起,对外提供事务与 raw API;max_key_size等配置字段直接参与命令参数校验。模块内还细分了txn(事务命令 → MVCC 抽象)、mvcc(MVCC 实现)、kv(持久化存储抽象)三个层次(mod.rs 模块注释)。

调度器与命令执行

  • txn/scheduler.rs 拥有命令准入、latches、任务追踪与 worker 交接。任务槽位数为1 << 12(4096,见 scheduler.rs)。
  • txn/commands/ 定义具体事务命令(prewrite、commit、acquire_pessimistic_lock、gc、cleanup、flashback 等,见 txn/mod.rs 的 actions 导出)。
  • txn/task.rs、txn/sched_pool.rs 与 txn/tracker.rs 支撑执行。
  • 关于调度池的细节(sched_pool.rs):
    • 仅为自定义资源组(customized resource groups)选择优先级队列;仅后台控制仍使用 vanilla 队列,并在池内对匹配的长运行任务进行节流。
    • 调度池会从非只读 scheduler 命令中累计 MVCC 读流——包括前台写命令执行过程中执行的读——并在 ticker 与 worker 关闭时的 flush 中,把 region 级ReadStats上报给 raftstore reporter(local_read_stats/report_read_stats)。需要留意:此路径上关键范围(key ranges)与 bucket 增量不可用。

MVCC

  • mvcc/mod.rs 定义 MVCC 错误与公共面。错误类型覆盖KeyIsLockedCommittedWriteConflictDeadlockTxnLockNotFoundPessimisticLockRolledBack等(mvcc/mod.rs),这些是事务冲突与恢复逻辑的判定基础。
  • mvcc/reader/ 拥有读侧:scanner、point getter、冲突检测。
  • mvcc/txn.rs 拥有写侧 MVCC 变更处理(MvccTxnGcInfoMAX_TXN_WRITE_SIZE)。

Raw KV

  • raw/ 实现 raw 键值 API 及其可选的 MVCC 封装。RawStore::newApiVersion分发到 V1 / V1Ttl / V2 三种内部实现(raw/store.rs):V1 直接基于 snapshot;V1Ttl 额外套一层RawEncodeSnapshot处理 TTL 过期;V2 再叠加RawMvccSnapshot。scan 操作带MAX_TIME_SLICE = 2msMAX_BATCH_SIZE = 1024的时间片/批量限制(raw/store.rs)。

等待与流控

  • lock_manager/ 定义存储侧锁等待契约与本地等待队列辅助:LockDigestDiagnosticContextKeyLockWaitInfoWaitTimeout以及 lock_wait_context / lock_waiting_queue。
  • 活跃的 waiter-manager 工作者、死锁检测器(deadlock detector)与锁管理器 RPC 服务位于 src/server/lock_manager/。
  • txn/flow_controller/ 控制写压力行为。

Max-ts 动态配置

  • config.rs 的MaxTsConfig::validate使用max_driftcache_sync_interval的有效整毫秒值进行比较,要求max_drift严格大于cache_sync_interval,否则启动报错(config.rs)。
  • config_manager.rs 的dispatchmax_ts.max_drift精确地以ReadableDuration下发到ConcurrencyManager::set_max_ts_drift_allowance(config_manager.rs)。
  • ConcurrencyManager有意用整毫秒存储该容差,因为 TSO 物理时间戳以毫秒为单位。例如一个合法的配置值15s1us,其有效强制执行容差为15000ms;这是领域边界(domain boundary),而非在线配置截断。这一行为由单元测试test_validate_max_ts_config_uses_effective_milliseconds锁定(config.rs)。

存储配置全景:参数、默认值与校验规则

storage 相关配置在 src/storage/config.rs 中定义,模板注释在 etc/config-template.toml 中可查。以下是关键字段、默认值及其校验逻辑。

顶层配置([storage]段对应字段)

字段默认值说明与校验
data_dir"./"数据目录(不可在线修改)
engine"raft-kv"raft-kv/partitioned-raft-kv(RaftKv2)。校验时会检查kvtablet子目录是否同时存在:两者同时存在直接报错;若数据目录与 engine 类型不符,则以已存在的数据目录为准自动回退(config.rs)
gc_ratio_threshold1.1已由GcConfig.ratio_threshold取代,仅保留向后兼容
max_key_size8 * 1024(8KB)命令 key 大小上限
scheduler_concurrency1024 * 512调度器 latch 并发度,上限2 * 1024 * 1024,超限会被钳制并告警(v4.0 起 latch 已优化,不建议设过大)
scheduler_worker_pool_sizeCPU≥16 时 8,否则clamp(1, 4)必须大于 0 且不超过max(4, cpu 配额),否则校验失败
scheduler_pending_write_threshold100MB依据 Little's law 的写入积压阈值(按 100MB/s 写速、约 100ms 处理时延估计)
reserve_space5GB磁盘满时保留的压缩空间
reserve_raft_space1GBraft 日志保留空间
enable_async_apply_prewritefalse异步 apply prewrite 开关
api_version1只能为 1 或 2;API V2 强制开启 TTL
enable_ttlfalseAPI V2 下必须为true
background_error_recovery_window1h后台错误恢复窗口,设为 0 可禁用(遇到此类错误立即 panic)
ttl_check_poll_interval12h全量 SST 的 TTL 检查间隔
txn_status_cache_capacity40_000 * 128(5.12M)TxnStatusCache 容量上限(128 槽位)
memory_quota256MB待执行与执行中 storage 命令(kv_get/kv_prewrite/kv_commit 等)的内存配额;校验时若小于 pending-write 阈值会自动抬升
flow_control见下流控子模块
block_cache见下共享 block cache 子模块
io_rate_limit见下IO 限速子模块
max_ts见下max-ts 偏差控制子模块

memory_quota的 256MB 默认值背后有工程依据(config.rs):单条命令内存约等于 1KB KV 对 + 约 448 字节 Command + 约 6184 字节执行 future,256MB 约可支撑 3.5 万个并发执行命令或 18.2 万个排队命令;单节点默认配置下 TPCC prepare(--threads 500)实测内存约 50MB。

[storage.flow-control]

字段默认值说明
enabletrue启用后禁用 kvdb 与 raftdb 的 write stall(memtable 除外),改为在 scheduler 层限流,raftstore 与 apply 不再被阻塞
soft-pending-compaction-bytes-limit192GB达到后开始以ServerIsBusy拒绝部分写请求
hard-pending-compaction-bytes-limit1024GB达到后拒绝全部写请求
memtables-threshold5kvdb 不可变 memtable 数达到该值开始流控
l0-files-threshold20kvdb L0 SST 文件数达到该值开始流控

模板注释见 etc/config-template.toml,流控配置支持动态修改,write_into_metrics会把阈值同步到CONFIG_FLOW_CONTROL_GAUGE指标(config.rs)。

[storage.block-cache]

  • capacity:共享块缓存大小。raft-kv 默认约为系统可用内存的 45%,partitioned-raft-kv 约为 30%(见 config-template.toml);单机多实例部署必须显式配置以避免 OOM。缓存总是共享(shared字段已废弃)。
  • 其他参数:num-shard-bits = 6(容量过小时adjust_shard_bits会自动下调)、strict-capacity-limit = falsehigh-pri-pool-ratio = 0.8low-pri-pool-ratio = 0.2memory-allocator = "nodump"(jemalloc nodump,用于避免缓存内存被 dump)。

[storage.io-rate-limit]

字段默认值说明
max-bytes-per-sec0MB每秒最大 IO 字节数;0 表示不限速,建议设为磁盘厂商标称的最优 IO 带宽
mode"write-only"目前仅支持write-only模式,其他模式校验报错
strictfalse关闭时高优先级 IO 只计数不限速;多租户场景应开启
*_priority见代码前/后台 IO 优先级:前台读写、flush 为 High,compaction 为 Low,其余见 config.rs

校验规则(config.rs):IOType::Other优先级会被强制为 High(该类型可能包含关键 IO);gc_priority会被强制与前台写优先级一致,避免优先级反转。

[storage.max-ts]

字段默认值说明
max-drift60s允许 max_ts 偏离 PD TSO 的最大值
cache-sync-interval15s从 PD 刷新 max_ts 上限的间隔(不可在线修改)
action-on-invalid-update"panic"收到非法 max_ts 更新时的动作(合法值由ActionOnInvalidMaxTs定义)

校验要求max_drift的有效毫秒值必须大于cache_sync_interval的有效毫秒值(config.rs),两者以"有效整毫秒"对齐运行时 TSO 的毫秒精度。

动态配置下发:config_manager.rs

运行时变更由 config_manager.rs 的StorageConfigManger::dispatch处理,它把配置修改分发到具体运行时对象(这也是"改配置必须同步改副作用"这一维护原则的落点):

  • block_cache.capacityConfigurableDb::set_shared_block_cache_capacity,并写CONFIG_ROCKSDB_CF_GAUGE
  • ttl_check_poll_interval→ 调度TtlCheckerTask::UpdatePollInterval
  • flow_control→ 先FlowController::update_config,再按enable开关批量设置各 CF 的disable_write_stall并调用FlowController::enable
  • scheduler_worker_pool_sizeTxnScheduler::scale_pool_size
  • memory_quotaTxnScheduler::set_memory_quota_capacity
  • io_rate_limit→ 更新IoRateLimiter的速率与各IoType优先级;
  • max_ts.action_on_invalid_updateConcurrencyManager::set_action_on_invalid_max_ts_updatemax_ts.max_driftConcurrencyManager::set_max_ts_drift_allowance

关键不变量(Critical Invariants)

维护src/storage时必须始终维持以下不变量:

  1. 命令回调必须恰好完成一次,且携带正确的错误/结果语义(对应ProcessResult与回调契约);
  2. latch 所有权必须串行化冲突命令且不使调度器死锁;
  3. MVCC 读写必须维持 lock、write、default CF 之间的关系;
  4. region 边界、快照上下文、flashback/max-ts 安全必须持续生效;
  5. max-ts 关系校验必须与运行时 TSO 强制执行使用相同的整毫秒精度;
  6. 内存配额与 pending-write 阈值必须保持运维上有效。

可观测性与运维信号

开始故障排查时,建议从以下位置入手:

  • src/storage/metrics.rs
  • src/storage/txn/scheduler.rs
  • src/storage/lock_manager/
  • src/storage/config_manager.rs

可观测信号包括:

  • scheduler 延迟、latch 等待与 pending-write 信号SCHED_HISTOGRAM_VEC_STATIC等热路径指标(scheduler.rs);
  • MVCC 冲突、读与 GC 相关指标
  • 流控与内存配额行为CONFIG_FLOW_CONTROL_GAUGE暴露阈值,配额限流器负责执行;
  • 锁等待与死锁诊断DiagnosticContext携带 key、资源组 tag 与 tracker,用于聚合同一语句产生的锁等待详情(lock_manager/mod.rs);
  • PD 读流报告:包含前台写命令执行的读;当 PD 读字节数与 read-pool 工作量不匹配时,检查 txn/sched_pool.rs 的读流累计与上报逻辑。

TopSQL / 资源计量的逻辑 IO 归属细节

resource-metering.enable-network-io-collection启用时,资源计量/TopSQL 记录逻辑 IO;当resource-metering.enable-detailed-io-collection也启用时,逻辑读与逻辑写被独立选取,前台 SQL 请求的 RocksDB PerfContext 增量被记录为rocksdb_block_read_count。该字段向下游read_iops维度提供相对归因,并不是设备级 IOPS 测量值。

存储命令边界在 metrics.rs 中拥有对该归因的所属权,适用于只读命令与写命令的读阶段。特别地:

  • 事务写与raw_compare_and_swap必须保留Storage的 PerfContext,因为 CAS 在决定是否写入前要先读取旧值;
  • 恢复的悲观锁(pessimistic-lock)批次可能包含来自多个请求的工作,TopSQL 有意使用合成命令的第一个上下文作为整个批次的代表(包括逻辑写与单次 detailed-I/O PerfContext 观测),因此混合 tag 批次的归因是近似值,以避免在锁唤醒路径上做逐项工作;
  • 不要把 raftstore apply/store 写 worker 的活动归因到请求上:这些路径使用只写 PerfContext 指标,且可能批量处理多个请求的工作。

变更管理指导

  • 任何对StorageAPI、回调语义、MVCC 契约或动态配置副作用的改动,都应在同一 patch 内同步更新本维护指南;
  • 存储语义或错误映射变化时,应审计 server 与 raftstore 桥接层;
  • 如果修改配置值却没有同步更新 config_manager.rs 的副作用,该改动通常是不完整的。

变更影响矩阵

变更类型需要检查的范围
公开StorageAPI 或回调变更mod.rs、txn/scheduler.rs,以及 src/server 中面向 RPC 的调用方
调度器、latch 或任务流变更txn/scheduler.rs、txn/task.rs、txn/sched_pool.rs 与指标
MVCC 读写或冲突变更mvcc/、txn/store.rs 与事务命令
raw KV 变更raw/、API 版本处理与 coprocessor_v2 存储适配器
锁等待或流控变更lock_manager/、txn/flow_controller/,以及 src/server 中的运行时工作者
动态配置变更config.rs、config_manager.rs 与运行时副作用消费方

审查清单

提交涉及存储层的改动前,请逐项确认:

  1. 是否影响StorageAPI 行为或回调契约?
  2. 是否触及 txn/scheduler.rs、latches 或锁等待队列?
  3. 是否修改 MVCC 冲突检测、锁解析或 commit-ts 规则?
  4. 是否改变与插件 coprocessor 或外部 API 共享的 raw KV 行为?
  5. 是否改变必须同时适用于raftkvraftkv2桥接层的快照或写入假设(src/server/raftkv/mod.rs 与 src/server/raftkv2)?
  6. 动态配置是否需要 config-manager 副作用?
  7. 是否改变资源控制集成、流控或配额限流器行为?

测试与可观测性

  • MVCC、事务命令、latches、锁等待、raw MVCC 与调度器辅助逻辑拥有大量内联单元测试(例如 config.rs 的 validate 测试 覆盖 engine 类型回退、max-ts 毫秒精度比较与 shard bits 调整);
  • 集成测试多归属 components/test_storage;
  • 调度器、MVCC、读池与流控的热路径指标遍布各模块。

常见故障模式

  • 回调从未调用或调用两次:破坏ProcessResult完成语义,会导致请求悬挂或结果重复;
  • latch 释放 bug:造成命令卡死或乱序;
  • MVCC 跨 CF 写/读漂移:lock/write/default 关系被破坏;
  • 从底层错误中提取 region 错误不正确:影响上层重试与错误上报;
  • 动态配置只更新了内存中的 config 值、未应用到运行时对象:典型的 config_manager 副作用缺失;
  • 锁等待唤醒逻辑导致饥饿或漏唤醒:影响悲观事务的并发与延迟。

阅读地图与配套文档

建议按 1 → 10 的顺序阅读代码地图中的必读文件(见上文"代码地图"一节),配套文档包括:

  • repo-overview.md
  • src/server.md
  • components/raftstore.md
  • PERFORMANCE_CRITICAL_PATH.md

术语表

  • MVCC:multi-version concurrency control,基于 default/lock/write CF 的多版本并发控制;
  • Latch:调度器侧用于冲突命令串行化的原语;
  • ProcessResult:调度执行结果,通过回调返回;
  • Flow control:压力管理逻辑,用于减缓或门控写密集活动。

相关组件

  • src/server/service/kv.rs:Storage的主要调用方;
  • src/server/raftkv/mod.rs:把写请求桥接进 raftstore;
  • src/server/lock_manager/:围绕存储侧等待队列契约的活跃锁管理器工作者与死锁检测服务;
  • components/resource_control 与 components/resource_metering:直接与调度和计量集成。

【免费下载链接】tikvDistributed transactional key-value database, originally created to complement TiDB项目地址: https://gitcode.com/GitHub_Trending/ti/tikv

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 19:02:13

Boost二级升压光伏并网系统设计与Simulink建模

1. Boost二级升压光伏并网系统的核心设计思路光伏发电系统在实际应用中常面临输出电压不足的问题&#xff0c;特别是在并网场景下。Boost二级升压结构通过两级升压转换&#xff0c;能够有效解决单级升压比受限的难题。这种拓扑结构的第一级通常将光伏板输出的不稳定低压&#x…

作者头像 李华
网站建设 2026/9/14 19:02:00

基于JavaWeb和Hadoop的图书推荐系统设计与实现

简介&#xff1a;一套基于JavaWeb与Hadoop的图书推荐系统大作业项目&#xff0c;源码、数据库脚本和设计报告齐备&#xff0c;面向计算机专业正在准备课程设计或期末大作业的学生&#xff0c;也适合需要大数据实战练习的学习者。整套资源共78个文件&#xff0c;涵盖Java源码与编…

作者头像 李华
网站建设 2026/9/14 19:01:37

Unity failed to set the cursor 报错?走 TaoToken 的 Codex 这样改 Read/Write

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 19:00:50

Tolaria 停在 2027 未来日期版本且无法继续更新时怎么恢复?

Tolaria 停在 2027 未来日期版本且无法继续更新时怎么恢复&#xff1f; 【免费下载链接】tolaria Desktop app to manage markdown knowledge bases 项目地址: https://gitcode.com/GitHub_Trending/to/tolaria 如果你的 Tolaria 桌面应用版本显示为 2027.7.31、2027.8.…

作者头像 李华