Ajax&Redis问题总结

一、JQuery——Ajax实现三大方法的应用及区别(get,post,ajax)

1. $.get()方法

<script type="text/javascript">
    $(function () {
        $("#send").click(function () {
            $.get("AjaxHandler.ashx", {
                username: $("#username").val(),
                content: $("#content").val()
            },
             function (data, status) {
                 $("#resText").html(data);//把返回的数据添加到页面上
             });
        });
    });
</script>

2. $.post()方法

<script type="text/javascript">
    $(function () {
        $("#send").click(function () {
            $.post("AjaxHandler.ashx", {
                username: $("#username").val(),
                content: $("#content").val()
            },
             function (data, status) {
                 $("#resText").html(data);//把返回的数据添加到页面上
             });
        });
    });
</script>

3. $.ajax()方法

<script type="text/javascript">
    $(function () {
        $("#send").click(function () {
            $.ajax({
                type: "post",
                timeout: 5000,//设置服务器请求超时的时间
                url: "AjaxHandler.ashx",//提交的地址
                data: $("#form1").serialize(),//序列化表单
                beforeSend: function () {//表单提交前执行的函数
                    return confirm("确认要提交吗?");
                },
                error: function () {
                    alert("提交失败!");
                },
                success: function (data) {
                    $("#resText").html(data);
                }
            });
        });
    });
</script>

二、Redis五种数据类型及应用场景

String: 一般做一些复杂的计数功能的缓存;

List: 做简单的消息队列的功能;

Hash: 单点登录;

Set: 做全局去重的功能;

SortedSet: 做排行榜应用,取TopN操作;延时任务;做范围查找。

类型

简介

特性

场景

String (字符串)

Redis的字符串是动态字符串,是可以修改的字符串,它的内部表示就是一个字符数组, 内部结构的实现类似于Java的ArrayList 它的内部结构是一个带长度信息的字节数组

可以包含任何数据,比如jpg图片或者序列化的对象,规定字符串的长度不得超过512MB。 Redis的字符串有两种存储方式,在长度特别短时,使用embstr形势存储,而长度超过44字节时候,使用raw形势存储

1、访问量统计:每次访问博客和文章使用 INCR 命令进行递增 2、将数据以二进制序列化的方式进行存储

Hash (字典)

Redis的字典相当于Java语言里面的HashMap 字典结构内部包含了两个Hashtable,通常情况下只有一个Hashtable是有值的, 但是在字典扩容缩容时候,需要重新分配新的Hashtable,然后进行渐进式搬迁,这时候两个Hashtable存储的分别是旧的Hashtable和新的Hashtable;待搬迁结束后,旧的Hashtable被删除,新的Hashtable取而代之

适合存储对象,并且可以像数据库中update一个属性一样只修改某一项属性值(Memcached中需要取出整个字符串反序列化成对象修改完再序列化存回去)。 大字典的扩容是比较耗时的,需要重新申请新的数组,然后将旧字典所有链表中的元素重新挂接到新的数组下面,这是一个O(n)级别的操作,作为单线程的Redis很难承受这样耗时的过程,所以Redis使用渐进式rehash小步搬迁虽然慢一点,但是肯定可以搬完。

1、存储、读取、修改对象属性,比如:用户(姓名、性别、爱好),文章(标题、发布时间、作者、内容)

List (列表)

Redis的列表相当于Java的LinkedList List的结构底层实现不是一个简单的LinkedList,而是快速链表(quicklist)。 首先在列表元素较少的情况下,会使用一块连续的内存存储,这个结构是ziplist,即压缩列表。它将所有的元素彼此紧挨着一起存储,分配的是一块连续的内存;当数据量比较多的时候才会改成quicklist。

增删快,提供了操作某一段元素的API 普通的链表需要的附加指针空间太大,会浪费空间,加重内存的碎片化。Redis将链表和ziplist结合起来组成了quicklist,也就是将多个ziplist使用双向指针串联起来使用,既满足了快速的插入删除性能,又不会出现太大的空间冗余

1、最新消息排行等功能(比如朋友圈的时间线) 2、消息队列

Set (集合)

Redis的集合相当于Java语言里面的HashSet,内部的键值对是无须的、唯一的 Set的结构底层实现是字典,只不过所有的value都是NULL,其他的特性和字典一摸一样。

