巧用双索引避免es出现索引不存在的问题

问题说明:

企业级门户APP,有移动端组织架构,员工可以在app端查询公司用户信息,支持员工在通讯录中多字段搜索(姓名/工号/手机号/邮箱/......),模糊搜索。我们这边是使用elasticsearch来存储员工信息,

以便于实现这种搜索功能。

 

 

 

 

正常把员工信息导入到es中,搜索也不会有问题。但是我们的产品是个企业级产品,专门服务于不同的企业客户的,解决客户各式各样的需求。比如,正常员工的字段有姓名/工号手机号/邮箱/性别等,这也是我们产品中标准的字段,无法满足客户的个性化需求,例如客户需求:

  • 客户A:我们有职级字段,怎么办
  • 客户B:我们有昵称字段,怎么办
  • 客户C:我们有爱好字段,怎么办
  • ...........

如上所述,不同企业有不同的字段,为了满足客户可以自定义属于他们自己的字段,我们设计了个性化字段的功能。

 个性化字段:企业可以在自己的租户下自定义此租户下员工字段,增加代码的灵活性(这个设计以后有机会在写一篇)
 

在个性化字段中,可以控制某些字段是否能够在es中搜索,要支持es中搜索,那么就需要把字段初始化在es的索引中,当添加字段的时候就会到对应索引中增加字段。为了防止意外,我们提供了web端操作的界面,管理员可以手动重置整个租户的索引以及数据。在重建重新初始化期间,如果恰好用户正在使用搜索功能,可能会出现索引不存在或者员工搜索不到数据等问题。

本文就是来说明下在es初始化期间怎么保证数据能够正常访问,方案:以空间换时间,引入双索引的来处理

思路来源:在看《redis设计与实现》一书的时候,其中说明redis对字典的哈希表执行rehash的时候就是有两个hash表交换处理的,分别是ht[0]和ht[1]

 

双索引设计

系统中默认两个索引index0和index1,如果正在提供服务的索引是index0 ,那么在下次初始化重建的时候就会初始化index1, 待初始化完成后则index1对外提供服务,删除索引index0。如此反复循环。

1. 名词解释

下面单独是说明0租户下的索引设计,多租户下区分各个redis key和es索引即可

  • elasticsearch中有两个索引,一个index0 (员工索引0,默认),另一个index1(员工索引1)

  • ajisun:elastic:employee:init0(有过期时间的单key,判断此租户是否正在初始化中,)

  • ajisun:elastic:employee,0(hash key,存储0租户正在使用的索引)

 

2. 索引重建步骤:
  1. 通过 redis keyajisun:elastic:employee:init0 来判断当前租户有没有正在初始化中,如果有,则忽略此次操作,如果没有初始化,请看第2步。

  2. 通过redis hash key ajisun:elastic:employee,{tenantId} 获取当前租户使用的es索引=oldIndex

  3. 如果oldIndex为空,就在redis中设置租户默认索引redis.hshPut("ajisun:elastic:employee", 0, "index0"), 那么即将初始化重建的新索引newIndex是 “index1”

  4. 如果oldIndex不为空,那么当前正在提供服务的索引是oldIndex,即将要初始化重建的新索引newIndex是非oldIndex(如果oldIndex是index0,那非oldIndex是index1,反之一样)

  5. 然后设置此租户索引正在初始化的key redis.strSet("ajisun:elastic:employee:init0", newIndex, 5L, TimeUnit.MINUTES); 加过期时间防止应用奔溃,导致状态是一直是初始化中

  6. 初始化新的索引newIndex,添加数据.

  7. 删除初始化状态的redis key,redis.delKey("ajisun:elastic:employee:init0");

  8. 改变redis中租户默认索引为newIndex, redis.hshPut("ajisun:elastic:employee",0, newIndex);

  9. 删除elasticsearch中的旧索引oldIndex。

 

流程图如下:


 


3. 数据搜索

搜索数据的就比较简单,直接在redis.hshGet("ajisun:elastic:employee",0) 中查找到0租户目前正在使用索引,如果为空则使用默认的索引index0搜索。

 

4. 数据修改/存储

主要就是如果索引在初始化中,则直接把数据的修改/存储 操作新的索引上

  1. redis.hshGet("ajisun:elastic:employee",0) 中获取0租户正在使用的索引。

  2. 如果为空默认索引是index0

  3. 如果索引不为空,则获取redis缓存中redis.strGet("ajisun:elastic:employee:init0") 0租户是否为空

  4. 如果不为空,则说明正在初始化中,则保存数据的索引使用初始化中的索引

 

以上就是针对es索引初始化期间影响正常使用的解决方案,其实没有难度的,只是提供一种思考方式,有问题欢迎提出交流。

 

关注不迷路

MySQL高级相关更多内容,如事务,锁,MVCC,读写分离,分库分表等还在持续更新中,欢迎关注催更。

我是阿纪,用输出倒逼输入而持续学习,持续分享技术系列文章,以及全网值得收藏好文,欢迎关注公众号,做一个持续成长的技术人。

 

posted @ 2021-12-19 11:03  纪先生笔记  阅读(322)  评论(0编辑  收藏  举报