redis

基本内容:redis特点,优点、redis持久化,主从复制、领导者选举算法、redis缓存穿透,雪崩,热点key及解决方案、redis缓存更新策略、分布式锁的几种实现方式db/redis/zk。

redis特点,优点:内存数据库、后端缓存、单线程。加速客户端读写速度、降低后端服务器,数据库服务器的读/写压力。

redis持久化:

    redis是内存数据库,它把数据保存在内存中,这样再加快读取速度的同时也对数据安全性产生了新的问题,即当redis服务器宕机后,内存中的数据都会丢失。为了解决这个问题,redis提供了两种持久化方式(RDB、AOF),将内存中的数据保存到硬盘中。

    (1)RDB方式。

    RDB持久化的时机:满足自动快照规则时、用户显示调用SAVE、BGSAVE命令时、FLUSHALL命令、执行主从复制时。

    RDB方式的持久化通过快照完成,当符号一定条件时redis会自动将内存中的所有数据生成一份副本并存储到硬盘上。

    自动快照原理:redis使用fock()函数复制一份当前进程(父进程)的副本(子进程);父进程继续接受并处理客户端发来的命令,而子进程开始将内存中的数据写入硬盘临时文件;当进程写入完所有数据后会用该临时文件代替旧的RDB文件,至此一次快照操作完成。在执行fork()的时候操作系统采用写时复制机制,子进程共享该父进程的地址空间,当父进程执行写操作时,才会复制父进程地址空间,保证子进程不受影响。所以新的RDB文件存储的是执行fork()那一刻的内存数据。

    (2)AOF方式:开启AOF持久化后每执行一条写命令,redis都会将该命令写入硬盘中的AOF文件。由于操作系统的缓存机制,命令可能并没有真正的同步写入硬盘,而是暂时写入了缓存。操作系统默认30秒执行一次同步操作,将缓存写入硬盘。可以通过配置显示指定同步时机:appendfsync :always/everysec/no。

引入redis集群的原因:从结构上讲,单机架构会发生单点故障,同一台服务器还要承受所有的读/写负载;这就需要为数据生成多个副本并分配在不同的机器上。从容量上讲,单个redis服务器的内存容易成为瓶颈,所以需要进行数据分片,存储的不同的机器上。

redis主从复制:

    通过持久化,redis保证了即使服务器在重启的情况下也不会损失(或少量损失)数据。但是由于数据存储在一台服务器上,如果这台服务器出现硬盘故障等问题,也会导致数据丢失。为了避免单点故障,通常就是将数据库复制多个副本以部署在不同的服务器上。为此,redis提供了复制功能,可以实现一台数据库中的数据更新后,自动将更新同步到其它数据库上。

    主从复制原理:当一个从数据库启动后,回向主数据库发送同步命令SYNC。主数据库接受到SYNC命令后会开始在后台保存快照(即RDB持久化过程),并将保存快照期间接受到的命令缓存起来。当快照完成后,redis会将快照文件和所有缓存的命令发送给从数据库。从数据库接收到后,会载入快照文件并执行收到的缓存的命令。以上过程称为复制初始化。这是一次典型的全量复制过程。复制初始化结束后,主数据库每当收到写命令时就会将该命令同步给从数据库,从而保证主从数据库数据一致。

    无硬盘复制:redis在与从数据库进行复制初始化时不会将快照内容存储到磁盘上,而是直接通过网络发送给从数据库,避免了硬盘的性能瓶颈。

    增量复制原理:如上是一次典型的全量复制过程,若在从数据库断开期间数据变化很少(甚至没有),全量复制不是理想的实现方式。

redis领导者选举算法:主数据库结点挂掉后,在从服务器中选一个作为新的领导者。这这个过程中,每个节点有3种状态(follower、candidate、leader)。

    (1)想成为candidate的结点把票投给自己,成为candidate并向其它结点发出请求。

    (2)一个结点收到投票请求时,若它还没有投过票(投给别人或自己)且申请结点(candidate)标号比自己大,则投票,否则不投。

    (3)若一个结点得到半数以上投票(>n/2)则宣布成为leader。

    (4)若同时有两个condidate均得到了半数票n/2,那么该轮选举失败,每个节点设置一个随机时钟,时间到后再重新发起投票。