1、添加、删除、查找的复杂度都是O(1) 2、为集合提供了求交集、并集、差集等操作 当set集合容纳的元素都是整数并且元素个数较少时,Redis会使用intset来存储集合元素。intset是紧凑的数组结构,同时支持16位,32位和64位整数

1、共同好友 2、利用唯一性,统计访问网站的所有独立ip 3、好友推荐时,根据tag求交集,大于某个阈值就可以推荐

Sorted Set (有序集合)

Redis有序列表类似于Java的SortedSet和HashMap的结合体, 一方面是一个set,保证内部value的唯一性,另一方面可以给每个value赋予一个score,代表这个value的排序权重。 它的内部实现是一个Hash字典 + 一个跳表。

数据插入集合时,已经进行天然排序 Redis的跳表共有64层,能容纳2的64次方个元素。 Redis之所以用跳表来实现有序集合 1. 插入、删除、查找以及迭代输出有序序列这几个操作,红黑树都能完成,时间复杂度跟跳表是一样的。但是按照区间来查找数据,红黑树的效率就没有跳表高 2. 跳表更容易代码实现,比起红黑树来说还是好懂、好写很多,可读性好,不容易出错 3. 跳表更加灵活,可以通过改变索引构建策略,有效平衡执行效率和内存消耗

1、排行榜,取TopN操作 2、带权重的消息队列

三、redis的缓存穿透、击穿、雪崩以及实用解决方案 

1.缓存穿透

概念

  • 缓存穿透指某一特定时间批量请求打进来并访问了缓存和数据库都没有的key,此时会直接穿透缓存直达数据库,从而造成数据库瞬时压力倍增导致响应速度下降甚至崩溃的风险;

解决方案

一、通过布隆过滤器解决

  • 原理:将所有需要缓存的key通过hash算法全部放到布隆过滤器将对应下标对应的值置成1,这样当请求进来时先去布隆过滤器里找,发现对应index的key是1则去缓存拿数据为0则直接返回,这样就避免了去数据库查询

  • 优缺点:布隆过滤器底层通过redis的bitMap实现是基于位的操作,所以效率高;但是需要提前将key存入过滤器,这大大增加了前置成本;并且key到index的映射通过hash算法,那么必然会出现hash碰撞的问题;

二、通过key-null

  • 原理:当请求进来从数据库查询回来的值为空时,将对应的null值也存入redis,这样下次请求时redis里已经有数据了就不会再怼到数据库了
  • 优缺点:思路清晰,操作简单,但是极端情况下会有N多key在数据库和缓存中都没有,那这种实现就造成了redis里有过多的垃圾数据,浪费了内存空间

三、增强前置判断

  • 原理:查询前在接口层做权限、逻辑校验,尽可能多的判断key的合法性
  • 优缺点:使用成本低但并不足以规避掉缓存穿透的风险,所以建议只做附加的解决方案使用

2.缓存击穿

概念

  • 缓存击穿指某一特定时间批量请求打进来对一个特定的值进行查询并访问了缓存中没有但是数据库里存在的key,此时会直接击穿缓存直达数据库,从而造成数据库瞬时压力倍增导致响应速度下降甚至崩溃的风险;需要注意的是,这里说的缓存中没有包含两层意思,一是缓存中本身没有,二十缓存中有但是过期了(少量热点key过期)

解决方案

一、设置key的过期时间随机

  • 原理:在像redis批量设置key的时候尽量做到过期时间的随机性,以此避免某一时间会有批量key过期导致的缓存击穿
  • 优缺点:虽然解决了缓存中同一时间批量key过期导致的击穿问题但是并不能解决缓存中压根就不存在key而造成的击穿

二、通过分布式锁解决

  • 原理:在查询数据库的时候加一个分布式锁,使得某一特定时间内只有一个请求访问数据库,访问成功后将查询到的值缓存近redis,这样在下次访问时就不会造成击穿的现象了;

需要注意的是,针对非海量请求的业务这里加单机锁也问题不大,无非就是将同一时间只有一个请求到数据改成了同一时间只有集群数量的请求到数据库,问题应该也不大

  • 优缺点:无论是缓存中本身就没有还是缓存中的key过期了使用加锁的方式都能解决缓存击穿的问题,但是却增加了加锁的成本

