下面说说java操作cassandra分页。需要注意的是,这个分页和我们平时做的页面分页是不一样的。有什么不同?耐心点往下看。如果一次查询获取的记录数过多,一次性返回,效率很低,很可能造成内存溢出,导致整个应用程序崩溃。因此,驱动程序对结果集进行分页并返回适当的数据页。1.设置fetchsize(设置fetchsize)fetchsize是指一次从cassandra中获取的记录数,换句话说就是每页的记录数;我们可以在创建集群实例时给fetchsize指定一个默认值。如果不指定,默认为5000//Atinitialization:Clustercluster=Cluster.builder().addContactPoint("127.0.0.1").withQueryOptions(newQueryOptions().setFetchSize(2000)).build();//Oratruntime:cluster.getConfiguration().getQueryOptions().setFetchSize(2000);此外,还可以在statementStatementstatement=newSimpleStatement("yourquery");statement.setFetchSize(2000);上设置fetchsize如果语句设置了提取大小,则语句的提取大小将起作用,否则集群上的提取大小将起作用。注意:设置fetchsize并不意味着cassandra总是返回准确的结果集(等于fetchsize),它可能返回比fetchsize略多或少的结果集。2、resultsetiterationfetchsize限制了每页返回的结果集数量。如果你迭代一个页面,驱动会在后台自动获取下一页的记录。举个例子,fetchsize=20:默认情况下,后台自动抓取发生在最后时刻,也就是迭代某页的记录时。如果需要更精细的控制,ResultSet接口提供了以下方法:getAvailableWithoutFetching()和isFullyFetched()检查当前状态;fetchMoreResults()强制获取页面;获取下一页导致的性能下降:ResultSetrs=session.execute("yourquery");for(Rowrow:rs){if(rs.getAvailableWithoutFetching()==100&&!rs.isFullyFetched())rs.fetchMoreResults();//thisisasynchronous//Processtherow...System.out.println(row);}3.保存和复用分页状态有时候,保存分页状态对于以后的恢复非常有用。想象一下:有一个显示结果列表的无状态Web服务,并显示指向下一页的链接,当用户单击此链接时,我们需要执行与之前完全相同的查询,只是迭代应该从哪里开始上一页停止了;相当于记住上一页迭代去了哪里,那么下一页可以从这里开始。为此,驱动程序公开一个PagingState对象,该对象表示获取下一页时我们在结果集中的位置。ResultSetresultSet=session.execute("yourquery");//迭代resultset...PagingStatepagingState=resultSet.getExecutionInfo().getPagingState();//PagingState对象可以序列化成字符串或者字节数组Stringstring=pagingState.toString();byte[]bytes=pagingState.toBytes();PagingState对象序列化后的内容可以持久化存储,也可以作为分页请求的参数,以防后面再次使用,反序列化成一个对象即可:PagingState.fromBytes(byte[]bytes);PagingState.fromString(Stringstr);请注意,分页状态只能与完全相同的语句(相同的查询,相同的参数)重用。此外,它是一个不透明的值,仅用于存储可重用的状态值,如果您尝试修改其内容或在不同的语句上使用它,驱动程序将抛出错误。具体的,我们看代码。下面的例子模拟一个分页请求遍历teacher表中的所有记录:interface:importjava.util.Map;importcom.datastax.driver.core.PagingState;publicinterfaceICassandraPage{Mappage(PagingStatepagingState);}主体代码:importjava.util.ArrayList;importjava.util.HashMap;importjava.util.List;importjava.util.Map;importcom.datastax.driver.core.PagingState;导入com.datastax.driver。core.ResultSet;importcom.datastax.driver.core.Row;importcom.datastax.driver.core.Session;importcom.datastax.driver.core.SimpleStatement;importcom.datastax.driver.core.Statement;importcom.huawei.cassandra。dao.ICassandraPage;importcom.huawei.cassandra.factory.SessionRepository;importcom.huawei.cassandra.model.Teacher;publicclassCassandraPageDaoimplementsICassandraPage{privatestaticfinalSession=SessionRepository.getSession();privatestaticfinalStringCQL_TEACHER_PAGE="select*frommycas.teacherrideMap;";@over对象>页面(PagingStatepagingState){finalintRESULTS_PER_PAGE=2;Mapresult=newHashMap(2);Listteachers=newArrayList(RESULTS_PER_PAGE);Statementst=newSimpleStatement(CQL_TEACHER_PAGE);st.setFetchSize(RESULTS_PER_PAGE);//第一页没有分页状态));//请注意,我们不依赖于RESULTS_PER_PAGE,因为fetchsize并不代表cassandra总是返回准确的结果集//它可能返回的比fetchsize稍微多一点或少一点,另外,我们可能会在最后保留结果集的=rs.getAvailableWithoutFetching();for(Rowrow:rs){Teacherteacher=this.obtainTeacherFromRow(row);teachers.add(teacher);if(--remaining==0){break;}}结果。放(“教师”,教师);returnresult;}privateTeacherobtainTeacherFromRow(Rowrow){Teacherteacher=newTeacher();teacher.setAddress(row.getString(“地址”));teacher.setAge(row.getInt(“年龄”));teacher.setHeight(row.getInt("height"));teacher.setId(row.getInt("id"));teacher.setName(row.getString("name"));returnteacher;}}测试代码:importjava.util.Map;importcom.datastax.driver.core.PagingState;importcom.huawei.cassandra。dao.ICassandraPage;importcom.huawei.cassandra.dao.impl.CassandraPageDao;publicclassPagingTest{publicstaticvoidmain(String[]args){ICassandraPagecassPage=newCassandraPageDao();Mapresult=cassPage.page(null);PagingStatepagingState=(PagingState)result.get("pagingState");System.out.println(result.get("teachers"));while(pagingState!=null){//PagingState对象可以序列化成字符串或者字节数组System.out.println("===============================================");result=cassPage.page(pagingState);pagingState=(PagingState)result.get("pagingState");System.out.println(result.get("teachers"));}}}让我们看看看Statement的setPagingState(pagingState)方法:4.偏移查询保存分页状态,可以保证从某页移动到下一页效果很好(上一页也可以实现),但是不满足随机跳转,比如直接跳转到第10页,因为我们不知道第10页上一页的分页状态cassandra本身不支持像这样需要偏移量查询的功能。原因是offset查询效率低下(性能与跳过的行数成线性反比),所以cassandra官方不鼓励使用offset。如果一定要实现offset查询,我们可以在客户端模拟一下。但是性能还是成线性反比的,也就是说offset越大,性能越低。如果性能在我们可以接受的范围内,还是可以达到的。比如每页显示10行,最多显示20页,也就是说当显示第20页时,最多需要额外抓取190行,但这不会造成太大的性能下降。所以如果数据量不大,还是可以模拟偏移量查询的。例如,假设每页显示10条记录,fetchsize为50,我们请求第12页(即第110到119行):1.第一次执行查询,结果集包含0到49行,我们不需要使用它,我们只需要分页状态;2.利用第一次查询得到的分页状态执行第二次查询;3.利用第二次查询得到的分页状态执行第三次查询。结果集包含100到149行;4、利用第三次查询得到的结果集,先过滤掉前10条记录,然后读取10条记录,最后丢弃剩余的记录。读取的10条记录是Page12需要显示的记录。我们需要尝试找到最佳的fetchsize以达到最佳平衡:太小意味着后台查询更多;太大意味着返回大量信息和更多不必要的行。另外,cassandra本身不支持偏移量查询。在满足性能的前提下,客户端模拟偏移量的实现只是一种折衷。官方建议如下:1.以预期的查询模式测试代码,确保假设正确2.对最高页码设置硬限制,防止恶意用户触发跳过大量行的查询5.Cassandra总结对分页的支持有限,上一页和下一页的实现比较好。不支持偏移量查询。如果要实现的话,可以使用客户端模拟的方式,但是这种场景最好不要用在cassandra上,因为cassandra一般都是用来解决大数据问题,而offset一旦查询数据量过大,表现不会讨人喜欢。在我的项目中,使用cassandra的分页进行索引修复。场景如下:cassandra表没有建立二级索引,使用elasticsearch实现cassandra表的二级索引,会涉及到索引的一致性修复。这里利用cassandra的分页,遍历cassandra中的一张表的整张表,将elasticsearch中的数据一一匹配。如果elasticsearch中不存在,则在elasticsearch中添加。如果存在但不一致,修复时会添加到elasticsearch中。具体elasticsearch是如何实现cassandra的索引功能的,在我后续的博客中会具体说明,这里就不多说了。但是在cassandra表中进行全表遍历时需要进行分页,因为表中的数据量太大,不可能一次将亿级数据全部加载到内存中。