通常, 我们的应用系统, 如果要做一次全量数据的读取, 大多数时候, 采用的方式会是使用分页读取的方式, 然而
分页读取的方式, 在大数据量的情况下, 在 solr 里面表现并不是特别好, 因为它随时可能会发生 OOM 的异常, 在 solr 里面
通过 rows 和 start 参数, 非常方便分页读取, 但是如果你的 start=1000000 rows=10, 那么 solr 里面会将前面 100 万元数据的索引信息读取在内存里面, 这样以来, 非常耗内存, 所以在 solr 里面, 分页并不适合深度分页.
深度分页在 solr 里面, 更推荐使用游标的方式, 游标是无状态的, 不会维护索引数据在内存里面, 仅仅记录最后一个 doc 的计算值类似 md5, 然后每一次读取, 都会如此记录最后一个值的 mark, 下一次通过这个 mark 便能快速的定位到第二页上, 如此往复, 便能完成整个数据的读取. 而且耗费内存非常少.
假如现在有排好队的 10 个人等待买饭, 而一个房间里面最多一次只能进 2 个人, 那么我们就可以将这个 2 个人, 编号顺序, 1 和 2, 他们打完饭后, 让 2 号的人通知, 下一组 2 个人, 进来打饭, 如此往复
所有人都能吃到饭, 这就类似 solr 中游标的使用.
使用游标的方式读取数据, 也有一些约束或者缺点:
(1) 查询条件里面必须有 cursorMark 参数, 而且必须不能有 start 参数
(2) 查询的条件里必须按照主键排序 (升序或降序), 如果没有这个条件, 主键重复, 那么会造成多个游标的 mark 值, 这样以来下一次请求就不知道如何定位了, 而且有可能出现重复读数据的情况
(3) 如果一个分页的系统, 按照指定页码跳转的功能, 这样实现的功能是实现不了的, 因为游标一旦读取了, 就不能再返回上一次的位置了, 这种业务最好使用 start+rows 搞定.
solrj 实现代码例子:
- // 游标查询
- public static void cursorQuery()throws Exception{
- //http solr 服务
- HttpSolrClient sc=new HttpSolrClient("http://localhost:8983/solr/one");
- //solr 查询封装
- SolrQuery sq =new SolrQuery();
- sq.setRows(2);// 设置游标一次读的数量
- sq.set("q", "*:*");// 按条件检索
- sq.setSort("id", ORDER.asc);// 根据主键排序
- String cursorMark = CursorMarkParams.CURSOR_MARK_START;// 游标初始化
- boolean done = false;
- while (!done) {
- sq.set(CursorMarkParams.CURSOR_MARK_PARAM, cursorMark);// 变化游标条件
- QueryResponse rsp = sc.query(sq);// 执行多次查询读取
- String nextCursorMark = rsp.getNextCursorMark();// 获取下次游标
- // 做一些操作数据的事
- for(SolrDocument sd:rsp.getResults()){
- System.out.println(sd.get("id"));
- }
- // 如果两次游标一样, 说明数据拉取完毕, 可以结束循环了
- if (cursorMark.equals(nextCursorMark)) {
- done = true;
- }
- cursorMark = nextCursorMark;
- }
- // 关闭连接
- sc.close();
- }
来源: http://www.bubuko.com/infodetail-2736225.html