三、设置热点key永不过期

  • 原理:针对热点key批量过期造成的穿透问题,将key设置成永不过期就能解决
  • 优缺点:与一一样只能解决key过期造成的击穿不能解决缓存中没有key造成的击穿,并且热点key的确认也是门学问,并不总能保证设置的热点key不遗漏

四、将key部署在不同的实例

  • 原理:针对集群部署的redis,将热点key分开部署也能避免过期造成的击穿问题
  • 优缺点:只有集群才能使用,且也只能解决key过期造成的击穿问题,并不总能保证设置的热点key不遗漏

3.缓存雪崩

概念

  • 与击穿相比雪崩表示过期或压根不存在的key是大量而不是一个或少量,导致大量请求落到数据库;

解决方案

一、通过设置过期时间随机来解决

  • 设置key的时候加上随机过期时间,尽量减少同一时间过期key的数量;

二、设置锁

  • 从缓存访问key开始就加锁,当缓存没有则去数据库查,查完塞入缓存最后释放锁;如果是单机应用则直接加虚拟机锁,集群部署的话则需使用分布式锁

四、解决redis高并发问题的几种思路

1.布隆过滤器

首先,布隆过滤器能解决绝大部分恶意攻击的请求,比如我们数据库中的id通常都设为自增的,是有一定范围大小的,如果有黑客恶意用数据库中没有的id一直访问我们的数据库,这就会给数据库造成很大的压力,为了解决这个bug,去我们可以使用布隆过滤器来过滤大部分恶意请求。

在这里插入图片描述

1.1 实现原理

布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。

布隆过滤器的原理是,当一个元素被加入集合时,通过K个Hash函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在。这就是布隆过滤器的基本思想。
在这里插入图片描述

注:这是最初的布隆过滤器,里面还没存储值,所以都是0

在这里插入图片描述

布隆过滤器会将数据通过多个不同的hash函数将数据生成多个hash值,并映射到槽中,把0变为1,说明有数据。

缺点:

​ 1:布隆过滤器的缺点也很明显,首先是删除数据非常困难,误算率会随着数据的增加逐步加大。

​ 2:它不能准确的判断这个槽中的数据,随着数据的增加,槽中的1也会越来越多,如果我们要查找数据,如果槽中的点有0,就说明没有数据映射到这个bit位上,数据不存在,如果其他的数据也把这个槽中的点置为1,那这个点说明数据存在的,造成了误判,随着数据的增加,被置为1的点会越来越多,即使有一个数据没有被存储过,但万一hash函数返回的三个bit位都被置成了1,那么程序还是会判断这个值是存在的。

总结:尽管有有一定的缺点,但能防止绝大部分数据的判断,也是很推荐使用布隆过滤器的。

能更好的解决缓存穿透。

2.全量同步和增量同步
在这里插入图片描述

Canal是一个基于MySQL二进制日志的高性能数据同步系统

全量同步:就是在上线的时候把查询的所有数据都存到redis中,设置永不失效。

增量同步:在MySql中的binlog日志记录着程序所有的增删改语句,cannal对binlog进行实时监控,如果binlog有更改,就会触发canal同步系统,redis中的内容进行同步更新。

解决问题:缓存雪崩,缓存击穿
 

3.主从原理

主节点:master 用来写

从节点:slave 用来读

主节点只能有一个,从节点可以有多个

 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yUHtosBq-1596442557298)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1596163357043.png)]

使用场景:当QPS(每秒查询率)达到一定高度时,一台服务器不够用时,需要搭建多台服务器时,就用到了主从复制。

原理:首先让用户访问从服务器,从服务器和主服务器建立了连接,主服务器发送数据副本,做到实时更新,这样就有额外的从服务器处理读请求,通过将读请求分发到不同的从服务器上进行处理,以达到分担压力,性能增强。用户请求的写功能,从服务器会将这个请求交给主服务器去做。
 

4.实现高可用

高可用(HA)是分布式系统架构设计中必须考虑的因素之一,它是通过架构设计减少系统不能提供服务的
时间。保证高可用通常遵循下面几点:

单点是系统高可用的大敌,应该尽量在系统设计的过程中避免单点。
通过架构设计而保证系统高可用的,其核心准则是:冗余。
实现自动故障转移
我们现在已经给Redis实现了主从复制,可将主节点数据同步给从节点,从节点此时有两个作用:

从节点扩展主节点的读能力,分担主节点读压力。
一旦主节点宕机,从节点作为主节点的备份可以随时顶上来。(高可用)
 

