如何在不耗尽内存的情况下复制大型Cassandra表?
我尝试运行一个简单的Cassandra数据库COPY脚本,示例如下:
COPY my_keyspace_name.my_table_name TO 'cassandra_dump/my_keyspace_name.my_table_name.csv' WITH HEADER=true AND PAGETIMEOUT=40 AND PAGESIZE=20 AND DELIMITER='|';
该脚本在大多数表上可正常运行,但处理最大表时出现内存分配不足错误。该表数据量不到1GB,远小于错误信息声称的大小。错误信息如下:
749314 rows exported to 1 files in 9 minutes and 11.240 seconds.
./dump_cassandra.sh: xmalloc: ../../.././lib/sh/strtrans.c:63: cannot allocate 18446744072166431589 bytes (6442528768 bytes allocated)
stdout_lines: ["[Thu May 17 13:41:47 UTC 2018] Executing the following query:", "COPY my_keyspace_name.my_table_name TO 'cassandra_dump/my_keyspace_name.my_table_name.csv' WITH HEADER=true AND PAGETIMEOUT=40 AND PAGESIZE=20 AND DELIMITER='|';"]
我参考过相关回答但未解决问题。是否有遗漏要点导致无法成功复制该(相对)大型表?
**编辑:**该错误似乎与环境相关,数据量相近的不同服务器上结果不一。
那个离谱的18EB内存分配请求一看就不是真的内存需求,这是无符号整数溢出导致的假报错,根本不是你的表太大的问题。结合你提到的环境差异,大概率是shell或者cqlsh的版本bug在搞鬼,给你几个具体的排查和解决方向:
检查并升级bash版本:错误里提到的
strtrans.c是bash的内部库文件,旧版本的bash(比如4.3及之前)在处理超长字符串时存在整数溢出的bug,会触发这种诡异的内存分配报错。你可以运行bash --version查看版本,升级到bash 4.4+的稳定版再试试。调整cqlsh的COPY参数,避开可能的触发点:
- 你已经设了很小的
PAGESIZE=20,可以再尝试缩小到10,减少单次返回的行数据量,降低字符串拼接的压力; - 暂时去掉
HEADER=true参数,有些旧版本cqlsh在生成表头时的字符串处理逻辑有问题,先测试无表头导出是否正常; - 加上
CHUNKSIZE=100参数,控制写入文件的块大小,避免一次性缓存过多数据。
- 你已经设了很小的
直接在cqlsh中运行命令,绕过shell脚本:你的错误是从shell脚本里抛出来的,有可能是脚本的字符串转义或者环境变量导致的问题。直接登录cqlsh,手动执行COPY命令,看看是否还会报错,排除shell的影响。
换成官方推荐的批量导出工具dsbulk:Cassandra的COPY命令本身就不是为超大表设计的,官方现在更推荐用dsbulk工具,它的内存管理更高效,稳定性更好,而且支持断点续传,适合处理大表导出。你只需要简单配置一下连接信息,就能快速导出数据,命令示例大概是:
dsbulk unload -k my_keyspace_name -t my_table_name -url cassandra_dump/my_table_name.csv -delim '|' -header true
如果还是不行,建议对比两台服务器的bash版本、cqlsh版本、Cassandra版本,找出环境差异的核心点,大概率就是版本bug导致的。
内容的提问来源于stack exchange,提问作者Ben Harrison

