一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - java實現cassandra高級操作之分頁實例(有項目具體需求)

java實現cassandra高級操作之分頁實例(有項目具體需求)

2020-09-04 15:25youzhibing2904 Java教程

這篇文章主要介紹了java實現cassandra高級操作之分頁實例(有項目具體需求),具有一定的參考價值,感興趣的小伙伴們可以參考一下。

上篇博客講到了cassandra分頁,相信大家會有所注意:下一次的查詢依賴上一次的查詢(上一次查詢的最后一條記錄的全部主鍵),不像mysql那樣靈活,所以只能實現上一頁、下一頁這樣的功能,不能實現第多少頁那樣的功能(硬要實現的話性能就太低了)。

我們先看看驅動官方給的分頁做法

如果一個查詢得到的記錄數太大,一次性返回回來,那么效率非常低,并且很有可能造成內存溢出,使得整個應用都奔潰。所以了,驅動對結果集進行了分頁,并返回適當的某一頁的數據。

一、設置抓取大小(Setting the fetch size)

抓取大小指的是一次從cassandra獲取到的記錄數,換句話說,就是每一頁的記錄數;我們能夠在創建cluster實例的時候給它的fetch size指定一個默認值,如果沒有指定,那么默認是5000

java" id="highlighter_666814">
?
1
2
3
4
5
6
7
8
// At initialization:
Cluster cluster = Cluster.builder()
  .addContactPoint("127.0.0.1")
  .withQueryOptions(new QueryOptions().setFetchSize(2000))
  .build();
 
// Or at runtime:
cluster.getConfiguration().getQueryOptions().setFetchSize(2000);

另外,statement上也能設置fetch size

?
1
2
Statement statement = new SimpleStatement("your query");
statement.setFetchSize(2000);

如果statement上設置了fetch size,那么statement的fetch size將起作用,否則則是cluster上的fetch size起作用。

注意:設置了fetch size并不意味著cassandra總是返回準確的結果集(等于fetch size),它可能返回比fetch size稍微多一點或者少一點的結果集。

二、結果集迭代

fetch size限制了每一頁返回的結果集的數量,如果你迭代某一頁,驅動會在后臺自動的抓取下一頁的記錄。如下例,fetch size = 20:

java實現cassandra高級操作之分頁實例(有項目具體需求)

默認情況下,后臺自動抓取發生在最后一刻,也就是當某一頁的記錄被迭代完的時候。如果需要更好的控制,ResultSet接口提供了以下方法:

getAvailableWithoutFetching() and isFullyFetched() to check the current state;

fetchMoreResults() to force a page fetch;

以下是如何使用這些方法提前預取下一頁,以避免在某一頁迭代完后才抓取下一頁造成的性能下降:

?
1
2
3
4
5
6
7
ResultSet rs = session.execute("your query");
for (Row row : rs) {
  if (rs.getAvailableWithoutFetching() == 100 && !rs.isFullyFetched())
    rs.fetchMoreResults(); // this is asynchronous
  // Process the row ...
  System.out.println(row);
}

三、保存并重新使用分頁狀態

有時候,將分頁狀態保存起來,對以后的恢復是非常有用的,想象一下:有一個無狀態Web服務,顯示結果列表,并顯示下一頁的鏈接,當用戶點擊這個鏈接的時候,我們需要執行與之前完全相同的查詢,除了迭代應該從上一頁停止的位置開始;相當于記住了上一頁迭代到了哪了,那么下一頁從這里開始即可。

為此,驅動程序會暴露一個PagingState對象,該對象表示下一頁被提取時我們在結果集中的位置。

?
1
2
3
4
5
6
7
ResultSet resultSet = session.execute("your query");
// iterate the result set...
PagingState pagingState = resultSet.getExecutionInfo().getPagingState();
 
// PagingState對象可以被序列化成字符串或字節數組
String string = pagingState.toString();
byte[] bytes = pagingState.toBytes();

