Riak首次按key查询对象返回空,重试可正常获取如何排查
问题背景
我们有一个由5个Riak节点组成的集群,使用riak-java-client完成Riak数据的创建、读取、删除操作。目前遇到旧数据读取异常问题:部分场景下首次按key查询对象会返回null,使用相同key重试查询即可得到正确对象。
以下是对应读取代码:
String uuid = <keyInRiak>; Namespace bucket = new Namespace("default", "default"); Location location = new Location(bucket, uuid); FetchValue fetchValue = new FetchValue.Builder(location).build(); FetchValue.Response response = riakClient.execute(fetchValue); if (!response.isNotFound()) { RiakObject riakObject = response.getValue(RiakObject.class); if(riakObject != null) { BinaryValue binaryValue = riakObject.getValue(); byte[] result = binaryValue != null ? binaryValue.getValue() : null; // ... process result ... } }
排查诊断步骤
- 首先确认读写一致性参数配置:Riak默认副本数N值为3,默认读参数R值为1(仅需要1个副本返回结果就算读成功),写参数W值默认也为1。如果首次请求路由到了还未同步到对应数据的副本节点,就会返回空结果,重试时请求路由到持有数据的节点就能正常返回。可以通过
riak-admin bucket-type get default bucket default命令查询目标bucket的N、R、PR、W、PW配置,若R确实为1,可以在构造FetchValue时手动指定读参数为多数派:FetchValue fetchValue = new FetchValue.Builder(location).withR(Quorum.quorumQuorum()).build();,强制要求超过半数的副本响应一致才返回结果,避免读取到未同步节点的空数据。如果是写入后立即读取的场景,建议同时将写入的W参数调整为quorum,保证写入成功时多数副本已经完成同步。 - 检查是否存在*删除墓碑(tombstone)*残留问题:Riak删除数据时会先写入墓碑标记,在墓碑被GC清理前,如果请求优先读取到了持有墓碑的副本,会返回not found,重试时请求落到持有有效数据的副本就会返回正常结果。可以查看集群的
delete_mode配置,确认墓碑保留时长是否符合业务场景,若存在频繁删除后立即写入相同key的场景,建议适当调高楼碑保留时长,避免数据和墓碑同时存在于不同副本导致读取异常。 - 排查是否存在*多版本冲突(Sibling)*未处理的情况:如果Riak桶开启了多版本支持(allow_mult=true),当多个副本存在不同版本的同一key数据时,客户端不会自动返回合并后的数据,
response.getValue(RiakObject.class)会直接返回null,此时首次读拿到null,第二次读时后台可能已经完成了冲突合并,就能拿到正常数据。可以在代码中增加sibling判断逻辑,确认是否触发了该问题:if (response.hasSiblings()) { // 手动处理冲突逻辑 } - 检查集群节点健康和数据同步状态:执行
riak-admin ring-status查看集群是否有节点处于离线、加入/离开、数据同步中的状态,若有节点数据同步落后,请求落到落后节点时也会返回空结果。可以在代码中增加日志,打印每次读请求对应的响应节点信息,确认首次返回空的请求是否固定落到特定节点,进一步验证节点数据一致性问题。
内容的提问来源于stack exchange,提问作者Sprinter
相关产品推荐
相关产品推荐

