一步当然可以(query and fetch),但是因为一般来说用户不需要一次性查出所有数据,只会查出score为前几的数据。而各shard直接互相不知道对方持有的文档的score情况,所以一次查询只能从所有shard上查出所有的文档,再进行排序后返回给用户。就算要做优化,每个shard先对score做排序再返回limit数量的文档,那最终也大概率会超过limit,从而浪费许多IO。
而先fetch取出id和score进行排序,再按需取回具体的文档,理论上可以减少部分IO(实际上不一定),所以也算是一种比较自然的做法。
两种方式都有缺点,存在很大的优化空间,不过我对es不怎么了解,所以回答也仅限于此。
再回答下题主的其他问题。
这种通过定位主键来定位数据的索引叫次级索引,和聚簇索引并不是强相关的。
索引是一种逻辑空间到物理空间的映射(也可以是逻辑空间到逻辑空间的映射),B+树和倒排索引在这点上是一致的,区别只是两者特征不一样导致使用场景不同。B+树可以低成本维持有序性,并提供高性能的Scan;倒排索引提供文本检索的能力,但无法提供有序性。
使用聚簇索引的系统中,主键以外的索引都是次级索引,即指向主键的索引,而非聚簇索引则是直接指向物理位置。虽然这种方式会引入题主说的“查两次”的问题,但实际上很多场景中聚簇索引的表现反而更好(比如MVCC)。避免“查两次”能得到局部的更优解,但是从全局来看可能并不是最好的选择。
所以说优化是最难做的,按下葫芦浮起瓢,再常见不过了……