PagingState對象被序列化后的內容可以持久化存儲起來,也可用作分頁請求的參數,以備后續再次被利用,反序列化成對象即可:

?
1
2
PagingState.fromBytes(byte[] bytes);
PagingState.fromString(String str);

請注意,分頁狀態只能使用完全相同的語句重復使用(相同的查詢,相同的參數)。而且,它是一個不透明的值,只是用來存儲一個可以被重新使用的狀態值,如果嘗試修改其內容或將其使用在不同的語句上,驅動程序會拋出錯誤。

具體我們來看下代碼,下例是模擬頁面分頁的請求,實現遍歷teacher表中的全部記錄:

接口:

?
1
2
3
4
5
6
7
8
9
import java.util.Map;
 
import com.datastax.driver.core.PagingState;
 
public interface ICassandraPage
{
  Map<String, Object> page(PagingState pagingState);
 
}

主體代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import com.datastax.driver.core.PagingState;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.SimpleStatement;
import com.datastax.driver.core.Statement;
import com.huawei.cassandra.dao.ICassandraPage;
import com.huawei.cassandra.factory.SessionRepository;
import com.huawei.cassandra.model.Teacher;
 
public class CassandraPageDao implements ICassandraPage
{
  private static final Session session = SessionRepository.getSession();
  
  private static final String CQL_TEACHER_PAGE = "select * from mycas.teacher;";
  
  @Override
  public Map<String, Object> page(PagingState pagingState)
  {
    final int RESULTS_PER_PAGE = 2;
    Map<String, Object> result = new HashMap<String, Object>(2);
    List<Teacher> teachers = new ArrayList<Teacher>(RESULTS_PER_PAGE);
 
    Statement st = new SimpleStatement(CQL_TEACHER_PAGE);
    st.setFetchSize(RESULTS_PER_PAGE);
    
    // 第一頁沒有分頁狀態
    if (pagingState != null)
    {     
      st.setPagingState(pagingState);
    }
    
    ResultSet rs = session.execute(st);
    result.put("pagingState", rs.getExecutionInfo().getPagingState());
    
    //請注意,我們不依賴RESULTS_PER_PAGE,因為fetch size并不意味著cassandra總是返回準確的結果集
    //它可能返回比fetch size稍微多一點或者少一點,另外,我們可能在結果集的結尾
    int remaining = rs.getAvailableWithoutFetching();
    for (Row row : rs)
    {
      Teacher teacher = this.obtainTeacherFromRow(row);
      teachers.add(teacher);
      
      if (--remaining == 0)
      {
        break;
      }
    }
    result.put("teachers", teachers);
    return result;
  }
 
  private Teacher obtainTeacherFromRow(Row row)
  {
    Teacher teacher = new Teacher();
    teacher.setAddress(row.getString("address"));
    teacher.setAge(row.getInt("age"));
    teacher.setHeight(row.getInt("height"));
    teacher.setId(row.getInt("id"));
    teacher.setName(row.getString("name"));
    
    return teacher;
  }
 
}

測試代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.util.Map;
 
import com.datastax.driver.core.PagingState;
import com.huawei.cassandra.dao.ICassandraPage;
import com.huawei.cassandra.dao.impl.CassandraPageDao;
 
public class PagingTest
{
  