5.哨兵机制(Sentinel)

 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ysUkjjcF-1596442557300)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1596163717659.png)]

应用场景:当主节点宕机,从节点晋升为主节点,同时修改应用方的主节点地址,还需要命令所有从节点去复制新的主节点,这个过程都是由Sentinel来进行实时监控的。

5.1 哨兵有三个定时监控任务完成对各节点的发现和监控

任务1,每个哨兵节点每10秒会向主节点和从节点发送info命令获取最新拓扑结构图,哨兵配置时只要配置对主节点的监控即可(sentinel monitor mymaster 192.168.1.3 26379 2),通过向主节点发送info,获取从节点的信息,并当有新的从节点加入时可以马上感知到;

任务2,每个哨兵节点每隔2秒会向redis数据节点的指定频道上发送该哨兵节点对于主节点的判断以及当前哨兵节点的信息,同时每个哨兵节点也会订阅该频道,来了解其它哨兵节点的信息及对主节点的判断,其实就是通过消息publish和subscribe来完成的;

任务3,每隔1秒每个哨兵会向主节点、从节点及其余哨兵节点发送一次ping命令做一次心跳检测,这个也是哨兵用来判断节点是否正常的重要依据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ShaZhRmV-1596442557301)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1596164088815.png)]

5.2 故障转移

由Sentinel节点定期监控发现主节点是否出现了故障,sentinel会向master发送心跳PING来确认master是否存活,如果master在“一定时间范围”内不回应PONG 或者是回复了一个错误消息,那么这个sentinel会主观地(单方面地)认为这个master已经不可用了,会要求其他sentinel确认该节点是否丢失,如果确认,则认为是客观下线,的确不可用了,开始进行故障转移
 

6.负载均衡和高可用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ygIYRYHM-1596442557302)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1596165498852.png)]

流程:

1:用户发送请求,首先经过nginx进行反向代理和负载均衡之后,再发送给tomcat集群,做到每个tomcat可以平均分担负载。不会因为某台服务器负载高宕机而某台服务器闲置的情况。

2:在这中间要考虑高可用问题,所以,采用tomcat集群的方式,我们防止nginx挂掉,多部署一个或多个nginx,实现高可用

3:如果nginx挂掉怎么办,在经过nginx之前用Linux的LVS(Linux虚拟服务器)采用IP负载均衡技术和基于内容请求分发技术进行负载均衡

4:那如果LVS挂掉了怎么办,还有硬件级负载均衡(F5)

5:如果还担心硬件级别的服务器挂掉了,就建立多个机房,在不同地区,考虑到自然灾害,一个机房不可用了,另一个稍远一点的机房就可以工作了,可能会出现响应变慢,这是肯定的。比如一些世界500强,就实施两地三中心。
 

7.redsi-Cluster 集群

应用场景:当哨兵模式下的数据访问过大,大到一台服务器存放不下的情况时,主从模式或sentinel模式就不能满足需求了,这个时候就需要对服务器进行分片,将数据存储到多个redis的 实例中,cluster模式的出现就是为了解决单机Redis容量有限的问题,将Redis的数据根据一定的规则分配到多台机器。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7ciQU7Op-1596442557304)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1596173322255.png)]

cluster可以说是sentinel和主从模式的结合体,通过cluster可以实现主从和master重选功能,所以如果配置两个副本三个分片的话,就需要六个Redis实例。因为Redis的数据是根据一定规则分配到cluster的不同机器的,当数据量过大时,可以新增机器进行扩容

架构特点:

1.所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
2.节点的fail是通过集群中超过半数的master节点检测失效时才生效。
3.客户端与redis节点直连,不需要连接集群所有节点,只需要连接集群中任意可用节点即可。
4.集群把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<>slot<>key关系

7.1 哈希槽分配数据

需要注意的是,这种集群模式下集群中每个节点保存的数据并不是所有的数据,而只是一部分数据。那
么数据是如何合理的分配到不同的节点上的呢?
Redis 集群是采用一种叫做 哈希槽 (hash slot) 的方式来分配数据的。redis cluster 默认分配了
16384 个slot,当我们set一个key 时,会用 CRC16 算法来取模得到所属的 slot ,然后将这个key 分到
哈希槽区间的节点上,具体算法就是: CRC16(key) % 16384 = 0-16383 。

