一個ES索引最大可以支持多少個shard?理論上無限擴展,我推測最大應該是java array的最大長度:Integer.MAX_VALUE。通常業務為了保證查詢效率,往往會限制data node上shards的總個數(cluster.routing.allocation.total_shards_per_node)或者某個索引的shards個數(index.routing.allocation.total_shards_per_node)。
一個ES shard(lucene index)最多可以索引2,147,483,519個document。https://issues.apache.org/jira/browse/LUCENE-5843
一個ES mapping默認最多可以有1000個字段(index.mapping.total_fields.limit),因為ES DSL默認最多支持1000個search條件表達式(rewrite之后)。nest對象默認最多50個字段(index.mapping.nested_fields.limit),另外最多支持10000個nest對象(index.mapping.nested_objects.limit)。
一個ES keyword字段支持的最大keyword長度是32766個byte(這個是hard code的為ByteBlockPool的BYTE_BLOCK_SIZE-2),如果keyword字段存的是ES的Array, 則變為Array中的每term最大32766個byte,Array的最大size不受約束,但Array最大存儲量是所有keyword總和為2147483647,可參考Lucene8.0.0的BytesRefHash的類注釋。
這就引出來一個有趣的問題,ES type=keyword的字段存Array和單個keyword的區別在哪里?
ES本身並不做底層的索引和存儲,lucene承擔了這部分工作,通過lucene構造一個帶有Array字段的document如下:
public static void indexDocument(IndexWriter writer) throws IOException {
List<String> cityNameList = new ArrayList();
cityNameList.add("BeiJing");
cityNameList.add("ShangHai");
cityNameList.add("HangZhou");
Document doc = new Document();
for(int i = 0; i<cityNameList.size();i++) {
doc.add(new StringField("cityName", new BytesRef(cityNameList.get(i)), Field.Store.YES));
}
long writerResult = writer.addDocument(doc);
System.out.println(writerResult);
}
可以看到一個document創建多個StringField,每個StringField都采用同樣的fieldname: cityName。通過debug addDocument方法,看到lucene會for循環所有的StringField並根據fieldName進行索引,相同fieldName會歸為同一個PerField處理,最終每個BytesRef都會都會存到同一個BytesRefHash結構中,從而完成了數組(其實存的HashMap)的存儲。同時不難推斷,針對Array的term query耗時應該是尋址Hash碰撞鏈與Hash碰撞鏈上遍歷到具體內容的時間之和,尋址hash鏈是O(1),hash鏈遍歷是O(n),n是hash碰撞鏈的長度。這個長度又跟hash取模和Array size有關,lucene8.0.0默認的hash模是15,terms個數來自索引數據,Array size越大查詢越耗時,好在不是線性增長而是log15n,size非常大時term query耗時並不會增長很多。
Reference:
https://www.amazingkoala.com.cn/Lucene/gongjulei/2019/0218/32.html