简单了解Redis、ElasticSearch、Zookeeper

简单了解Redis、ElasticSearch、Zookeeper

以Redis为例的分布式缓存

基本内容

  1. 博客里有专门写过Redis的分析帖,不再赘述。本文只说说redis的使用场景分析。

核心数据结构使用场景

  1. String:
    • 因为Redis的String是二进制安全的,因此可以存储对象。对象存储形式可以为JSON或者序列化后的对象。这边再介绍一种方法,通过MSET方法批量插入对象属性,好处是避免序列化或解析JSON,直接从redis中获取对象相应属性值。
    • SETNX\DEL可以实现分布式锁。SETNX(SET IF NOT EXISTS):只在键key不存在的情况下,将value设为true,如果key已经存在,则不进行任何操作。因此比如有3个线程希望获得分布式锁,只有返回值为1的线程表示拿到了分布式锁,通过DEL释放锁。问题是,如果拿到锁的线程宕机,那么将不会执行DEL,导致其他线程永远无法拿到锁,解决方案是加一个过期时间。
    • INCR/DECR 通过计数器实现阅读/点赞/商品的计数
    • 实现分布式session。
    • INCRBY,批量增加,实现分布式系统全局序列号。比如有10个请求需要对数据库的同一张表进行操作,那么可以用redis给每个请求的进程用INCRBY增加100,相当于第一个进程插入1到100序号,第二个进程插入101到200。因为redis是单线程的,因此用INCRBY这个原子操作让每个请求都分配到了不冲突的号。
  2. Hash:
    • 对象缓存,类似于MSET。
    • 电商购物车 1.以用户id为key 2.以商品id为field 3.商品数量为value。
      标准:hset key field value 举例:hset cart:1001 10099 1,在cart:1001这个key中,有field叫商品id,value为商品id对应的数量,所以举例中的意思是,在cart:1001购物车中,有商品id为10099的商品1个。用java语言来表示就是——
      Map<String,Map<Product,Integer>> cart = new HashMap();
      Product p = new Product(10099);
      Map<Product,Integer> map = new HashMap();
      
      String key = "cart:1001";
      cart.put(cart,map);
      cart.get("cart:1001").put(p,1);
      
      [外链图片转存失败(img-3DhRnsFj-1566955872876)(http://img.sonihr.com/7d9fde2f-1e6d-4f42-801f-ed3e41efa488.jpg)]
      用hincrby/hdecrby来增加和删除购物车中商品数量,用hlen来获得购物车总数量,用hgetall来实现全选。
  3. List:
    • 实现栈、队列、阻塞队列的数据结构。
      [外链图片转存失败(img-OuA6puAw-1566955905269)(http://img.sonihr.com/191d2824-0a46-4079-a3d9-e351cfe759b4.jpg)]
    • 实现微博信息流,某大V有100万个关注,那么它的消息就要定时推送给这100万个粉丝,通过LPUSH将消息放到信息流最前,如果你关注的另一个大V随后也发了一个信息,那么他也LPUSH,这样缓存中就是先大V2号的消息,再大V1号的消息,通过LRANGE方法读取即可。
  4. Set:
    • 微信抽奖小程序
    • 微信/微博点赞、收藏、标签
      http://img.sonihr.com/977bf2db-0483-46c1-8f0c-130a986605b6.jpg
    • 集合操作实现微博微信关注模型
      [外链图片转存失败(img-l6LjlJAK-1566955842321)(http://img.sonihr.com/c1946cde-45b5-46ff-a252-deedbbd0c2c3.jpg)]
  5. ZSet:
    • 微博热搜
      [外链图片转存失败(img-0YZtsd8p-1566955970835)(http://img.sonihr.com/f2c218be-9f28-409e-bc1d-9259441efa38.jpg)]

以ElasticSearch为例的搜索引擎

为什么要用一种新的搜索引擎,数据库不行么?

  1. 比如你从数据库里搜索,就是如下图:

    存在以下问题
    • 张三%还能走索引,如果是%张三%呢?不能走索引就要全表搜索,太慢了。
    • 无法将搜索词进行拆分,上图只能搜张三开头的员工,搜不到张大三。
  2. 了解全文索引、倒排索引。 全文索引是指对文中某个词建立索引,给出其在文章中的坐标。倒排索引被用来存储在全文搜索下,某个单词在一个文档或一组文档中的存储映射,即对单词建立索引,索引所指向的数据是单词出现的位置。为什么要叫倒排呢? 因为一个未经处理的数据库中,一般是以文章ID作为索引,以文档内容作为记录。

倒排索引

  1. 具体说说倒排索引。 这边有一个生动的样例,比如有人让你说出含有“前”这个字的诗句,你是很难说出来的,因为我们人脑中存放的一般是“诗名”–>“诗文”的映射,而倒排索引就是一组“诗中字”—“诗文”的索引。但是如果这样,一个诗文有太多的字,那建立的索引就会非常多,因此我们建立“诗中字”—>“诗名”—>“诗文”的索引,前半部门是倒排索引,后半部分是正序索引。当然,我们一个“诗中字”可能映射了多个“诗名”,这个也叫索引矩阵。
  2. 由倒排索引到ElasticSearch中的存储结构。ES的索引是存放数据的地方,而不是刚才说的倒排索引的key,一个索引对应一种类型,这种类型通过Mapping规定了每个字段的类型,比如keyword类型,text类型,integer类型。keyword类型是不用分词的,而text类型是要分词的,

keyword就是上文说的key,什么意思?凡是类型为keyword的,都会根据字符串内容建立反向索引,text类型会先分词,然后根据分词结果建立反向索引。即,上文中,标题、作者、朝代、字数都是keyword,都被建立了反向索引,因此当你搜索作者为李白的时候,将会搜索出所有author为李白的文档,而content是text类型的,因此当你搜索“夜”的时候,也同样会得到这个文档。

  1. 如何建立倒排索引呢?ES是通过HTTP的REST API来实现语言无关的操作的,比如使用curl -XPUT 'ip:port/poems’就能建立一个名为poems的索引。

ElasticSearch是什么?

  1. ElasticSearch是分布式的搜索引擎,以REST API向外提供无言无关的接口。因为是支持分布式的,因此可以自动维护数据分布到多个节点的索引的建立,搜索请求分布到多个节点的执行,自动维护数据的冗余副本,保证了一旦机器宕机,不会丢失数据。封装了更多高级的功能,比如聚合分析、基于地理位置的搜索。ElasticSearch底层搜索的功能是封装了Lucene这个jar包。按照我的理解,ES本质上就是一个微服务,这个微服务通过REST向外提供服务,正如之前所示,微服务本身可能也需要一个集群来共同提供服务。

ES的分布式原理

  1. 什么叫shard,分片 分片是指索引的数据被分配到各个分片上,相当于一桶水用了N个杯子装。分片有利于横向扩展,即集群内增加更多的机器,此时N个分片会rebalance到不同的几点上班,分片的最大大小是Integer.MAX_VALUE - 128个文档。什么叫replica,备份分片 primary shard为主分片,replica为备份分片,主分片和备份分片不会出现在同一个节点上,这是为了防止单点故障。默认情况下,一个索引创建a个分片,b个备份,其中一个备份=a个分片,此时节点数 = a * (1+b)个。replica的作用包括容灾(replica变成primary,然后replica自动创建新的replica),提高查询性能(查询既可以查主,也可以查备)。主分片数量不能随便调整,只能重建索引,但是replica可以随时调整。

  2. ES会对数据进行切分,同时每一个分片会保存多个副本。下图中,绿色的表示数据块,其实ES中的数据块也是备份存储至多个节点中的。在ES中也是master、salve架构的,每个节点是对等的,节点间会通过自己的一些规则选举集群的master,master会负责集群状态信息的改变,并同步给其他节点。

    下图是建立索引的过程,先通知master建立索引,然后在同步到其他节点。

    只有建立索引要经过master,数据的写入有一个简单的Routing规则,可以route到集群中的任何节点,所以数据写入的压力是分散在整个集群的。

  3. 分片机制:当我们存储一篇文章时,不需要手动选择存储在什么分片上,是自动的。shard负载均衡:当我们有3个节点要分配25片是,es会自动平均分配。shard副本:当我们新增索引时,备份管理由ES自动完成。请求路由:你可以向任何一个节点请求数据,ES会自动将我们的请求定位到有数据的地方并返回。集群扩容:当我们新启动一个节点时,会自动加入已经有的集群中。shard重分配:当我们有没有分配shard的时候,如果有新的节点加入,则会自动分配该节点。

  4. ES的水平扩容和垂直扩容。垂直扩容是增加集群中单机的存储容量,水平扩容是增加机器数量。

ES的使用场景

  1. 使用ElasticSearch作为主要后端。传统的项目中,搜索引擎是部署在成熟的数据存储的顶部,以提供快速且相关的搜索能力。这是因为早期的搜索引擎不能提供耐用的存储或其他经常需要的功能,如统计。
  2. 在现有的系统中增加elasticSearch。用ES来辅助数据库进行检索,这个ES就相当于是缓存数据库。此时数据库与ES之间就存在一个同步的问题,可以通过定时器进行自动同步。

以Zookeeper为例的注册中心

Zookeeper是什么

  1. 看下图。

    server指的是实体的服务器,他们在逻辑上可以成为leader或者follower或者observer。但是这一群server共同组成了一个zookeeper服务,而在这个服务之中的server都具有数据最终一致性。
  2. 是一个数据库(Node),通过create /luban 123,在/luban这个节点存入数据,get可以读取数据。(数据大小限制在1M以内)
  3. 是一个拥有文件系统特点的数据库
    如上图,一定要是/luban而不是luban,因为其是具有文件系统特点的,每个znode都可以存储数据。有四种znode,持久化目录节点,持久化顺序编号目录节点,临时目录节点,临时顺序目录节点。持久化代表客户端与zookeeper断开连接后仍然存在节点,临时表示会话结束后即删除节点。顺序编号表示zookeeper自动为节点名称进行编号。
  4. 是一个解决了数据一致性该问题的分布式数据库
    同一个集群中的zookeeper中的数据都是一致的
  5. 是一个具有发布和订阅功能的分布式数据库(Watch)。即实现了通知机制,客户端注册监听他关心的目录节点,当目录节点发生变化(数据变化,被删除,子目录节点增加或删除)时,zookeeper会通知客户端。
  6. Zookeeper是分布式的,但是它适用于管理集群的,监视集群中各个节点的状态,从而达成集群间数据的一致。客户端的读操作可以被集群中任意一台机器处理,写操作会同时发给所有zookeeper,然后达成一致后才会返回成功。因此,当ZK集群机器增多,会导致读请求吞吐提高,但写降低。
  7. ZK是有序的,所有更新都是全局有序的,每个更新都有一个时间错,兼做zxid。读操作会返回zxid,因此读不一定是最新的。换而言之,ZK保证最终一致,但是不保证强一致。

Zookeeper的使用场景

  1. 注册中心。 在Dubbo中就是承担的注册中心任务。服务生产者将自己提供的服务注册到ZK,服务的消费者在进行服务调用的时候先到ZK中查找服务,获取到服务生产者的详细信息后,再去调用服务生产者的内容和数据。
  2. 配置一致。比如有多台服务器,每个服务器共享一个配置文件,此时你要修改服务器配置文件,就很麻烦。你可以将配置文件存放在Zookeeper这个树的一个数据节点,然后多台服务器对这个数据节点设置监听器,当配置文件修改后,会通知这些服务器修改配置文件。(观察者模式)
  3. 主备用动态切换。有一个Master节点,多个follower节点,如果Master宕机了,就需要其他节点顶上,即主备动态切换。用Zookeeper的一个数据节点存放Master的信息,并且设置为临时节点,另一个follower节点监听这个临时节点,当master宕机后,follower收到消息,然后自动成为主节点,当原master再上线后发现已经存在新master,则转变为follower。注意,客户端不是直接去连接master,而是从zookeeper中找master的地址,然后再去连接,实现了解耦。
  4. 发布/订阅,本质上还是用到了watch。
  5. 软负载均衡。在Zookeeper中记录每台服务器的访问数,让访问数最少的服务器去处理最新的客户端请求。
  6. 分布式锁。
  7. 统一命名服务。在分布式环境中,有些资源虽然内部是多个,但是要对外提供统一的命名,可以采用zookeeper,父节点为统一命名,而子节点是不同的名称。如下图,统一以域名对外提供访问,但是内部其实分为了很多的ip。
  8. 统一的集群管理,通过zookeeper内可以实现对节点状态的监控,方法是用客户端注册并监听一个节点,那么这个客户端可以获得这个节点的状态变化。

分布式与数据复制

  1. Zookeeper为一个集群提供一致的数据服务,因此它会在所有的机器之间提供数据复制。
    • 容错:一个节点出错,别的节点可以接替他。
    • 提高系统扩展能力:把负载分布到多个节点上,或增加节点来提高系统的负载能力
    • 提高性能:让客户端本地访问就近的节点,提高访问速度
  2. ZK的任意服务器(包括leader,follower,observer)均可以接收请求,但只有leader可以处理写请求,读操作是任何server都可以处理的,但是获得的数据不一定是最新的,因此会返回zxid和读到的值。ZK对数据的写操作全部由leader完成,leader写入日志后再复制到所有的follower节点(如下图,这个是2PC的
    写leader
    写Follower/Observer

ZAB协议,原子广播协议

  1. ZAB协议,支持崩溃恢复的一致性协议,有两种模式,1.选主模式 2.广播模式。正常情况下应该是广播模式,只有在服务启动和leader宕机的时候才进入恢复模式,即选主模式。广播模式即同步模式,当leader被选举,且过半的服务器完成了和leader的状态同步后,选主模式结束,同步模式确保leader和server具有相同的状态。
  2. 选主过程:当leader宕机或者leader失去过半follower(比如过半follower都在一个机房,但是这个机房和leader之间网线断了),此时进入选主模式,采用fast-paxos算法:某机器向所有server(包括自己)请求成为leader(这相当于paxos中的proposer角色),自己肯定投自己一票,然后和别人竞争(节点启动一个接受线程接受其他节点发送过来的投票,并对投票进行处理,相当于paxos中的acceptor角色),在接受线程中,会和自己的选票比较,先比较zxid,再比较myid,大的优先,且更新自己的选票,并且把选票放在选票列表中(每个节点都有一个自己的选票列表),该列表存储了所有节点的投票,key是节点id,value是该节点的投票,然后再次把自己的投票发给其他节点。接下来,节点会统计选票列表中每个节点获得的票数,当选举票数超过一半后,成为leader。以上说的投票,都指的是一个轮次中,什么意思?有第一轮投票,第二轮投票,因此需要一个计数器来标识当前是第几轮投票,即logicClock。若受到的logicClock大于自己的,则将自己的投票箱清空,并且设置为最新的logicClock,如果收到的logicClock小于自己的,则忽略。
    备注: leader怎么知道follower还活着?follower怎么知道leader还活着?leader和follower之间会定时互相发送ping,无果没有ping通就会变为looking状态,并发起新一轮选举,处于选举模式时,zookeeper服务不可用。
    给出一篇很好的参考文献

    https://dbaplus.cn/news-141-1875-1.html

  3. 同步流程:follower发送给leader自己最大的zxid,leadr根据发来的zxid确定同步点,完成同步后,leader通知follower已经变成uptodate状态,可以重新接受client的请求进行服务。

watch机制

  1. 所有对ZK的读操作,都可以附带一个watch,一旦相应的数据有变化,该watch就会被处罚。
    • 主动触发:watch被触发是,有ZK服务器主动将更新推送给客户端。
    • 一次性:watch只会被触发一次,如果想获得后续更新,需要在接收到watch通知后再重新注册一个。
    • 可见性:更新通知优先于鞥下结果。即,如果watch被附加在读上,那客户端在得到watch消息之前,不会看到更新后的数据。
    • 顺序性: 多个更新触发了watch,则watch被触发的顺序与更新顺序一致。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值