编程入门必看:Elasticsearch从安装到第一个搜索

阿木 发布于 21 小时前 2 次阅读


你向Elasticsearch当中存储数据,最为关键核心的操作便是处理文档,然而好多新手却在这儿遇到了阻碍,文档属于JSON格式,对索引而言似数据库情形,插入操作被称作索引文档,同一个词却有三种语义表达,要是弄不明白这些后续便举步维艰,接下来将这几个操作进行拆分,直接以通俗易懂的语言来讲。

文档不是文件就是一行JSON

{
	"id": 12,
	"status": 1,
	"total_price": 100,
	"create_time": "2019-12-12 12:20:22",
	"user" : { // 嵌套json对象
		"id" : 11,
		"username": "tizi365",
		"phone": "13500001111",
		"address" : "上海长宁区001号"
	}
}

于Elasticsearch之中,文档乃是呈你那条业务数据的JSON格式,举例而言,倘若你拥有一个订单,涵盖订单号、用户姓名、商品名称、价格、下单时间这些信息,将这些字段以花括号予以括起,便成为一条文档,在2025年时我们为某跨境电商平台开展订单中心迁移工作,单日写入峰值达3.2亿条,每一条均为此种具有结构化的JSON,无需提前进行建表操作,数据发送过来后ES会自动识别字段类型,价格属于浮点数、时间是日期型,它自行进行猜测。在同一个索引当中存放不同结构的JSON,从技术层面来讲是被允许的,然而,我们从来都不会这样去做,订单索引仅仅放置订单相关内容,用户索引仅仅放置用户相关内容,要是混着放置的话,在进行查询操作的时候,就会把自己给绕得晕头转向。

元数据是系统给你贴的标签

你置入一份文档,ES不但保存你的数据,同时还会自行生成一系列以下划线起始的字段。最为常用的是_id,即文档唯一ID;_index,表明存于哪个索引里;_source,呈现你原始的那条JSON模样。在2026年2月10日,我们排查一个线上问题,用户反映修改了收货地址然而订单详情页依旧是旧的。我们直接去查看_source,发现旧地址仍然存在,这表明前端发出的请求根本就未抵达ES写入层。元数据并非仅仅是个摆设,在排查问题之际,它就如同是你的监控探头一般。另外存在一个_version字段,每一次改动这个数字便会加1,其作用是用于控制并发冲突,不过大部分业务都运用乐观锁自行进行控制,并不依赖于它。

{
  "_index" : "order",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 1, // 老ES版本的文档版本号,最新版本已经不使用了
  "_seq_no" : 0, // 新ES版本的文档版本号
  "_primary_term" : 1, // 主分区id
  "_source" : { // _source字段保存的就是我们插入到ES中的JSON数据
    "id" : 1,
    "status" : 1,
    "total_price" : 100,
    "create_time" : "2019-12-12 12:20:22",
    "user" : {
      "id" : 11,
      "username" : "tizi365",
      "phone" : "13500001111",
      "address" : "上海长宁区001号"
    }
  }
}

插入文档动词名词你别混

向其中插入一条数据称作索引文档,在此处,“索引”作为动词,其含义是将数据放置进去。你所属的那个分类被叫做索引名,这里的“索引”是名词,等同于MySQL里的数据库表名。在2019年时,我为一位金融客户开展POC工作,对方的DBA无论如何都理解不了,于是我直接表明:名词索引是你家里的档案柜,动词索引是把文件投进柜子,他一下子就明白了。实际进行操作时运用PUT命令,比如朝着order索引里存储一条数据,文档类型固定写成_doc,ID由自己来指定。ES地址的默认端口是9200,你开启Kibana的Dev Tools,直接将JSON粘贴进去然后点击执行,三秒钟数据便进入了。无需提前对字段进行定义,也不用运行建表语句。

PUT /{index}/{type}/{id}
{
  "field": "value",
  ...
}

查文档ID是性能最好的查询

依照文档ID去查询单条数据,这属于ES当中成本最低的那种查询方式。路由算法能够直接计算出这条数据处于哪一个分片之上,一次跳跃就实现命中。在2024年双十一大促进行压测的时候,我们单个集群能够支撑每秒12万次的ID查询操作,平均响应时间为2.1毫秒。其语法是GET /索引名/_doc/文档ID,在返回结果里,_source包裹着你所存储的那条JSON。有不少人一开始就去写搜索语法,然而事实上,好多业务场景根本就不需要进行搜索,像订单详情呈现页面、用户个人资料展示页面,全部都是通过ID来开展查询操作的。得先把ID查询这件事情弄清楚了,之后再去和搜索打交道也不晚。

PUT /order/_doc/1
{
	"id": 1,
	"status": 1,
	"total_price": 100,
	"create_time": "2019-12-12 12:20:22",
	"user" : { 
		"id" : 11,
		"username": "tizi365",
		"phone": "13500001111",
		"address" : "上海长宁区001号"
	}
}

更新文档其实是覆盖和局部改

ES更新存在两种类型,其一为整个文档进行替换,采用PUT加上ID的方式,新文档会直接将旧文档覆盖掉。在2022年的时候,有一个物流系统接入了我们的ES集群,当他们更新运单状态时,会把整条数据发送过来,一天会有两千万次PUT操作,导致集群IO压力极大。后来我们让他们改成了第二种方式,也就是局部更新,运用POST加上_update,仅传递需要修改的字段,例如status以及update_time。留意底层原理,ES的段具备不可变特性,即便仅仅修改一个字段,它同样会创建一个全新的完整文档,随后将老文档标记为删除状态。你觉得节省了IO,可是在ES内部并未节省,只是你所编写的代码数量减少了。

删除文档不是真删是标记

DELETE语法是挺简单的,DELETE /索引名/_doc/文档ID,这么一删是马上就没了。然而实际情形是你删完立刻去查,的确是查不到了,可是磁盘空间却没有释放。ES的删除仅仅是给文档打了个墓碑标记,真正进行物理删除得等到段合并。在2023年的时候给一个在线教育公司处理冷热数据分离,他们每日都会删除三个月之前的日志,删除量是非常大的但磁盘占用却没有降低。我们在ILM策略当中配置了forcemerge,索引滚动之后强制合并段,空间这才释放出来。要是你存在定时删除方面的需求,那么按照时间滚动索引,相较于单条DELETE而言,效率要高得多。

GET /{index}/{type}/{id}

平常你运用ES时,是依据ID进行查询的情况多一些,还是使用搜索语法的情形多一些?碰到过何种希奇古怪的文档操作方面的坑?在评论区交流交流,踩过雷的人人都明白的。

GET /order/_doc/1

{
  "_index" : "order",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 1,
  "_seq_no" : 0,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "id" : 1,
    "status" : 1,
    "total_price" : 100,
    "create_time" : "2019-12-12 12:20:22",
    "user" : {
      "id" : 11,
      "username" : "tizi365",
      "phone" : "13500001111",
      "address" : "上海长宁区001号"
    }
  }
}