  public static void main(String[] args)
  {
    ICassandraPage cassPage = new CassandraPageDao();
    Map<String, Object> result = cassPage.page(null);
    PagingState pagingState = (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)方法:

java實現cassandra高級操作之分頁實例(有項目具體需求)

四、偏移查詢

保存分頁狀態,能夠保證從某一頁移動到下一頁很好地運行(也可以實現上一頁),但是它不滿足隨機跳躍,比如直接跳到第10頁,因為我們不知道第10頁的前一頁的分頁狀態。像這樣需要偏移查詢的特點,并不被cassandra原生支持,理由是偏移查詢效率低下(性能與跳過的行數呈線性反比),所以cassandra官方不鼓勵使用偏移量。如果非要實現偏移查詢,我們可以在客戶端模擬實現。但是性能還是呈線性反比,也就說偏移量越大,性能越低,如果性能在我們的接受范圍內,那還是可以實現的。例如,每一頁顯示10行,最多顯示20頁,這就意味著,當顯示第20頁的時候,最多需要額外的多抓取190行,但這也不會對性能造成太大的降低,所以數據量不大的話,模擬實現偏移查詢還是可以的。

舉個例子,假設每頁顯示10條記錄,fetch size 是50,我們請求第12頁(也就是第110行到第119行):

1、第一次執行查詢,結果集包含0到49行,我們不需要用到它,只需要分頁狀態;

2、用第一次查詢得到的分頁狀態,執行第二次查詢;

3、用第二次查詢得到的分頁狀態,執行第三次查詢。結果集包含100到149行;

4、用第三次查詢得到的結果集,先過濾掉前10條記錄,然后讀取10條記錄,最后丟棄剩下的記錄,讀取的10條記錄則是第12頁需要顯示的記錄。

我們需要嘗試著找到最佳的fetch size來達到最佳平衡:太小就意味著后臺更多的查詢;太大則意味著返回了更大的信息量以及更多不需要的行。

另外,cassandra本身不支持偏移量查詢。在滿足性能的前提下,客戶端模擬偏移量的實現只是一種妥協。官方建議如下:

1、使用預期的查詢模式來測試代碼,以確保假設是正確的

2、設置最高頁碼的硬限制,以防止惡意用戶觸發跳過大量行的查詢

五、總結

Cassandra對分頁的支持有限,上一頁、下一頁比較好實現。不支持偏移量的查詢,硬要實現的話,可以采用客戶端模擬的方式,但是這種場景最好不要用在cassandra上,因為cassandra一般而言是用來解決大數據問題,而偏移量查詢一旦數據量太大,性能就不敢恭維了。

在我的項目中,索引修復用到了cassandra的分頁,場景如下:cassandra的表不建二級索引,用elasticsearch實現cassandra表的二級索引,那么就會涉及到索引的一致性修復的問題,這里就用到了cassandra的分頁,對cassandra的某張表進行全表遍歷,逐條與elasticsearch中的數據進行匹對,若elasticsearch中不存在,則在elasticsearch中新增,若存在而又不一致,則在elasticsearch中修復。具體elasticsearch怎么樣實現cassandra的索引功能,在我后續博客中會專門的講解,這里就不多說了。而在cassandra表進行全表遍歷的時候就需要用到分頁,因為表中數據量太大,億級別的數據不可能一次全部加載到內存中。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:http://www.cnblogs.com/youzhibing/p/6653129.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 果冻传媒林予曦图片 | 青青青青在线视频 | 情缘免费观看完整版 | 精品一区二区三区波多野结衣 | 国产精品视频在线观看 | 国产成人手机在线 | 精品国产麻豆免费人成网站 | jizz农村野外jizz农民 | 奇米影视在线视频8888 | 欧美日韩视频在线一区二区 | 欧美在线一二三区 | 大桥未久midd—962在线 | 嗯啊视频在线观看 | 日韩免费 | 4455四色永久免费 | 九九精品免费视频 | 成人国产网站v片免费观看 成人国产精品视频 | 亚洲精品久久碰 | 日本视频观看 | 国产精品原创永久在线观看 | 国产在线视频一区二区三区 | 亚洲婷婷在线视频 | 色伦网 | 青青视频国产依人在线 | 国产精品免费久久久久影院小说 | 成人久久18免费网站 | 日本五十路六十30人8时间 | 久久伊人在 | 九九精品视频在线观看 | xxx美国| 啪啪大幂幂被c | 久久一er精这里有精品 | 好湿好紧好大野战 | 午夜神器18以下不能进免费 | 久久毛片基地 | 99热都是精品 | 放荡女小洁的性日记 | 99精品国产高清自在线看超 | 黄色aaa级片 | 秋葵视频成人 | 国产精品亚洲专区在线播放 |