使用Cassandra JDBC驱动读取记录时数据丢失的原因咨询
问题原因分析
这个现象的核心是Cassandra的写入路径机制和非精准分区查询的可见性逻辑共同导致的,即使单节点集群也会出现,具体原因如下:
1. Cassandra写入的异步持久化特性
Cassandra的写入流程是:
- 先将数据写入内存中的
memtable,只要memtable写入成功,就会向客户端返回写入成功 - 之后memtable会异步批量刷写到磁盘上的SSTable,这个过程不是立即触发的(默认当memtable达到一定大小或定时触发)
在memtable还没刷写到SSTable之前,只有通过精准匹配分区键的查询(比如SELECT * FROM productInfo WHERE productId = ?)才能读取到这部分数据;而像SELECT *全表扫描、SELECT * WHERE timestamp > ?这类非精准分区的范围/过滤查询,是通过遍历磁盘上的SSTable来获取数据的,不会主动扫描memtable中的未持久化数据(或者说,这类查询的扫描范围在启动时就固定为已存在的SSTable,不会包含查询过程中写入的memtable数据)。
2. 分区键设计放大了可见性问题
你的表中每个productID都是唯一分区键,意味着10000条记录对应10000个独立分区。当读写并行时:
- 写应用持续向不同分区写入数据,大量新数据分散在各个分区的memtable中
- 读应用的初始全表扫描需要遍历所有10000个分区,但此时很多分区的新数据还在memtable里,没被刷写到SSTable,因此这部分数据不会被初始扫描捕获
- 后续你用
timestamp > lastNotedTimestamp做增量查询,而那些没被初始扫描到的记录,其timestamp可能小于初始扫描得到的lastNotedTimestamp(因为是并行写入,这些记录的写入时间早于初始扫描完成时间),导致后续查询永远不会再读取到它们,最终表现为“随机丢失记录”
3. 写入完成后无丢数的原因
当所有写入操作完成后,Cassandra会将所有memtable异步刷写到SSTable(你也可以手动执行nodetool flush testkeyspace productInfo强制刷写)。此时全表扫描或范围查询可以遍历到所有SSTable中的数据,自然能返回全部记录,不会出现丢数。
补充验证点
你可以做个简单测试验证这个逻辑:
- 在读写并行时,当发现丢记录后,立即执行
nodetool flush testkeyspace productInfo - 再执行查询,之前丢失的记录会出现在结果集中
内容的提问来源于stack exchange,提问作者Mohamed Kamarudeen
相关产品推荐
相关产品推荐

