业务背景

财税平台核心业务表一张表数据量达千万,每日更新百万次,要求在不依赖Redis的场景下实现复杂分页查询毫秒级响应。

一、核心挑战:

  1. 海量数据 & 高频更新: 单表千万行,日更新百万次。

  2. 深度分页慢: LIMIT 5000,10 导致全表扫描。

  3. 混合负载阻塞: 复杂统计查询拖慢在线交易。

  4. 无Redis依赖: 需纯数据库与架构优化解决。

二、核心解决思路: 

分库分表打基础 + 冷热分离减压力 + 精准查询提速度 + 旁路统计保实时

核心优化方案与落地实现

  1. 根治深度分页:游标分页 + 强力索引 (立即见效)

    • 问题根源: OFFSET 越大,数据库越要扫描前面所有数据。

    • 解决方案:

      • 前端改造: 分页请求带上上一页最后一条记录的唯一排序值(如 last_idlast_update_time)。

      • 后端查询:

        -- 假设主键是`id`,按时间`update_time`倒序分页
        SELECT * FROM your_table
        WHERE update_time < :last_update_time OR (update_time = :last_update_time AND id < :last_id) -- 关键!避免漏/重
        ORDER BY update_time DESC, id DESC
        LIMIT 10;

      • 索引是命根子: 必须为 (update_time DESC, id DESC) 创建联合索引。EXPLAIN 检查是否 Using index,拒绝 Using filesort/filesort

    • 好处: 无论翻到第几页,查询速度恒定极快(毫秒级)。

  2. 应对海量数据与更新:分库分表 + 冷热分离 (架构基石)

    • 问题根源: 单表/单库容量和性能有极限,冷热数据混杂效率低。

    • 解决方案:

      • 分库分表 (必做!):

        • 分片键选择: 高查询频率字段 (如 tax_no 纳税人识别号、firm_id 企业ID)。基因法常用 (如取 tax_no 后几位或哈希)。

        • 工具: ShardingSphere (推荐) 或 MyCAT。配置分片规则(如 tax_no % 16 分16库/表)。

        • 效果: 数据分散存储,读写负载分摊到多个物理节点,突破单点瓶颈。

      • 冷热数据自动分离:

        • 定义热数据: 近 N 天/月/年数据 (如近1年)。

        • 存储:

          • 热数据: 放在分库分表后的 主库集群 (高性能SSD)。

          • 冷数据: 定期 (如每日凌晨) 迁移到 历史库/表。历史表可按月/年分区存储。

        • 查询路由: ShardingSphere 根据查询条件中的时间范围自动路由到热表或冷表。SELECT ... WHERE create_time > '2024-01-01' -> 查热表;SELECT ... WHERE create_time < '2023-01-01' -> 查冷表/分区。

        • 迁移工具: 用 DataXSpark SQL 或存储过程实现低峰期稳定迁移。

    • 好处: 在线库只保留小部分热数据,压力骤降;历史查询走专用存储,互不影响。

  3. 解决复杂统计慢:旁路实时数仓 (ClickHouse/StarRocks) (解放主库)

    • 问题根源: 在主库跑 COUNT(), SUM(), GROUP BY 等聚合,消耗巨大 CPU/IO,阻塞交易。

    • 解决方案:

      • 选型: ClickHouse (极致分析速度) 或 StarRocks (更好实时性 & 并发点查)。StarRocks 更适合需要同时支持高并发点查和实时分析的场景。

      • 数据同步:

        • 核心: Flink CDC (Change Data Capture) 直接读取数据库 Binlog

        • 流程: MySQL Binlog -> Kafka -> Flink SQL (做简单清洗/转换) -> 写入 ClickHouse/StarRocks

        • 优势: 准实时 (秒级/分钟级延迟),低侵入,高吞吐。

      • 统计查询: 所有复杂报表、聚合查询、大范围搜索,全部走 ClickHouse/StarRocks。主库只负责简单交易和基于热数据的分页/点查。

      • 物化视图 (加速常用统计):

        -- ClickHouse (示例)
        CREATE MATERIALIZED VIEW tax_monthly_sum_mv
        ENGINE = AggregatingMergeTree()
        ORDER BY (tax_no, month)
        AS SELECT
            tax_no,
            toYYYYMM(callback_time) AS month,
            countState() AS total_count
        FROM task_info_all
        GROUP BY tax_no, month;
        -- 查询物化视图
        SELECT tax_no, month, countMerge(total_count) AS total FROM tax_monthly_sum_mv WHERE ...;

    • 好处: 主库彻底解脱;统计查询飞快 (亚秒级到秒级);数据准实时。

  4. 高频点查加速:智能 JVM 缓存 (Caffeine) (补充优化)

    • 场景: 高频访问的、相对静态的配置数据、纳税人基础信息等。

    • 方案:

      // Caffeine 示例 (Guava Cache 类似)
      LoadingCache<String, TaxpayerInfo> taxpayerCache = Caffeine.newBuilder()
          .maximumSize(10000) // 缓存条目数
          .expireAfterWrite(5, TimeUnit.MINUTES) // 写入后5分钟过期
          .refreshAfterWrite(1, TimeUnit.MINUTES) // 写入后1分钟开始异步刷新
          .build(key -> database.queryTaxpayer(key)); // 缓存加载逻辑
      // 使用缓存
      TaxpayerInfo info = taxpayerCache.get("TAX12345678");

    • 关键点:

      • 缓存内容: 只缓存变更频率低的数据 (如企业名称、基础税种信息)。

      • 过期时间: 设置合理的过期时间 (秒级到分钟级),平衡时效性与数据库压力。

      • 刷新机制: refreshAfterWrite 保证后台异步更新,用户可能看到短暂旧数据但体验流畅。

      • 一致性 (弱): 接受短暂不一致,或通过监听 Binlog 变更失效缓存 (更复杂)。

    • 好处: 将大量重复点查拦截在应用层,极大减轻数据库压力,响应更快 (毫秒内)。