假设现在有3个节点已经组成了集群,分别是:A, B, C 三个节点,它们可以是一台机器上的三个端口,也可以是三台服务器。那么采用哈希槽的方式来存放16384个槽的话,

节点A覆盖0-5460
节点B覆盖5461-10922
节点C覆盖10923-16383
那么,现在要设置一个key ,比如叫 my_name :
按照redis cluster的哈希槽算法: CRC16(‘my_name’)%16384 = 2412 。 那么就会把这个key 的存储分
配到 节点A 上了。

当用户发送请求的时候,连接任意一个节点即可,哈希槽的计算,集群会自动进行计算和重定向到有该数据的节点上,然后返回用户数据

7.2 redis-Cluster的主从模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cB4YothU-1596442557305)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1596174743053.png)]

为了保证集群的高可用,在每个redis节点上都建立一个从节点,从节点会实时复制主节点的数据,当主节点宕机的时候,从节点就会顶上去变成主节点。

8.redis的另一种集群方式:Codis

 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nBh5xeyj-1596442557307)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1596174624018.png)]

简单来说,codis是作为一个中间件存在的,当客户端向codis发送指令时,codis负责将指令转发到后面的redis来执行,并将结果返回给客户端,一个codis实例可以连接多个redis实例,也可以启动多个codis实例来支撑,每个codis节点都是对等的,这样可以增加整体的QPS需求,还能起到容灾功能。

9.redis的持久化技术

Redis提供了将数据定期自动持久化至硬盘的能力,包括RDB和AOF两种方案,两种方案分别有其长处和短板,可以配合起来同时运行,确保数据的稳定性

Redis为持久化提供了两种方式:

1.RDB:在指定的时间间隔能对数据进行快照存储。
2.AOF:每次对服务器写的操作都记录在一个日志文件中,当服务器重启的时候会重新执行这些命令来恢复原始的数据。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Octzs8NO-1596442557309)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1596175563353.png)]

 五、Redis的各种集群方案、及优缺点对比

1.主从模式

redis单节点虽然有通过RDB和AOF持久化机制能将数据持久化到硬盘上,但数据是存储在一台服务器上的,如果服务器出现硬盘故障等问题,会导致数据不可用,而且读写无法分离,读写都在同一台服务器上,请求量大时会出现I/O瓶颈。

为了避免单点故障 和 读写不分离,Redis 提供了复制(replication)功能实现master数据库中的数据更新后,会自动将更新的数据同步到其他slave数据库上。

 

如上redis主从结构特点:一个master可以有多个salve节点;salve节点可以有slave节点,从节点是级联结构。

主从模式优缺点

  • 1.优点: 主从结构具有读写分离,提高效率、数据备份,提供多个副本等优点。
  • 2.不足: 最大的不足就是主从模式不具备自动容错和恢复功能,主节点故障,集群则无法进行工作,可用性比较低,从节点升主节点需要人工手动干预。
#普通的主从模式,当主数据库崩溃时,需要手动切换从数据库成为主数据库:

1.在从数据库中使用SLAVE NO ONE命令将从数据库提升成主数据继续服务。

2.启动之前崩溃的主数据库,然后使用SLAVEOF命令将其设置成新的主数据库的从数据库,即可同步数据

2.哨兵模式

第一种主从同步/复制的模式,当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用,这时候就需要哨兵模式登场了。哨兵模式是从Redis的2.6版本开始提供的,但是当时这个版本的模式是不稳定的,直到Redis的2.8版本以后,这个哨兵模式才稳定下来。

哨兵模式核心还是主从复制,只不过在相对于主从模式在主节点宕机导致不可写的情况下,多了一个竞选机制:从所有的从节点竞选出新的主节点。竞选机制的实现,是依赖于在系统中启动一个sentinel进程。

如上图,哨兵本身也有单点故障的问题,所以在一个一主多从的Redis系统中,可以使用多个哨兵进行监控,哨兵不仅会监控主数据库和从数据库,哨兵之间也会相互监控。每一个哨兵都是一个独立的进程,作为进程,它会独立运行。

(1)哨兵模式的作用

  • 监控所有服务器是否正常运行:通过发送命令返回监控服务器的运行状态,处理监控主服务器、从服务器外,哨兵之间也相互监控。

  • 故障切换:当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换master。同时那台有问题的旧主也会变为新主的从,也就是说当旧的主即使恢复时,并不会恢复原来的主身份,而是作为新主的一个从。