redis缓存穿透,缓存雪崩,缓存击穿,及解决方案:

    (1)redis缓存穿透:外部构造大量不合法请求,请求cache拿不到数据,就会去存储层拿,都拿不到时,返回空值(可能会返回大量空值)。或者代码有问题,拿不到数据。就会一直请求数据。导致后端数据库压力上升。

    解决方案:1.缓存空对象:若mysql段也找不到目标数据,则缓存一个空对象并设置过期时间;可能在redis中缓存大量无效key(恶意攻击构造大量无效key)。2.布隆过滤器:用一个位数组维护状态,用多个hash函数进行涂黑,当key再次到来,用每个hash函数计算后对应的位肯定都是黑,于是被过滤,显然可能存在误判。优缺点:用极小的空间实现大量过滤;一定的误判率、无法移除黑名单的某条记录。

    (2)redis雪崩:redis结点挂了,客户端直接请求到数据库里面。数据库负载非常高。甚至数据库拖挂了。

    解决方案:这是典型的单点问题,实际场景一般用集群。

    (3)热点key:在一个key过期的时刻,大量客户端线程同时请求这个key,由于后端服务器用多线程并发的为客户端服务,可能导致瞬间请求数据库多次。

    解决方案:1.基于redis的分布式(互斥)锁,实现只请求一次mysql存储端,类似单例模式(只造一个对象)思路,可以保证强一致性但是可能引发死锁(无限期sleep,目标数据在mysql中不存在)。2.缓存永不过期,即数据在缓存层面没有设置过期时间(没有用expire),业务层面添加逻辑过期时间,发现逻辑过期后,启用单独的线程去请求后端数据库并构建缓存,基本不会有死锁问题,但会有一定的不一致。

//解决方案:1用redis做互斥锁
public String get(key){
  String value=redis.get(key);
  if(value!=null){
     return value;
  }
//缓存无法命中,互斥只访问后端数据库一次
  while(redis.setnx(mutex_key,1)!=1){//自旋尝试获取
         Thread.sleep(3);
  }    
  expire(mutex_key,3*60);//防止线程挂了无法移除--但不可靠--可能某个事物需要执行很长的时间
  value=db.get(key);
  redis.set(key,value,erpire_sec);
  redis.del(mutex_key);
}

redis缓存更新策略:超时剔除与主动更新相结合,最大内存淘汰策略兜底。LRU/LFU/FIFO算法剔除,如maxmemory-policy策略,一致性最差;超时剔除,如expire设置过期时间,一致性较差;主动更新,如当后端数据库数据更新后在业务代码中主动更新缓存(消息订阅),一致性强。 

分布式锁的几种实现方式db/redis/zk:   

    (1)基于数据库表的简单实现:例如要实现对某个方法加锁, 建一张method_lock(id,method,timeStampt)表,对方法名加unique约束,加锁时往数据库插入一条记录,解锁时删除数据库的这条记录。

    问题:无法实现可重入?:加count字段、主机_线程字段;外部原因导致锁无法释放?:开一个清理线程定期清理超时未释放锁;非阻塞式?:搞一个while循环,直到插入成功才退出,在循环体里面sleep;单点不可靠?:弄两个数据库,强一致。

    (2)基于redis缓存实现分布式锁。setnx(key,value):如果键不存在,设置成功返回如果1,失败返回0;如果键不存在,什么也不做,返回0(set if not exist)。expire(key,Timeout),设置键过期时间。del(key),删除一个键。

    问题:通过超时时间来控制锁的失效时间并不是十分的靠谱,非弹性。

其它应用场景:

    用redis实现一小时内最多登录5次:

    用redis实现任务队列:

    用redis实现发布/订阅模式:

参:

redis入门指南、从从Paxos到Zookeeper 分布式一致性原理与实践     

https://www.cnblogs.com/austinspark-jessylu/p/8043726.html

https://blog.csdn.net/u013322876/article/details/78953360

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值