1. 业务背景
类似Dexx的交易工具需要抓取和存储dex的大量交易对产生的大量交易数据,交易对数量可达数百万,区块中交易笔数每日可达百万级。 如何保证每个交易对的数据存储和查询效率,尤其是高频交易对(如ETH/USDT等)成为了亟待解决的问题。
用户侧主要有两种查询:
- 指定交易对:用户查询某个交易对最近的n笔交易。对应[最新成交]的功能模块
- 指定交易对和指定钱包地址:用户查询某个交易对某个钱包地址最近的n笔交易。用于发现聪明钱
目前1年的交易数量约10亿数据,使用如下的分表方案
2. 当前方案:基于交易对地址路由的分表方案
现有的分表策略中,交易对的交易记录是通过对交易对地址(PoolAddress
)进行哈希计算,然后将哈希值对256取余(PoolAddress % 256
),来决定该交易对的数据存储在trade_0
到trade_255
之间的256个分表中的一个。
分表路由规则:(PoolAddress(交易对地址字符串) % 256
),计算结果决定该交易对的数据存储在trade_0
到trade_255
的哪一个分表。
当前查询语句
- 指定交易对:
where 交易对=?
- 指定交易对和指定钱包地址:
where 交易对=? And 钱包地址=?
3. 当前问题:热点交易对导致分表数据不均衡
尽管分表方案能将大部分交易对的交易记录均匀分布到256个分表中,但热点交易对(如ETH/USDT)的查询频率和交易量可能会大幅度高于其他交易对。这样的数据分布依然可能造成一些分表(例如trade_0
)的负载过重,导致查询延迟增加,系统性能下降。
问题分析:
- 热点交易对的查询压力:存储热门交易对的分表数据量远超过其他分表,单表可达千万,导致查询效率低下。同时热门交易对的查询频率也高,压力更大。累的累死。。。
- 冷门交易对的资源浪费:与热门交易对相比,冷门交易对几乎没有查询需求,对应的分表(例如
trade_100
)几乎没有负载,导致计算和存储资源的浪费。闲的闲死。。。
4. 解决方案:手动调整路由分表并迁移数据
为了优化查询性能和数据分布,提出手动调整分表路由的方案,针对热门交易对进行精确的手动调整,而无需进行二次分表。
4.1 手动调整分表路由
针对热门交易对(例如ETH/USDT),我们可以选择通过手动调整路由来优化分表分布。即将这些热门交易对的交易记录迁移到新的分表中,从而减轻原有分表(如trade_0
)的查询压力。具体过程如下:
执行思路
识别热点交易对,规划新的分表-> 入库双写+数据迁移 -> 查旧表改为查新表
4.2 实施过程
为确保迁移过程中的数据一致性和查询不中断,我们采用双写策略来实现平滑迁移。迁移的具体步骤如下:
-
启用双写:修改爬虫入库逻辑,确保所有新的交易记录同时写入旧的分表(例如
trade_0
)和新的分表(例如trade_256
)。这样可以确保新交易记录在新分表不丢失。 -
迁移历史数据:将热点交易对的历史交易记录从原有分表(如
trade_0
)迁移到新的分表(如trade_256
)。编写脚本,分批将历史数据迁移到新分表。 -
确认数据一致性:验证新旧分表中的数据一致,避免数据丢失或重复。
-
切换查询路由:历史数据迁移完成后,将查询路由切换到新的分表。此时,所有关于ETH/USDT的查询将直接访问
trade_256
,而不再访问原分表。 -
停止双写:在确认迁移完成并且新分表已经承载查询流量后,停止双写模式。此时,所有数据都只写入新的分表。
-
清理旧数据:一旦确认新的分表完全接管了查询请求,可以将旧分表中的相关数据删除,释放存储资源。
5. 缺点与挑战
- 操作复杂性:手动调整分表路由和数据迁移过程需要精准的计划和执行。
- 数据一致性问题:双写和数据迁移期间,必须确保新旧表的数据一致性,避免因网络问题或系统故障造成的数据丢失或重复。
6. 总结
通过手动调整路由分表和数据迁移,可以有效解决由于热点交易对造成的数据倾斜问题。 相比于复杂的二次分表方案,这种方法在操作上更为简单和灵活,并能高效应对去中心化交易所中高频查询的挑战。
原创文章转载请注明出处: 分表后数据倾斜问题:手动调整分表方案