(2)哨兵实现原理

哨兵在启动进程时,会读取配置文件的内容,通过如下的配置找出需要监控的主数据库:

sentinel monitor master-name ip port quorum

#master-name是主数据库的名字

#ip和port 是当前主数据库地址和端口号

#quorum表示在执行故障切换操作前,需要多少哨兵节点同意。

这里之所以只需要连接主节点,是因为通过主节点的info命令,获取从节点信息,从而和从节点也建立连接,同时也能通过主节点的info信息知道新增从节点的信息。

一个哨兵节点可以监控多个主节点,但是并不提倡这么做,因为当哨兵节点崩溃时,同时有多个集群切换会发生故障。 哨兵启动后,会与主数据库建立两条连接。

1.订阅主数据库_sentinel_:hello频道以获取同样监控该数据库的哨兵节点信息

2.定期向主数据库发送info命令,获取主数据库本身的信息。

跟主数据库建立连接后会定时执行以下三个操作:

  • (1)每隔10s向master和 slave发送info命令。作用是获取当前数据库信息,比如发现新增从节点时,会建立连接,并加入到监控列表中,当主从数据库的角色发生变化进行信息更新。

  • (2)每隔2s向主数据里和从数据库的_sentinel_:hello频道发送自己的信息。作用是将自己的监控数据和哨兵分享。每个哨兵会订阅数据库的_sentinel:hello频道,当其他哨兵收到消息后,会判断该哨兵是不是新的哨兵,如果是则将其加入哨兵列表,并建立连接。

  • (3)每隔1s向所有主从节点和所有哨兵节点发送ping命令,作用是监控节点是否存活。

(3)主观下线和客观下线

哨兵节点发送ping命令时,当超过一定时间(down-after-millisecond)后,如果节点未回复,则哨兵认为主观下线。主观下线表示当前哨兵认为该节点已经下面,如果该节点为主数据库,哨兵会进一步判断是够需要对其进行故障切换,这时候就要发送命令(SENTINEL is-master-down-by-addr)询问其他哨兵节点是否认为该主节点是主观下线,当达到指定数量(quorum)时,哨兵就会认为是客观下线。

当主节点客观下线时就需要进行主从切换,主从切换的步骤为:

  • (1)选出领头哨兵。
  • (2)领头哨兵所有的slave选出优先级最高的从数据库。优先级可以通过slave-priority选项设置。
  • (3)如果优先级相同,则从复制的命令偏移量越大(即复制同步数据越多,数据越新),越优先。
  • (4)如果以上条件都一样,则选择run ID较小的从数据库。

选出一个从数据库后,哨兵发送slave no one命令升级为主数据库,并发送slaveof命令将其他从节点的主数据库设置为新的主数据库。

(4)哨兵模式优缺点

#1.优点

哨兵模式是基于主从模式的,解决可主从模式中master故障不可以自动切换故障的问题。

#2.不足-问题

(1)是一种中心化的集群实现方案:始终只有一个Redis主机来接收和处理写请求,写操作受单机瓶颈影响。

(2)集群里所有节点保存的都是全量数据,浪费内存空间,没有真正实现分布式存储。数据量过大时,主从同步严重影响master的性能。

(3)Redis主机宕机后,哨兵模式正在投票选举的情况之外,因为投票选举结束之前,谁也不知道主机和从机是谁,此时Redis也会开启保护机制,禁止写操作,直到选举出了新的Redis主机。

​​​​​​​主从模式或哨兵模式每个节点存储的数据都是全量的数据,数据量过大时,就需要对存储的数据进行分片后存储到多个redis实例上。此时就要用到Redis Sharding技术。

3.各大厂的Redis集群方案

Redis在3.0版本前只支持单实例模式,虽然Redis的开发者Antirez早在博客上就提出在Redis 3.0版本中加入集群的功能,但3.0版本等到2015年才发布正式版。各大企业等不急了,在3.0版本还没发布前为了解决Redis的存储瓶颈,纷纷推出了各自的Redis集群方案。这些方案的核心思想是把数据分片(sharding)存储在多个Redis实例中,每一片就是一个Redis实例。

(1)客户端分片

客户端分片是把分片的逻辑放在Redis客户端实现,(比如:jedis已支持Redis Sharding功能,即ShardedJedis),通过Redis客户端预先定义好的路由规则(使用一致性哈希),把对Key的访问转发到不同的Redis实例中,查询数据时把返回结果汇集。这种方案的模式如图所示。

