使用spring-data-dynamodb按DynamoDB GSI字段SongTitle排序报错如何解决?
问题原因
- GSI注解配置错误:你在
SongTitle字段上同时标注了@DynamoDBIndexHashKey和@DynamoDBIndexRangeKey,且都指向同一个GSIGSISongTitle。每个DynamoDB的表/全局二级索引只能有唯一的分区键(Hash Key)和唯一的可选排序键(Range Key),同一个字段不可能同时充当同一个索引的两类键,该注解配置本身就是非法的。 - 排序逻辑不符合DynamoDB底层规则:DynamoDB不支持跨分区的全局排序,你直接调用
findAll(pageable)传入排序参数的写法是关系型数据库的思路。DynamoDB只有Query操作支持排序,且必须指定分区键,仅能在同一个分区键对应的数据集范围内,对该分区下的排序键做升序/降序排序,没有全表维度的全局排序能力。findAll默认走的是Scan操作,本身不支持排序,框架无法基于你的请求生成合法的DynamoDB查询语句,因此抛出键条件无效的异常。
修复方案
可根据你的数据量选择对应方案:
场景1:数据量较小(万级以内)
直接在内存中做排序分页即可,不需要调整GSI和实体类:
// 先查询全表数据 List<ImprovedMusic> allData = myRepositor.findAll(); // 内存中按SongTitle降序排序 allData.sort((o1, o2) -> o2.getSongTitle().compareTo(o1.getSongTitle())); // 手动分页,注意PageRequest页码默认从0开始,你原有代码写的1代表第二页 int pageNum = 1; int pageSize = 5; int start = pageNum * pageSize; int end = Math.min(start + pageSize, allData.size()); List<ImprovedMusic> pageData = start >= allData.size() ? Collections.emptyList() : allData.subList(start, end); // 构造Page对象返回 Page<ImprovedMusic> aPage = new PageImpl<>(pageData, PageRequest.of(pageNum, pageSize), allData.size());
场景2:数据量较大,需要在DynamoDB层面支持排序
需要先调整GSI结构和实体类注解:
- 在DynamoDB端修改
GSISongTitle的结构:新增一个固定值的分区键字段(比如gsi_pk,所有数据该字段值统一设为music),将gsi_pk设为该GSI的Hash Key,SongTitle设为该GSI的Range Key。 - 修正实体类的GSI注解:
// 新增GSI固定分区键字段 @DynamoDBIndexHashKey(attributeName = "gsi_pk", globalSecondaryIndexNames = "GSISongTitle") public String getGsiPk() { return "music"; } // SongTitle仅保留GSI排序键注解 @DynamoDBIndexRangeKey(attributeName = "SongTitle", globalSecondaryIndexNames={"GSISongTitle"}) public String getSongTitle() { return improvedMusicId != null ? improvedMusicId.getSongTitle() : null; }
- 在Repository层新增查询方法,指定使用GSI查询:
@Query(consistentRead = false, indexName = "GSISongTitle") Page<ImprovedMusic> findByGsiPk(String gsiPk, Pageable pageable);
- 调用时传入固定分区键值即可实现DynamoDB层面的排序分页:
Sort sortBy = Sort.by("SongTitle").descending(); Pageable page = PageRequest.of(1,5,sortBy); Page aPage = myRepositor.findByGsiPk("music", page);
注:该方案单分区键最多支持10GB数据,如果你的数据量超过10GB,可将静态分区键拆分为
music_1、music_2等多个值,查询时并行查询所有分区再合并结果即可。
内容的提问来源于stack exchange,提问作者SriniMurthy
相关产品推荐
相关产品推荐

