You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

IO密集场景下如何从MariaDB快速提取海量CDR数据?

海量CDR数据查询性能优化方案

关键信息

cdrs表结构

+-----------------+---------------+------+-----+---------+----------------+
| Field           | Type          | Null | Key | Default | Extra          |
+-----------------+---------------+------+-----+---------+----------------+
| id              | bigint(12)    | NO   | PRI | NULL    | auto_increment |
| server_id       | tinyint(2)    | NO   |     | 0       |                |
| cdr_id          | bigint(13)    | NO   | MUL | 0       |                |
| user_id         | int(11)       | NO   | MUL | 0       |                |
| transaction_id  | int(11)       | YES  | MUL | NULL    |                |
| sip_id          | int(11)       | NO   | MUL | 0       |                |
| call_type       | tinyint(2)    | YES  | MUL | NULL    |                |
| did_from        | char(24)      | YES  |     | NULL    |                |
| did_from_alias  | bigint(18)    | YES  | MUL | 0       |                |
| did_to          | char(24)      | YES  |     | NULL    |                |
| did_to_alias    | bigint(18)    | YES  | MUL | 0       |                |
| call_status     | char(12)      | YES  | MUL | NULL    |                |
| start_time      | int(11)       | NO   | PRI | 0       |                |
| duration        | decimal(13,3) | YES  |     | NULL    |                |
| billed_duration | decimal(13,3) | NO   |     | 0.000   |                |
| rate            | decimal(10,4) | YES  |     | NULL    |                |
| amount          | decimal(10,4) | YES  |     | NULL    |                |
| usf             | decimal(10,4) | YES  |     | NULL    |                |
| total           | decimal(10,4) | YES  | MUL | NULL    |                |
| country         | varchar(96)   | YES  |     | NULL    |                |
| country_id      | int(8)        | NO   |     | 0       |                |
| code            | varchar(8)    | NO   |     |         |                |
+-----------------+---------------+------+-----+---------+----------------+

表索引信息

+---------+------------+------------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+
| Table   | Non_unique | Key_name         | Seq_in_index | Column_name    | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Ignored |
+---------+------------+------------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+
| cdrs |          0 | PRIMARY          |            1 | id             | A         |   573346816 |     NULL | NULL   |      | BTREE      |         |               | NO      |
| cdrs |          0 | PRIMARY          |            2 | start_time     | A         |   573346816 |     NULL | NULL   |      | BTREE      |         |               | NO      |
| cdrs |          1 | i_cdr_id         |            1 | cdr_id         | A         |   573346816 |     NULL | NULL   |      | BTREE      |         |               | NO      |
| cdrs |          1 | i_user_id        |            1 | user_id        | A         |      158909 |     NULL | NULL   |      | BTREE      |         |               | NO      |
| cdrs |          1 | i_call_type      |            1 | call_type      | A         |       19887 |     NULL | NULL   | YES  | BTREE      |         |               | NO      |
| cdrs |          1 | i_transaction_id |            1 | transaction_id | A         |   143336704 |     NULL | NULL   | YES  | BTREE      |         |               | NO      |
| cdrs |          1 | i_total          |            1 | total          | A         |      163953 |     NULL | NULL   | YES  | BTREE      |         |               | NO      |
| cdrs |          1 | i_start_time     |            1 | start_time     | A         |    24928122 |     NULL | NULL   |      | BTREE      |         |               | NO      |
| cdrs |          1 | i_call_status    |            1 | call_status    | A         |        9877 |     NULL | NULL   | YES  | BTREE      |         |               | NO      |
| cdrs |          1 | i_sip_id         |            1 | sip_id         | A         |      353481 |     NULL | NULL   |      | BTREE      |         |               | NO      |
| cdrs |          1 | i_did_to         |            1 | did_to_alias   | A         |   286673408 |     NULL | NULL   | YES  | BTREE      |         |               | NO      |
| cdrs |          1 | i_did_from       |            1 | did_from_alias | A         |    57334681 |     NULL | NULL   | YES  | BTREE      |         |               | NO      |
+---------+------------+------------------+--------------+----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+

问题现状

  • 表行数已超int最大值,每日GB级增长,未来将达数十至数百GB,部署在Raid10 SSD存储上。
  • 每月1号客户提取上月通话记录时,因数据不在InnoDB缓存,查询为IO密集型:
    • 示例查询:SELECT * FROM cdrs WHERE user_id='<SOME_USER_ID' AND start_time>=1654099200 AND start_time<1656691200,返回52627431条数据耗时50秒。
    • 部分高量客户提取1小时数据需30分钟,性能严重不足。

已尝试无效方案

  • 汇总表:无法满足客户获取原始数据的需求。
  • 按月分区:性能提升不明显。
  • MariaDB ColumnStore:对CDR原始数据提取无显著帮助。
  • Redis:数据量达1.1TB且持续增长,无法承载。

可行优化方案

1. 新增复合索引(优先级最高)

当前查询的核心过滤条件是user_id + start_time范围,现有独立索引无法高效支撑联合过滤。创建复合索引:

CREATE INDEX idx_user_start ON cdrs(user_id, start_time);

该索引可直接定位目标用户的时间范围数据,避免全表扫描或二次查找,大幅减少IO次数。若允许调整查询语句,可改为覆盖索引查询(仅返回所需字段)进一步降低IO;即使必须返回全字段,复合索引依然能极大提速。

2. 冷数据归档与分层存储

  • 将超过6个月的历史CDR数据归档到只读从库或低成本存储介质,确保原始数据可直接获取。
  • 主库仅保留最近3个月的热数据,减少主库数据量与IO压力,查询历史数据时路由至归档库。

3. 分库分表(长期扩容方案)

按user_id进行哈希或范围分片,将大表拆分为多个小表:

  • 分片后每个节点数据量大幅降低,查询时仅需访问目标用户所在分片,避免跨分片扫描。
  • 结合时间维度分片(如按季度),进一步缩小查询范围。

4. 优化查询输出方式

  • 使用SELECT ... INTO OUTFILE直接将查询结果写入磁盘文件,比通过应用层返回结果集快数倍,用户可直接下载文件。
  • 开启MySQL 8.0+/MariaDB的并行查询功能,利用多核CPU并行扫描数据,提升大结果集查询速度。

5. 数据结构瘦身

  • 移除冗余字段:country字段可通过country_id关联字典表获取,无需存储在CDR表中,减少单条记录大小,提升每页存储行数。
  • 调整字段类型:将varchar(96)的country改为char(2)(ISO国家代码),code字段根据实际内容调整为更小类型,进一步压缩数据体积。

6. 缓存预热(临时应急方案)

每月查询前,提前执行轻量查询预热缓存:

SELECT COUNT(*) FROM cdrs FORCE INDEX(idx_user_start) WHERE user_id='<SOME_USER_ID' AND start_time>=xxx AND start_time<xxx;

将目标数据加载到InnoDB缓冲池,避免查询时全IO读取,仅适用于数据量较小的场景。


内容的提问来源于stack exchange,提问作者Joséph Flames

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 11:06:20