客户端分片的优缺点:

  • 优点:客户端sharding技术使用hash一致性算法分片的好处是所有的逻辑都是可控的,不依赖于第三方分布式中间件。服务端的Redis实例彼此独立,相互无关联,每个Redis实例像单服务器一样运行,非常容易线性扩展,系统的灵活性很强。开发人员清楚怎么实现分片、路由的规则,不用担心踩坑。

#1.一致性哈希算法:

是分布式系统中常用的算法。比如,一个分布式的存储系统,要将数据存储到具体的节点上,如果采用普通的hash方法,将数据映射到具体的节点上,

如mod(key,d),key是数据的key,d是机器节点数,如果有一个机器加入或退出这个集群,则所有的数据映射都无效了。

一致性哈希算法解决了普通余数Hash算法伸缩性差的问题,可以保证在上线、下线服务器的情况下尽量有多的请求命中原来路由到的服务器。


#2.实现方式:一致性hash算法,比如MURMUR_HASH散列算法、ketamahash算法

比如Jedis的Redis Sharding实现,采用一致性哈希算法(consistent hashing),将key和节点name同时hashing,然后进行映射匹配,采用的算法是MURMUR_HASH。

采用一致性哈希而不是采用简单类似哈希求模映射的主要原因是当增加或减少节点时,不会产生由于重新匹配造成的rehashing。一致性哈希只影响相邻节点key分配,影响量小。
  • 不足:

    • 这是一种静态的分片方案,需要增加或者减少Redis实例的数量,需要手工调整分片的程序。

    • 运维成本比较高,集群的数据出了任何问题都需要运维人员和开发人员一起合作,减缓了解决问题的速度,增加了跨部门沟通的成本。

    • 在不同的客户端程序中,维护相同的路由分片逻辑成本巨大。比如:java项目、PHP项目里共用一套Redis集群,路由分片逻辑分别需要写两套一样的逻辑,以后维护也是两套。

客户端分片有一个最大的问题就是,服务端Redis实例群拓扑结构有变化时,每个客户端都需要更新调整。如果能把客户端分片模块单独拎出来,形成一个单独的模块(中间件),作为客户端 和 服务端连接的桥梁就能解决这个问题了,此时代理分片就出现了。

(2)代理分片

redis代理分片用得最多的就是Twemproxy,由Twitter开源的Redis代理,其基本原理是:通过中间件的形式,Redis客户端把请求发送到Twemproxy,Twemproxy根据路由规则发送到正确的Redis实例,最后Twemproxy把结果汇集返回给客户端。

Twemproxy通过引入一个代理层,将多个Redis实例进行统一管理,使Redis客户端只需要在Twemproxy上进行操作,而不需要关心后面有多少个Redis实例,从而实现了Redis集群。

 

Twemproxy的优点:

  • 客户端像连接Redis实例一样连接Twemproxy,不需要改任何的代码逻辑。

  • 支持无效Redis实例的自动删除。

  • Twemproxy与Redis实例保持连接,减少了客户端与Redis实例的连接数。

Twemproxy的不足:

  • 由于Redis客户端的每个请求都经过Twemproxy代理才能到达Redis服务器,这个过程中会产生性能损失。

  • 没有友好的监控管理后台界面,不利于运维监控。

  • Twemproxy最大的痛点在于,无法平滑地扩容/缩容。对于运维人员来说,当因为业务需要增加Redis实例时工作量非常大。

Twemproxy作为最被广泛使用、最久经考验、稳定性最高的Redis代理,在业界被广泛使用。

(3)Codis

  Twemproxy不能平滑增加Redis实例的问题带来了很大的不便,于是豌豆荚自主研发了Codis,一个支持平滑增加Redis实例的Redis代理软件,其基于Go和C语言开发,并于2014年11月在GitHub上开源 codis开源地址 。

 

在Codis的架构图中,Codis引入了Redis Server Group,其通过指定一个主CodisRedis和一个或多个从CodisRedis,实现了Redis集群的高可用。当一个主CodisRedis挂掉时,Codis不会自动把一个从CodisRedis提升为主CodisRedis,这涉及数据的一致性问题(Redis本身的数据同步是采用主从异步复制,当数据在主CodisRedis写入成功时,从CodisRedis是否已读入这个数据是没法保证的),需要管理员在管理界面上手动把从CodisRedis提升为主CodisRedis。

