#新增索引PUT trade_bid_price_reduction_sku_pool_t1{mappings: {properties: {business_suggest_price: {type: long,index: false},name: {type: text,analyzer: standard,index: true}}}}#查询索引GET trade_bid_price_reduction_sku_pool_t1#删除索引DELETE trade_bid_price_reduction_sku_pool_t1#索引增加字段不能修改旧字段PUT trade_bid_price_reduction_sku_pool_t1/_mapping{properties: {cityCode: {type: long}}}#-------------------------------------------------------------------##插入数据POST /trade_bid_price_reduction_sku_pool_t1/_doc{business_suggest_price:100,name:test 4,cityCode:20}#查询数据GET /trade_bid_price_reduction_sku_pool_t1/_doc/1#删除数据DELETE /trade_bid_price_reduction_sku_pool_t1/_doc/1#修改文档全量修改PUT /trade_bid_price_reduction_sku_pool_t1/_doc/1{business_suggest_price:100,name:test 1}#修改文档局部修改POST /trade_bid_price_reduction_sku_pool_t1/_update/1{doc: {name: test2}}#全文检索(查询 type text index true的数据)GET /trade_bid_price_reduction_sku_pool_t1/_search{query:{match_all: {}}}GET /trade_bid_price_reduction_sku_pool_t1/_search{query:{match: {name:test}}}GET /trade_bid_price_reduction_sku_pool_t1/_search{query: {bool: {should: [{match: {name: test}},{match: {sku: sku123}}]}}}GET /trade_bid_price_reduction_sku_pool_t1/_search{query:{multi_match: {name:test}}}#精确匹配插叙 其他类型 index true的数据GET /trade_bid_price_reduction_sku_pool_t1/_search{query:{term: {cityCode:{value:20}}}}GET /trade_bid_price_reduction_sku_pool_t1/_search{query:{range: {cityCode:{gte:10,lte:20}}}}规范一集群使用命名约定目前出价使用的集群有两个1.1 主集群地址bidding-interface-es.shizhuang-inc.com主要承担对应bidding_seller inventory_sale表索引流量索引命名无强制要求一般为trade_bid_ 或 trade_inv_ 开头bidding集群新建索引需要评估使用场景和影响面1.2 计算集群地址bidding-calculate.elastic.shizhuang-inc.com主要承担数仓回流数据非核心业务表数据的流量索引命名前缀要求trade_calculate_bid_xxxx 如trade_calculate_bid_common_gray主要使用索引前缀匹配命名路由至此集群。目前使用出价es组件ElasticSearch连接组件进行多集群连接和路由路由方式主要是通过索引全名或者索引前缀下面ark中 supportIndexs 代表这个名称的索引会路由到elastic-dev-2.shizhuang-inc.com集群下面ark中 supportPreIndexs 代表这个前缀命名的索引会路由到elastic-dev-2.shizhuang-inc.com集群没有以上配置的走默认集群elastic-dev-1.shizhuang-inc.comes.bidding.index.autotrue es.bidding.index.httpHosts[0].hostelastic-dev-1.shizhuang-inc.com es.bidding.index.httpHosts[0].port-1 es.bidding.index.httpHosts[0].schemahttp es.bidding.index.clients[0].connectNum100 es.bidding.index.clients[0].connectPerRoute100 es.bidding.index.clients[0].userNameelastic es.bidding.index.clients[0].pwd123456 es.bidding.property.showEsSqltrue es.bidding.property.tempIndexst1_trade_es_record_seller_bid es.bidding.index.httpHosts[1].hostelastic-dev-2.shizhuang-inc.com es.bidding.index.httpHosts[1].port-1 es.bidding.index.httpHosts[1].schemahttp es.bidding.index.clients[1].connectNum100 es.bidding.index.clients[1].connectPerRoute100 es.bidding.index.clients[1].userNameelastic es.bidding.index.clients[1].pwd123456 es.bidding.index.clients[1].supportIndexstrade_inventory_sale_table_full_name es.bidding.index.clients[1].supportPreIndexstrade_calculate_bid_二数据模型设计2.1 es mapping规范2.1.1 属性命名类保持与数据库属性名一致采用下划线命名如seller_bidding_no一般不考虑类似commodity.brandId这种嵌套对象命名方式。建议使用 commodity_brand_id这种方式命名2.1.2 属性设置类常用类型对应关系java类型es类型备注Integerinteger数字或金额确定用于范围查询或聚合尽量少的用于等值查询尤其区分度不高的情况下integerkeyword需要排序的数字区分度比较高的用于分组的数字范围查询排序分组聚合使用Integer等值查询使用keywordkeyword一般只用于等值查询不可用于数字类范围查询以及单独的分组聚合longlong数字或金额确定用于范围查询或聚合尽量少的用于等值查询如果是id类可以偶尔使用longkeyword一般是id类如uidspuId。范围查询排序分组聚合使用long等值查询使用keyworddatedate约定的format yyyy-MM-dd HH:mm:ss.SSSSSS||yyyy-MM-dd HH:mm:ss.SSS||yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millisStringkeyword枚举值textkeyword适用于大部分场景text适用于需要模糊搜索的场景缺点是无法通过docValue取值只要不涉及模糊查询全部用keywordBigDecimalscaled_float金额选 scaled_float包含小数的精确值代码如下java为Integer的类型quantity : { type : integer }state : { type : integer, fields : { keyword : { type : keyword } } }数量金额类数字 Integer状态枚举类Integer状态枚举类-百分百确认不用于范围查询和排序的数字state : { type : keyword }java为long的类型quantity : { type : long }uid : { type : long, fields : { keyword : { type : keyword } } }数量金额类数字 longid或者引用id类的long如spu_iduidjava为date的类型modify_time : { type : date, format : yyyy-MM-dd HH:mm:ss.SSSSSS||yyyy-MM-dd HH:mm:ss.SSS||yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis }java为String的类型merchant_type : { type : keyword }spu_name : { type : text, fields : { keyword : { type : keyword } } } //只能模糊查不能通过索引取值 spu_name : { type : text }枚举或者标记类或百分百确认不会模糊查询的文本文本名称类可能存在模糊查询的一些字段2.1.3 索引Mapping设置Mapping类配置mappings : { _meta: { author: dingqi, comments: 出价库存表 }, dynamic:false, _routing : { required : true }, properties : { uid : { type : keyword } } }dynamic 是否动态映射索引要求设置为false或stricttrue默认允许 Elasticsearch 自动创建新字段的映射会给新字段自动创建索引的mappingfalse字段能新增但是自动创建mapping即能正常写入这个字段但是写入后这个字段无法搜索strict禁止自动创建新字段的映射不允许未定义mapping字段插入_meta 类似注释信息。_routingrequired为true时必须要指定路由才能写入建议根据业务特点指定路由写入属性数量不超过100个。2.2 es settings规范index : { --- lifecycle : { --- name : dingqidel_2d --- }, 索引过期策略一般滚动索引使用 number_of_shards : 4, number_of_replicas : 1, refresh_interval : 5S }number_of_shards索引分片数为1 或者 节点的倍数如果是固定不动的索引需要建议提前预估5倍以上数据增量的数据空间。每个分片建议数据量 50g以内。number_of_replicas一般为1即可多了降低写入性能不建议超过3。 重要索引建议 number_of_shards*number_of_replicas1建议等于节点数即每个分片占一个节点。refresh_interval ES写入后刷新到磁盘的时间默认1S配置时间长一些能显著的提升查询效率按业务能接受的延时来可以尽量大点。lifecycle : 索引过期策略一般使用按日期生成的滚动索引需要设置保证定期过期索引释放空间。三 常用类型查询语句使用场景es类型 Integer 或 long型即如下两种类型qty : { type : integer } price : { type : long }适用场景query-range范围查询sort排序aggs-terms分组聚合aggs-sum(求和)aggs-minmax最大最小 aggs-avg平均建议字段金额数量类。不建议字段状态枚举类不适用场景query-termterms等值查询如果非常额外的有查等于的需求但是字段又是Integer时采用下面的写法 大于等于 小于等于 那个值(应急调整用的)如为long型且是id这种区分度极高的字段也可以使用query-termterms但依然建议增加keyword属性。有条件的可以直接重建索引也可以采用增加keyword属性的形式之后等值查询即可。//查询大于等于2且小于等于1的值 只用于紧急优化 range : { trade_status : { gte : 2, lte : 2 } } //即没有keyword类型时应急调整的改法 requestDto.rangeGteAndLteBuilder(trade_status, 2,2); //增加属性后需要重新写入一遍数据可以缓慢进行 trade_status : { type : integer, fields : { keyword : { type : keyword } } } //查询组件使用 requestDto.equalsBuilder(trade_status.keyword, 1);原因是因为es对数字类型且区分度不够大的字段查询优化不够如下图tradeStatus是数字类型直接用等值查询该字段耗时450ms。换成range后可以看出耗时降到了144ms附近。如果再换成 keyword则最终优化至50mses类型keyword即如下一种类型state : { type : keyword } //因查询本身字段就是keyword的类型直接使用即可 requestDto.equalsBuilder(state, 1);适用场景query-termterms等值查询aggs-cardinality基数属性类似sql的 count(distinct(属性)) 建议字段状态枚举类或者确认不需要遍历和排序的id类不建议字段需要范围查询或模糊查询的字段不适用场景query-range范围查询query-wildcard模糊查询sort排序aggs-terms分组聚合看聚合结果数量很少可以使用aggs-sum(求和)aggs-minmax最大最小 aggs-avg平均只使用term查询和terms不要使用range查询和。尤其重点主要是aggs-terms分组聚合时尽量不要使用keyword去分组尤其不要使用区分度高的如id类的keyword去分组聚合如下图使用spuId.keyword进行分组聚合耗时高达6.5s换成spuId ( long ) 后耗时仅需44ms。es类型textspu_title : { type : text } //需要对es分词充分理解该字段直接查询到spu_title分词后结果 requestDto.equalsBuilder(spu_title, nike); //条件本身可以分词查询 requestDto.matchBuilder(spu_title, nike ad);适用场景 matchmatch_phrase 模糊匹配短语匹配建议字段文本类如商品名称不建议字段需要等值查询的字段注意term和match有区别不熟悉的情况尽可能使用matchmatch_phrase避免使用term时出现有时查的出有时查不出的情况(分词的原因)text会被分词默认的标准分词器可以覆盖大部分场景。文本长度超过20个中文字符时需要开始考虑其他分词器如IK等es类型 date即如下类型modify_time : { type : date, format : yyyy-MM-dd HH:mm:ss.SSSSSS||yyyy-MM-dd HH:mm:ss.SSS||yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis } //组件所有range开头查询均支持参数可以是符合date的format的字符串也可以是毫秒 requestDto.rangeGtBuilder(date, 2024-01-01 00:00:00);适用场景query-range范围查询sort排序aggs-minmax最大最小 aggs-avg平均建议字段日期类。不适用场景query-termterms等值查询为避免出现时区gap写入和查询的格式化尽可能使用同一种。es类型 longinteger keywordid : { type : long, fields : { keyword : { type : keyword } } }使用场景同上long和keyword的场景。正常用id使用keyword时就 id.keyword建议字段主键id类或引用id类重点使用时id.keyword去应对适合等值查询的场景id则去解决需要long的场景id类虽然没有范围查询的需求但是通过滚动分页searchafter查询是经常使用到的类似uid之类的引用id也常用于一些分组聚合。如下建议在等值查询时使用spu_id.keyword而terms时用spu_id{ from: 0, size: 0, profile: true, query: { bool: { filter: [{ term: { uid: 7 } }, { term: { status: { value: 1, boost: 1.0 } } },{ terms: { spu_id.keyword: [ 1033310, 1615418 ] } }], adjust_pure_negative: true, boost: 1.0 } }, aggs: { test: { terms: { field: spu_id, size: 10 } } } }es类型 textkeywordspu_title : { type : text, fields : { keyword : { type : keyword } } } //需要对es分词充分理解该字段直接查询到spu_title分词后结果 requestDto.equalsBuilder(spu_title, nike); //条件本身可以分词查询 requestDto.matchBuilder(spu_title, nike ad); requestDto.equalsBuilder(spu_title.keyword, 需要商品全名);使用场景同上text和keyword的场景。正常用brand_name_list使用keyword时就 brand_name_list.keyword建议字段大部分文本建议单独使用text或单独的keyword即可。四基础查询路由 Routing有路由值的数据且查询时有路由字段必须指定路由查询如查询某个用户的所有数据GET trade_inv_db_table_inventory_sale_v2/_search?routing7 { from: 0, size: 1, profile: true, query: { bool: { filter: [{ term: { uid: 7 } } } } //组件使用 requestDto.setRouting(7);无路由值的数据且查询时无路由字段严格控制QPS。因为实际QPS请求QPS*主分片数字段指定按需查询指定的字段es 语句 _source: [sku_id], //出价组件 requestDto.setIncludesxxxxxx组件中对指定字段查询进行了较大优化只能指定带docValue的字段进行查询text字段不支持指定字段查询同样的简单查询指定只查id时10000条数据仅用14ms而全字段查询10000条数据则使用了1047ms。出价es组件ElasticSearch连接组件分页size一般不建议过大默认的常规查询取数不超过100。且尽可能减少深分页使用docvalue取数不超过1w。业务性质的遍历数据考虑使用search_aftersearch_after:[1234]} requestDto.searchAfterBuilder([1234]);排序尽可能减少无意义的排序禁止使用keyword字段作为第一排序如使用id排序可以但是id.keyword排序就要避免了等值查询不论term或者terms查询均使用keyword作为查询条件禁止枚举类型的数字值使用term查询GET trade_inv_db_table_inventory_sale_v2/_search?routing7 { from: 0, size: 1, profile: true, query: { bool: { filter: [{ term: { sale_area.keyword: 1 //反例 sale_area: 1 } } } } //正例 requestDto.equalsBuilder(sale_area.keyword, 1); //反例 requestDto.equalsBuilder(sale_area, 1);脚本查询一般建议在查询时尽可能少的使用脚本脚本只能作为其他较高区分度条件后的辅助条件禁止纯脚本的查询条件禁止使用动态文本参数作为查询脚本文本内容(固定的文本会被缓存)建议针对脚本结果冗余字段如查询 qty*price0可以新增加totalPrice字段保存其值GET trade_inv_db_table_inventory_sale_v2/_search?routing7 { from: 0, size: 1, profile: true, query: { bool: { filter: [{ term: { uid: 7 } },{ script: { source: doc[qty].value * doc[price].value 0, lang: painless } } } } requestDto.scriptBuilder(doc[qty].value * doc[price].value 0) //需要动态参数采用这个接口取编写动态的参数写在params里es会缓存script部分 requestDto.scriptBuilderWithParam(String script, MapString, Object params)五聚合查询详情可以参考ElasticSearch连接组件-聚合查询分组查询aggs-terms分组查询禁止分组条件超过3个建议单个条件分组查询禁止使用较多值的keyword属性作为分组key如 spu_id.keyword分组应改为 long型的 spu_id分组。size建议设置结果集超过1w条时应考虑其他方案。{ spuIdTerm: { terms: { field: spu_id size:1000 } } }aggs-composite分组查询建议分组条件超过 1 时使用。其他使用方式等于aggs-terms的形式多个字段组合禁止使用较多值的keyword属性作为分组key如 spu_id.keyword分组应改为 long型的 spu_id分组。size建议设置结果集超过1w条时应考虑其他方案。{ aggs: { uid_spu_composite: { composite: { sources: [ { uid: { terms: { field: uid } } }, { spu_id: { terms: { field: spu_id } } } ], size: 1000 // 每个请求的最大返回数 } } } }聚合查询sum求和 avg平均, min最小, max 最大使用数字字段时直接使用字段即可使用script查这些函数需要确保函数的source没有变化的参数变化的参数需要以param的形式使用cardinality 类似count(distinct(属性))该方式统计时如果数据量结果较多存在一定误差一般1万以内的数据无需担心误差。该字段使用keyword属性的字段去统计时精准度高于普通数字如统计Spu数量时使用spu_id.keyword会比使用spu属性统计结果更精准数量较大时。scripted_metric 脚本聚合尽可能使用es已提供的函数使用scripted_metric的聚合方式每次都需要专门评估六数据写入业务表数据写入以业务表主键或唯一键作为es的文档id带有明显业务性质的优先使用routing写入。如商家出价使用uid作为routing优先使用DTS平台同步DTS订阅mysql 到es https://middleware.shizhuang-inc.com/env-prod/dts/subscribe/esTaskListNew需要加工宽表的采用订阅dts的binlog形式处理。一般约定采用insert 和update 的api进行数据处理。谨慎使用index api数据全文档覆盖数据量较大时采用bulk api合并所有的更新请求单次合并请求量小于等于1000.日志类数据或数仓计算写入一般不指定id一般采用滚动索引保存数据以时间或者pt来区分数据滚动索引模板七数据限流ElasticSearch连接组件增加了配置ES查询熔断和限流EsRequestDto增加以下两个参数需要ES组件1.0.2-bidding-es-RELEASE 以及以上版本最新1.0.7.1-bidding-es-RELEASEqueryFromBizSentinel 目前要求必传使用默认限流可能会被直接限流在熔断时根据此参数设置熔断方式。ES-indexName-queryFromBizSentinelValueES-索引名称-queryFromBizSentinel参数值在流控平台配置限流的情况es组件默认会给一个单机5QPS的限流。配置后以流控平台配置为准sentinelReturnError 代表熔断时是否返回异常为true时es查询返回异常设置false时es查询不会返回异常只会返回空list。可以ark修改es.bidding.property.sentinerBizThrowError.(queryFromBizSentinel的值)true or false 变化优先取ark里设置的值降级且sentinelReturnErrorfalse时执行可以用于es降级至mysql查询或执行其他内容。list泛型需要与EsQueryResult的泛型一致。EsRequestDto {/*** 限流配置*/private String queryFromBizSentinel DEFAULT_BIZ_FROM;/*** 发生限流时是否返回异常, true:返回异常, false:返回空集合,默认true*/private Boolean sentinelReturnError;/*** 降级且sentinelReturnErrorfalse时执行可以用于es降级至mysql查询* 返回参数list泛型类型需要与es查询返回一致,不设置则返回空list*/private SupplierList sentinelReturnSupplier; //可以通过sentinelReturnSupplier设置被降级或熔断后从哪查 requestDto.setSentinelReturnSupplier(()-{ ListDTO list queryFromDb(); return list; });配置方式示例提前配置熔断和预估QPS 并配置限流比如有些业务场景查询复杂可能50以内QPS无问题但是超过100QPS就会造成es cpu急剧上升注意使用规范良好的属性使用能提供更高的性能支持比如点查询优先使用keyword类型的字段压测上线后持续观察es错误率和RT以及QPS情况默认单机QPS5存在部分循环查的任务突破单机限制但是总QPS不高的情况需使用方根据实际情况修改滚动索引模板路由至集群2的索引前缀trade_calculate_bid_生命周期管理dingqidel_2d 两天删除index_new_spu_pending_bidding_policy_7d 7天删除PUT _template/trade_calculate_bid_guide_auto_follow_window_pop_template { order : 0, index_patterns : [ --- trade_calculate_bid_ 索引前缀要求这个 trade_calculate_bid_guide_auto_follow_window_pop_* ], settings : { index : { --- lifecycle : { --- name : dingqidel_2d --- },这个按需填写index_new_spu_pending_bidding_policy_7d和dingqidel_2d number_of_shards : 1, number_of_replicas : 1, refresh_interval : 30S } }, mappings : { _meta: { author: dingqi, comments: 数仓回流任务xxxx }, properties : { uid : { type : keyword }, sale_inventory_no : { type : keyword }, seller_bidding_no : { type : keyword }, logo_url : { type : keyword }, last_show_time : { type : keyword }, property : { type : keyword }, data_type : { type : keyword }, sku_id : { type : keyword }, bid_type : { type : keyword }, spu_id : { type : keyword }, title : { type : keyword } } } }dataworks回流模板{ transform: false, type: job, version: 2.0, steps: [ { stepType: odps, parameter: { partition: [ pt${datehours} ---按对应数仓pt ], datasource: odps_first, envType: 1, successOnNoPartition: false, tunnelQuota: default, isSupportThreeModel: false, column: [ gray_biz_id, ---数仓列名 gray_biz_type ], tableComment: , enableWhere: false, table: trade_calculate_bid_common_gray -数仓表名 }, name: Reader, category: reader }, { stepType: elasticsearch, parameter: { enablePartition: true, esPartitionColumn: [ { name: gray_biz_id, ---es routing的字段 type: keyword } ], column: [ { name: gray_biz_id, es字段 type: keyword }, { name: gray_biz_type, type: keyword } ], index: trade_calculate_bid_common_gray_${datehours}, ---动态的es索引名称 aliasMode: exclusive, splitter: ,, timeout: 600000, actionType: index, indexType: , cleanup: false, datasource: bidding_calculate, envType: 1, discovery: false, alias: trade_calculate_bid_common_gray, ----索引对应的别名 primaryKeyInfo: { fieldDelimiterOrigin: ,, column: [ gray_biz_id, gray_biz_type --联合主键信息 ], type: specific, fieldDelimiter: , }, dynamic: true, batchSize: 1024 }, name: Writer, category: writer }, { copies: 1, parameter: { nodes: [], edges: [], groups: [], version: 2.0 }, name: Processor, category: processor } ], setting: { errorLimit: { record: 0 }, locale: zh_CN, speed: { throttle: true, concurrent: 2, mbps: 2 } }, order: { hops: [ { from: Reader, to: Writer } ] } }