三、 性能优化预期效果

场景 优化前 优化后 核心手段
深度分页 (第1000页) 10s - 30s+ < 100ms 游标分页 + 强力联合索引
月维度统计查询 5s - 15s+ < 1s 旁路实时数仓 (ClickHouse/StarRocks)
高频单条点查 50ms - 200ms < 5ms JVM 缓存 (Caffeine) + 主库索引
数据库 CPU 峰值 80% - 100% < 40% 分库分表 + 冷热分离 + 旁路统计

四、 长期演进与关键洞见

  1. 分库分表精细化:

    • 自动扩缩容: 基于 ShardingSphere 或云数据库能力,根据数据量/负载自动增加或合并分片。

    • 更优分片键: 持续分析业务查询模式,优化分片键选择 (如加入时间范围)。

  2. 实时数仓增强:

    • 统一查询入口: 利用 StarRocks 的联邦查询能力,尝试将对历史数据的查询也统一到数仓。

    • 更实时: 优化 Flink CDC 链路,降低同步延迟。

  3. 存储成本优化:

    • 冷数据归档: 将极冷数据 (如5-7年以上) 从 ClickHouse/StarRocks 迁移到更便宜的 对象存储 (S3/OSS) + Hive/Iceberg,通过 Presto/Trino 查询。

    • 列式存储: 历史库/表可评估使用 TiDB (HTAP) 或 MyRocks (压缩率高)。

  4. 智能化与自治:

    • 慢查询根因分析: 自动分析慢查询日志,定位是缺索引、冷热路由错误还是数仓问题。

    • 索引建议: 基于实际负载,数据库本身 (如 MySQL 8.0) 或监控工具 (如 PMM) 可提供索引优化建议。

    • 缓存策略调优: 根据缓存命中率和数据变更频率自动调整 JVM 缓存大小和过期时间。

五、 核心洞见与铁律:

  • 分库分表是海量数据基石: 千万级日更,单库单表必死。必须做!

  • 冷热分离是性价比之王: 把最热的、最小的数据集放在最快的存储上,效果立竿见影。

  • 查询走对路: 简单交易/点查/热数据分页 -> 主库;复杂分析/历史扫描 -> 实时数仓严格区分!

  • 索引是查询的命脉: 没有正确的索引,一切优化都是空谈。持续优化!

  • 旁路解耦是关键: 用 Flink CDC + 实时数仓将分析负载从主库剥离,是保证 OLTP 稳定的核心。

  • JVM 缓存是双刃剑: 用得好是利器,用不好(缓存大对象、频繁变更数据)反成负担。

  • 可观测性是保障: 强大的监控 (数据库、Flink、数仓、缓存) 是发现瓶颈、验证效果、快速排障的基础。

六、实施风险与规避:

  • 分库分表改造风险高:

    • 灰度: 先切读流量,验证无误再切写流量。

    • 双写: 过渡期可采用双写方案,确保可回退。

    • 数据校验: 迁移后必须严格校验数据一致性。

  • 数据同步延迟:

    • 监控: 严密监控 Flink CDC 延迟。

    • 保障: 确保数仓延迟在业务可接受范围内 (如 < 1分钟)。

    • 降级: 延迟过大时,复杂查询可暂时降级查主库 (需评估影响)。

  • 缓存不一致:

    • 接受弱一致性: 明确缓存数据的时效性要求。

    • 监听失效: 关键数据可通过监听 Binlog 主动失效缓存 (增加复杂度)。

七、总结:

千万级日更数据库的毫秒级分页查询,绝非单一技术可解决。本方案提供了一套切实可行、阶梯式实施的架构:

  1. 紧急止血: 立即实施 游标分页 + 强力索引,快速解决深度分页慢的问题。

  2. 中期治本: 核心投入 分库分表 + 冷热分离 + 旁路实时数仓 (Flink CDC + ClickHouse/StarRocks),根治容量、性能和混合负载问题。

  3. 补充优化: 合理运用 JVM 缓存 减轻高频点查压力。

  4. 长期演进: 在架构稳定的基础上,追求 自动化、智能化、成本优化

这套方案完全不依赖 Redis,充分利用了数据库内核优化、架构分层解耦和大数据生态组件,实现了高性能、高扩展性和低成本的目标。实施时需结合具体业务场景,做好详细设计、灰度发布和全面监控。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