从几个例子出发:
1.ES的分片机制天生支持分布式,同时也带来了分布式了弊端:排序和算分问题;
搜索的场景需要严格的定制化,通过对mapping的修改可以支持算分的调整,影响查询的结果-----代价是大大增加开发和mapping的维护成本
想要精确的算分和排序--->需要大量的计算和内存
如果数据量不大---->默认使用一个分片---->不需要对排序和算法另外处理/同时牺牲了分布式的特性(建议一个业务场景的分片不超过20g。日志索引的分片不超过50g),这也是为什么7.x版本之后默认一个分片
如果数据量巨大---->必须使用分片----->定制化mapping,粒度到字段级别----->同时还需要大量的内存(只能通过优化尽量压缩,对业务进行区分,必要的时候只能占用大量的cpu和内存 DFS query then Fetch)
下面来看一下ES的查询过程:默认场景下的-----牺牲精准度保证效率
2.kafka与ES面临相同的问题:kafka的单个分区内可以保证顺序性,分区之间是不能保证顺序性的
如果是类似于日志,不需要考虑消息的顺序性,那可以很好的发挥性能
如果需要保证顺序性:
2.1)从业务上把需要有序的打到同一个partition。因为大多数情况只需要业务上保证有序就可以,不用全局有序(通过message key来定义,因为同一个key的message可以保证只发送到同一个partition,比如说key是user id,table row id等等,所以同一个user或者同一个record的消息永远只会发送到同一个partition上,保证了同一个user或record的顺序)
那么单个分片内kafka如何保证有序?
- producer发消息到队列时,通过加锁保证有序 现在假设两个问题: broker leader在给producer发送ack时,因网络原因超时,那么Producer 将重试,造成消息重复。 先后两条消息发送。t1时刻msg1发送失败,msg2发送成功,t2时刻msg1重试后发送成功。造成乱序。
2.解决重试机制引起的消息乱序
为实现Producer的幂等性,Kafka引入了Producer ID(即PID)和Sequence Number。对于每个PID,该Producer发送消息的每个都对应一个单调递增的Sequence Number。同样,Broker端也会为每个维护一个序号,并且每Commit一条消息时将其对应序号递增。对于接收的每条消息,如果其序号比Broker维护的序号)大一,则Broker会接受它,否则将其丢弃:
- 如果消息序号比Broker维护的序号差值比一大,说明中间有数据尚未写入,即乱序,此时Broker拒绝该消息,Producer抛出InvalidSequenceNumber
- 如果消息序号小于等于Broker维护的序号,说明该消息已被保存,即为重复消息,Broker直接丢弃该消息,Producer抛出DuplicateSequenceNumber
- Sender发送失败后会重试,这样可以保证每个消息都被发送到broker
3.分布式的系统中路由算法的弊端:增加/删除节点时会有路由错误的问题(引发类似于缓存雪崩的场景)
对应的解决方案:
3.1)如redis的一致性hash算法
3.2)ES同样面临类似的场景:每个index的主分片设置策略,动态分配会有大量的数据迁移,分配不合理又会影响性能
对此,ES的解决方案是:index建立好之后不允重新设置分片,需要重新设置需要reindex
2.从CAP到BASE,都是对分布式的一种权衡策略
总结:
根据不同的需求和场景,进行定制化的使用