如果手动处理觉得麻烦,豌豆荚也提供了一个工具Codis-ha,这个工具会在检测到主CodisRedis挂掉的时候将其下线并提升一个从CodisRedis为主CodisRedis。

Codis中采用预分片的形式,启动的时候就创建了1024个slot,1个slot相当于1个箱子,每个箱子有固定的编号,范围是1~1024。slot这个箱子用作存放Key,至于Key存放到哪个箱子,可以通过算法“crc32(key)%1024”获得一个数字,这个数字的范围一定是1~1024之间,Key就放到这个数字对应的slot。例如,如果某个Key通过算法“crc32(key)%1024”得到的数字是5,就放到编码为5的slot(箱子)。1个slot只能放1个Redis Server Group,不能把1个slot放到多个Redis Server Group中。1个Redis Server Group最少可以存放1个slot,最大可以存放1024个slot。因此,Codis中最多可以指定1024个Redis Server Group。

Codis最大的优势在于支持平滑增加(减少)Redis Server Group(Redis实例),能安全、透明地迁移数据,这也是Codis 有别于Twemproxy等静态分布式 Redis 解决方案的地方。Codis增加了Redis Server Group后,就牵涉到slot的迁移问题。例如,系统有两个Redis Server Group,Redis Server Group和slot的对应关系如下。

Redis Server Group slot
1 1~500
2 501~1024

当增加了一个Redis Server Group,slot就要重新分配了。Codis分配slot有两种方法:

  • 第一种:通过Codis管理工具Codisconfig手动重新分配,指定每个Redis Server Group所对应的slot的范围,例如:可以指定Redis Server Group和slot的新的对应关系如下。
Redis Server Group slot
1 1~500
2 501~700
3 701~1024
  • 第二种:通过Codis管理工具Codisconfig的rebalance功能,会自动根据每个Redis Server Group的内存对slot进行迁移,以实现数据的均衡。

4.Redis Cluster

Redis 的哨兵模式虽然已经可以实现高可用,读写分离 ,但是存在几个方面的不足:

  • 哨兵模式下每台 Redis 服务器都存储相同的数据,很浪费内存空间;数据量太大,主从同步时严重影响了master性能。

  • 哨兵模式是中心化的集群实现方案,每个从机和主机的耦合度很高,master宕机到salve选举master恢复期间服务不可用。

  • 哨兵模式始终只有一个Redis主机来接收和处理写请求,写操作还是受单机瓶颈影响,没有实现真正的分布式架构。

redis在3.0上加入了 Cluster 集群模式,实现了 Redis 的分布式存储,也就是说每台 Redis 节点上存储不同的数据。cluster模式为了解决单机Redis容量有限的问题,将数据按一定的规则分配到多台机器,内存/QPS不受限于单机,可受益于分布式集群高扩展性。Redis Cluster是一种服务器Sharding技术(分片和路由都是在服务端实现),采用多主多从,每一个分区都是由一个Redis主机和多个从机组成,片区和片区之间是相互平行的。Redis Cluster集群采用了P2P的模式,完全去中心化。

如上图,官方推荐,集群部署至少要 3 台以上的master节点,最好使用 3 主 3 从六个节点的模式。Redis Cluster集群具有如下几个特点:

  • 集群完全去中心化,采用多主多从;所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。

  • 客户端与 Redis 节点直连,不需要中间代理层。客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。

  • 每一个分区都是由一个Redis主机和多个从机组成,分片和分片之间是相互平行的。

  • 每一个master节点负责维护一部分槽,以及槽所映射的键值数据;集群中每个节点都有全量的槽信息,通过槽每个node都知道具体数据存储到哪个node上。

redis cluster主要是针对海量数据+高并发+高可用的场景,海量数据,如果你的数据量很大,那么建议就用redis cluster,数据量不是很大时,使用sentinel就够了。redis cluster的性能和高可用性均优于哨兵模式。

Redis Cluster采用虚拟哈希槽分区而非一致性hash算法,预先分配一些卡槽,所有的键根据哈希函数映射到这些槽内,每一个分区内的master节点负责维护一部分槽以及槽所映射的键值数据。

猜你喜欢

转载自blog.csdn.net/m0_65260253/article/details/128590828