Hyperledger Fabric中不使用start_key/end_key实现CouchDB分页查询
Hyperledger Fabric 下CouchDB无start_key/end_key的分页查询方案
不需要依赖start_key、end_key做范围切分,直接使用Fabric原生适配CouchDB的书签(bookmark)分页能力即可,该能力从Fabric 1.4版本开始稳定支持,适配所有使用CouchDB作为状态数据库的部署场景。
实现逻辑
核心依托两个底层能力完成分页,全程不需要手动拼接键范围:
- CouchDB原生的bookmark分页标记:每次查询返回结果时会同步返回一个不透明的字符串标记,指向当前查询结果集的最后一条数据位置
- Fabric链码、SDK层对富查询分页参数的原生透传,不需要额外修改底层配置
具体操作步骤
- 第一步:编写符合CouchDB Mango语法的富查询语句,按需添加过滤、排序规则即可,不需要额外关联键范围。例如查询所有资产类型为
fruit的状态数据,查询语句示例:
{ "selector": { "assetType": "fruit" }, "sort": [{"_id": "asc"}] }
注意:必须提前为查询条件、排序字段创建对应的CouchDB Mango索引,否则查询会触发全表扫描,极易触发Fabric的查询超时限制,性能也无法满足生产要求。首次查询时bookmark参数传空字符串即可。
- 第二步:调用Fabric提供的带分页能力的富查询接口,不要用普通的富查询接口:
- Go版本链码调用
GetQueryResultWithPagination方法,入参顺序为:Mango查询语句、单页数据条数、bookmark值 - Node.js版本链码调用
getQueryResultWithPagination方法,参数逻辑和Go版本一致 - 应用层通过Fabric SDK调用时,对应接口同样支持传入pageSize、bookmark参数,无需额外封装
Go版本链码的核心调用示例:
// 首次查询bookmark传空,单页大小设为10 queryStr := `{"selector":{"assetType":"fruit"},"sort":[{"_id":"asc"}]}` pageSize := int32(10) bookmark := "" resultIter, respMeta, err := stub.GetQueryResultWithPagination(queryStr, pageSize, bookmark) if err != nil { return shim.Error(err.Error()) } defer resultIter.Close() // 遍历解析当前页数据 var pageData []map[string]interface{} for resultIter.HasNext() { kv, err := resultIter.Next() if err != nil { return shim.Error(err.Error()) } var item map[string]interface{} json.Unmarshal(kv.Value, &item) pageData = append(pageData, item) } // 取出下一页的书签,随当前页数据返回给上层 nextBookmark := respMeta.Bookmark - Go版本链码调用
- 第三步:实现翻页逻辑。上层应用拿到当前页返回的
nextBookmark后,下一次查询将该值作为bookmark参数传入,即可拉取对应位置的下一页数据。重复该流程,直到返回的bookmark为空、或者当前页返回的数据条数小于设定的pageSize,即代表已经遍历完所有符合条件的数据。
注意事项
- 分页全程不要修改查询条件、排序规则:规则变更会导致bookmark失效,出现数据重复、漏查的问题
- bookmark是CouchDB生成的不透明字符串,不需要手动解析内容,直接透传即可,不同查询条件、不同CouchDB实例生成的bookmark不能混用
- 单页大小不要设置过大,建议控制在100条以内,避免单次查询返回数据量太大触发节点性能瓶颈
- 该分页方式比start_key/end_key的范围分页灵活性更高,支持任意字段的过滤查询,不需要提前设计复合键做范围切分
内容的提问来源于stack exchange,提问作者Ravi Kumar
相关产品推荐
相关产品推荐

