对于多属性类型系统的数据库设计
主要是以下⼏类系统:
1. ⽣活信息系统, 内容:⼩, 属性:⼤,
2. 电商商品系统, 内容:⼤, 属性:⼤,
3. 风控征信系统, 内容:⼩, 属性:⼤,
4. 新闻系统, 内容:⼤, 属性:⼩,
这些系统共同的特点, 都是在主体内容上会携带多个属性, 并且属性需要随时能调整, 并且要求能兼容旧属性, 还需要频繁的通过属性组合进⾏检索.
对于属性的管理, 可以参考58同城的这个解决⽅案
其⽅式, 就是通过JSON化的字段, 来避免使⽤纵表, 这样可以i减轻开发的⼯作量, 以及实际运⾏时的数据量. 在这个之上, 使⽤⼀些⼿段压缩了JSON字段的尺⼨, 以及通过属性, 属性分组, 属性枚举等结构增加了数据的灵活性. 在搜索上, 58是使⽤⾃研的系统, 这个在普通应⽤中可以使⽤elastic search代替.
另外关于电商SKU的数据设计, 也可以采⽤上⾯的⽅式避免纵表带来的巨⼤记录数量. SKU⼀般是按 产品分类, 产品, 产品SKU, 属性分组, 属性, 属性枚举 这样的结构来实现业务数据管理的.
携程在Elastic Search上的介绍
【携程旅⾏⽹ 吴晓刚】
ElasticSearch⽬前在互联⽹公司主要⽤于两种应⽤场景,其⼀是⽤于构建业务的搜索功能模块且多是垂直领域的搜索,数据量级⼀般在千万⾄数⼗亿这个级别;其⼆⽤于⼤规模数据的实时OLAP,经典的如ELKStack,数据规模可能达到千亿或更多。 这两种场景的数据索引和应⽤访问模式上差异较⼤,在硬件选型和集优化⽅⾯侧重点也会有所不同。⼀般来说后⼀种场景属于⼤数据范畴,数据量级和集规模更⼤,在管理⽅⾯也更有挑战。
数据库属性的概念应Medcl⼤⼤的邀请,为ES中⽂社区做今年的Advent开篇,分享⼀下我在管理⾃家公司⽤于⽇志分析的ES集⽅⾯的⼀点⼼得,蜻蜓点⽔,泛泛⽽谈,希望⼤⽅向上能对⼤家提供⼀些帮助。
这⾥的⾃家,即是携程旅⾏⽹。从2013年开始接触ES,我们团队先后实践过0.9.x -> 5.0.0中间各个版本,从最初只⽤于运维内部IIS⽇志的分析,到如今⽀持IT、呼叫中⼼、安全、测试、业务研发等多个部门超过200种⽇志型数据的实时检索与分析。 ⼀路⾛来,愉悦了⼤家,也死磕了⾃⼰。
⽬前我们最⼤的⽇志单集有120个data node,运⾏于70台物理服务器上。数据规模如下:
单⽇索引数据条数600亿,新增索引⽂件25TB (含⼀个复制⽚则为50TB)
业务⾼峰期峰值索引速率维持在百万条/秒
历史数据保留时长根据业务需求制定,从10天 - 90天不等
集共3441个索引、17000个分⽚、数据总量约9300亿, 磁盘总消耗1PB
Kibana⽤户600多⼈, 每⽇来⾃Kibana和第三⽅的API调⽤共63万次
查询响应时间百分位 75%:0.160s 90%:1.640s 95%:6.691s 99%:14.0039s
运维这样⼤规模的ES集,有哪些值得注意的地⽅?
⼀. 必不可少的⼯具
⼯欲善其事必先利其器,从⼀开始,哪怕就只有⼏个node,就应该使⽤分布式配置管理⼯具来做集的部署。随着应⽤的成熟,集规模的逐步扩⼤,效率的提升会凸显。 官⽅提供了ES Puppet Module和Chef Cookbook,熟悉这两个⼯具的同学可以直接拿过来⽤。 我们⾃⼰则是采⽤的Ansible,编写了
⼀套Playbook来达到类似的效果。 ⽤熟这类⼯具,对于集的初始部署,配置批量更改,集版本升级,重启故障结点都会快捷和安全许多。
第⼆个必备利器就是sense插件。通过这个插件直接调⽤集的restful API,在做集和索引的状态查看,索引配置更改的时候⾮常⽅便。语法提⽰和⾃动补全功能更是实⽤,减少了翻看⽂档的频率。在Kibana5⾥⾯,sense已经成为⼀个内置的控制台,⽆需额外安装。
⼆. 硬件配置
我们采⽤的是32vcoreCPU + 128GB RAM的服务器,磁盘配置⼤部分服务器是12块4TB SATA机械磁盘做的Raid0,少部分机器是刚上了不久的6块800GB SSD raid0,主要⽬的是想做冷热数据分离,后⾯谈到集架构的时候,再进⼀步解释⼀下如何利⽤硬件资源。
三. 集的管理
⾸先很有必要对ES的结点做⾓⾊划分和隔离。⼤家知道ES的data node除了放数据以外,也可以兼任master和client的⾓⾊,多数同学会将这些⾓⾊混⼊到data node。然⽽对于⼀个规模较⼤,⽤户较多的集,master和client在⼀些极端使⽤情况下可能会有性能瓶颈甚⾄内存溢出,从⽽使得共存的data node故障。data node的故障恢复涉及到数据的迁移,对集资源有⼀定消耗,容易造成数据写⼊延迟
或者查询减慢。如果将master和client独⽴出来,⼀旦出现问题,重启后⼏乎是瞬间就恢复的,对⽤户⼏乎没有任何影响。另外将这些⾓⾊独⽴出来的以后,也将对应的计算资源消耗从data node剥离出来,更容易掌握data node资源消耗与写⼊量和查询量之间的联系,便于做容量管理和规划。
避免过⾼的并发,包括控制shard数量和threadpool的数量。在写⼊量和查询性能能够满⾜的前提下,为索引分配尽量少的分⽚。分⽚过多会带来诸多负⾯影响,例如:每次查询后需要汇总排序的数据更多;过多的并发带来的线程切换造成过多的CPU损耗;索引的删除和配置更新更慢Issue#18776; 过多的shard也带来更多⼩的segment,⽽过多的⼩segment会带来⾮常显著的heap内存消耗,特别是如果查询线程配置得很多的情况下。 配置过⼤的threadpool更是会产⽣很多诡异的性能问题Issue#18161⾥所描述的问题就是我们所经历过的。 默认的Theadpool⼤⼩⼀般来说⼯作得很不错了。
冷热数据最好做分离。对于⽇志型应⽤来说,⼀般是每天建⽴⼀个新索引,当天的热索引在写⼊的同时也会有较多的查询。如果上⾯还存有⽐较长时间之前的冷数据,那么当⽤户做⼤跨度的历史数据查询的时候,过多的磁盘IO和CPU消耗很容易拖慢写⼊,造成数据的延迟。所以我们⽤了⼀部分机器来做冷数据的存储,利⽤ES可以给结点配置⾃定义属性的功能,为冷结点加上"boxtype":"weak"的标识,每晚通过维护脚本更新冷数据的索引路由设置uting.allocation.{require|include|exclude},让数据⾃动向冷结点迁移。 冷数据的特性是不再写⼊,⽤户查的频率较低,但量级可能很⼤。⽐如我们有个索引每天2TB,并且⽤户要求保持过去90天数据随时可查。保持这么⼤量的索引为open状态,
并⾮只消耗磁盘空间。ES为了快速访问磁盘上的索引⽂件,需要在内存⾥驻留⼀些数据(索引⽂件的索引),也就是所谓的segment memory。稍微熟悉ES的同学知道,JVM heap分配不能超过32GB,对于我们128GB RAM, 48TB磁盘空间的机器⽽⾔,如果只跑⼀个ES实例,只能利⽤到32GB不到的heap,当heap快⽤饱和的时候,磁盘上保存的索引⽂件还不到10TB,这样显然是不经济的。因此我们决定在冷结点上跑3个ES实例,每个分配31GB heap空间,从⽽可以在⼀台物理服务器上存储30多TB的索引数据并保持open状态,供⽤户随时搜索。 实际使⽤下来,由于冷数据搜索频率不⾼,也没有写⼊,即时只剩余35GB内存给os做⽂件系统缓存,查询性能还是可以满⾜需求的。
不同数据量级的shard最好隔离到不同组别的结点。 ⼤家知道ES会⾃⼰平衡shard在集的分布,这个⾃动平衡的逻辑主要考量三个因素。其⼀同⼀索引下的shard尽量分散到不同的结点;其⼆每个结点上的shard数量尽量接近;其三结点的磁盘有⾜够的剩余空间。这个策略只能保证shard数量分布均匀,⽽并不能保证数据⼤⼩分布均匀。 实际应⽤中,我们有200多种索引,数据量级差别很⼤,⼤的⼀天⼏个TB,⼩的⼀个⽉才⼏个GB,并且每种类型的数据保留时长⼜千差万别。抛出的问题,就是如何能⽐较平衡并充分的利⽤所有节点的资源。 针对这个问题,我们还是通过对结点添加属性标签来做分组,结合index routing控制的⽅式来做⼀些精细化的控制。尽量让不同量级的数据使⽤不同组别的结点,使得每个组内结点上的数据量⽐较容易⾃动平衡。
定期做索引的force merge,并且最好是每个shard merge成⼀个segment。前⾯提到过,heap消耗与s
egment数量也有关系,force merge可以显著降低这种消耗。 如果merge成⼀个segment还有⼀个好处,就是对于terms aggregation,搜索时⽆需构造Global Ordinals,可以提升聚合速度。
四. 版本选择
我们在2.4版本上稳定跑了很长时间,⽐较保守的同学可以上2.4,激进有精⼒折腾的可以考虑最新的5.0。 我们集两周前从v2.4.0升级到了v5.0.0这个版本,除了升级第⼀周遇到⼀个不稳定的问题以外,感觉新版本带来的以下特性还是⾮常值得去升级的:
结点启动的Bootstrap过程加⼊了很多关键系统参数设置的核验,⽐如Max File Descriptors, Memory Lock, Virtual Memory设置等等,如果设置不正确会拒绝启动并抛出异常。 与其带着错误的系统参数启动,并在⽇后造成性能问题,不如启动失败告知⽤户问题,是个很好的设计!
索引性能提升。升级后在同样索引速率下,我们看到cpu消耗下降⾮常明显,除了对索引速率提升有帮助,也会⼀定程度提升搜索速率。
新的数值型数据结构,存储空间更⼩,Range和地理位置计算更快速
Instant Aggregation对于类似now-7d to now这样的范围查询聚合能够做cache了,实际使⽤下来,效果明显,⽤户在Kibana上跑个过去⼀周数据的聚合,头2次刷新慢点,之后有cache了⼏乎就瞬间刷出!
更多的保护措施保证集的稳定,⽐如对⼀次搜索hit的shard数量做了限制,增强了circuit breaker的特性,更好的防护集资源被坏查询耗尽。
升级第⼀周,我们的冷数据结点出现间歇性不响应问题,从⽽刨出3个issue提交给官⽅:
Issue#21595 Issue#21612 Issue#21611
第⼀个问题确认为Bug,将在5.0.2修复,其他两个⽬前还不清楚根源,看起来也只在我们的应⽤场景⾥遇到了。所幸问题都到了了规避措施,实施这些措施以后,最近⼀周我们的集重新回到以前2.4版本时期的稳定状态。
五. 监控
不差钱没空折腾的建议还是买官⽅的xpack省⼼,有精⼒折腾的,利⽤ES各种丰富的stats api,⽤⾃⼰熟悉的监控⼯具采集数据,可视化出来就好了。 那么多监控指标,最最关键的还是以下⼏类:
各类Thread pool的使⽤情况,active/queue/reject可视化出来。 判断集是否有性能瓶颈了,看看业务⾼峰期各类queue是不是很
⾼,reject是不是经常发⽣,基本可以做到⼼⾥有数。
JVM的heap used%以及old GC的频率,如果old GC频率很⾼,并且多次GC过后heap used%⼏乎下不来,说明heap压⼒太⼤,要考虑扩容了。(也有可能是有问题的查询或者聚合造成的,需要结合⽤户访问记录来判断)。
Segment memory⼤⼩和Segment的数量。节点上存放的索引较多的时候,这两个指标就值得关注,要知道segment memory是常驻heap不会被GC回收的,因此当heap压⼒太⼤的时候,可以结合这个指标判断是否是因为节点上存放的数据过多,需要扩容。Segement的数量也是⽐较关键的,如果⼩的segment⾮常多,⽐如有⼏千,即使segment memory本⾝不多,但是在搜索线程很多的情况下,依然会吃掉相当多的heap,原因是lucene为每个segment会在thread local⾥记录状态信息,这块的heap内存开销和(segment数量* thread数量)相关。
很有必要记录⽤户的访问记录。我们只开放了http api给⽤户,前置了⼀个nginx做http代理,将⽤户第三⽅api的访问记录通过access log 全部记录下来。通过分析访问记录,可以在集出现性能问题时,快速到问题根源,对于问题排查和性能优化都很有帮助。
最后就是多上⼿实践,遇到问题多查官⽅资料,多Google看是否有其他⼈遇到同类问题,精⼒充⾜有编程背景的同学也可以多刨刨源码。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。