存储与搜索
ES是Elastic Search的缩写,ES是基于Lucene的分布式存储;Lucene提供了全文检索的功能,ES在此之上加入索引分布式的机制,提供了数据分片、数据副本、数据同步等功能,保证了数据的安全性;
ES存储的基本单位是一个Document,可以想象是数据库中的一行,与数据库类似的是,一个Document包含有多个Field,可以是数值类型也可以是字符类型,与数据库不同的是,Document可以随意添加或删除字段,也就是Schema less,另外ES对于需要检索的字段可以声明为需要索引,这样可以很方便就对Document进行检索。
ES具有以下特性:
- 分布式的实时文件存储,每个字段都被索引并可被搜索;
- 分布式的实时分析搜索引擎;
- 可以扩展到上百台服务器,处理PB级结构化或非结构化数据;
关于分片
ElasticSearch推荐的最大JVM堆空间是30~32G, 所以把你的分片最大容量限制为30GB, 然后再对分片数量做合理估算. 例如, 你认为你的数据能达到200GB, 我们推荐你最多分配7到8个分片.分片的大小一般在50GB以内。
分片不是越多越好,需要你预估3-5年的业务量,根据业务量来设置分片
你分配的每个分片都是有额外的成本的:
-
每个分片本质上就是一个Lucene索引, 因此会消耗相应的文件句柄, 内存和CPU资源
-
每个搜索请求会调度到索引的每个分片中. 如果分片分散在不同的节点倒是问题不太. 但当分片开始竞争相同的硬件资源时, 性能便会逐步下降
-
ES使用词频统计来计算相关性. 当然这些统计也会分配到各个分片上. 如果在大量分片上只维护了很少的数据, 则将导致最终的文档相关性较差
关于副本
ES禁止同一个分片的主分片和副本分片在同一个节点。比如单节点ES,不允许有副本分片,有副本分片,则集群不健康。
索引数据的分片规则
shard_num = hash(_routing) % num_primary_shards
1
shard_num: 分片位置,哪个分片。
_routing: id字段或者parent字段。
num_primary_shards: 主分片数量。
可以看得出来,你的文档存在哪个分片跟主分片的数量有关系
集群配置
我使用到的集群 19节点,包含4个gw网关节点
、3个master主节点、12个data数据节点,数据都存储在12个data节点上,分片是12个分片,1个副本
集群版本7.5、6.3
存储类型
{
"settings": {
"index": {
"number_of_shards": "8",
"number_of_replicas": "1"
}
},
"mappings": {
"dynamic": true,
"properties": {
"xxId": {
"type": "long"
},
"xxxJSON": {
"type": "object"
},
"createTime": {
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis",
"type": "date"
},
"xxName": {
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
}
ES支持的数据类型:
1、字符串类型 :
string:
text:
keyword:
从ElasticSearch 5.x开始不再支持string,由text和keyword类型替代;当一个字段是要被全文搜索的,设置text类型,字段内容会被分析,text类型的字段不用于排序,很少用于聚合;keyword类型的字段只能通过精确值搜索到,如果字段需要进行过滤、排序、聚合,设置keyword类型。
2、整数类型:
long
integer
short
byte
在满足需求的情况下,尽可能选择范围小的数据类型,字段的长度越短,索引和搜索的效率越高。
3、浮点类型
double:64位双精度IEEE 754浮点类型
float:32位单精度IEEE 754浮点类型
half_float:16位半精度IEEE 754浮点类型
scaled_float : 缩放类型的的浮点数
对于float、half_float和scaled_float,-0.0和+0.0是不同的值,使用term查询查找-0.0不会匹配+0.0,同样range查询中上边界是-0.0不会匹配+0.0,下边界是+0.0不会匹配-0.0。其中scaled_float,比如价格只需要精确到分,price为57.34的字段缩放因子为100,存起来就是5734 优先考虑使用带缩放因子的scaled_float浮点类型。
4、逻辑类型 :boolean
5、日期类型 :date
日期类型表示格式可以是以下几种:
日期格式的字符串,比如 “2018-01-13” 或 “2018-01-13 12:10:30”
long类型的毫秒数( milliseconds-since-the-epoch,epoch就是指UNIX诞生的UTC时间1970年1月1日0时0分0秒)
integer的秒数(seconds-since-the-epoch)
以上是常用的数据类型,还有些不在罗列,但有一个需要强调下,就是object类型,它内部可以存储的数据就是json数据,搜索时候比如内部存储了id=1,则可以通过
xxxJSON.id = 1 来搜索,对应的java类型也是object:
private Object xxxJSON = new Object();
关于索引
能用直接query则直接query,尽量少使用aggregation来进行数据的查询除非是数据分析,聚合肯定要比直接查慢,遇到特殊的业务需求直接查不出来的可以先用聚合进行bucket分桶,再根据分桶的结果再次查询,不要在分桶的同时进行子聚合获取tophits,这两个和一起很慢,建议分开来做,
主要我这里遇到了spu+sku信息放在一行了,然后业务需要根绝筛选条件既有sku的也有spu的条件来赛选spu,同时展示spu时候一起展示sku列表,这就很尼玛奇特,本来可以分为两个的,先展示spu,点击spu再展示其sku,非尼玛鼠标滑倒时候就展示sku,故做起来性能不咋地,后来考虑到可以分为两部去实现,效果应该会好很多。
size 设置的不能过大,也不能过小,大小要大于(当前页码*每页数量),查询时候会每个节点去查一遍这个数,主节点最后再汇总这些节点的数据,你要是设置的特别大,性能就会很慢
深分页
超过1w条的数据会报错,分页查询单次不能超过10000条。
如果查询数据过大,从ES在每个分片的数据查询量就越大,性能指数级下降。
- From+Size 适合浅分页
- Scroll 适合深分页,并且数据实时要求不高,最适合离线场景,需要一页一页查
- Search After 适合深分页,需要一页一页查
web前端页面展示时候通常会做一个限制,防止深分页展示,比如只展示前1000页,超过1000页不在展示,但是页码会显示总页数
数据导出时候实时性要求不高可以使用Scroll方式导出所有数据
分词
text数据类型存储后会自动分词,默认是英文的分词器,会以空格、逗号等进行自动分词,查询时候使用小写字母+terms就能查到,不建议使用那个模糊搜索的,数据量大了会性能问题。中文分词可以使用ik分词器,我用的ik_max_word中文分词后会很多词组
mapping映射
一般的, mapping 可以分为 动态映射(dynamic mapping) 和 静态(显示) 映射 (explicit mapping) 和精准(严格) 映射(strict mapping)
具体由dynamic 属性控制
"dynamic" : "true", # ES 默认允许添加新的字段
我用的 true,方便后续扩展字段,但是要控制数据存储,不能随便保存无用的字段。
- 动态映射(dynamic:true):动态添加新的字段(或缺省)。
- 静态映射(dynamic:false):忽略新的字段。
- 严格模式(dynamic: strict):如果遇到新的字段,就抛出异常。