目录
Redis简介
Redis是完全开源免费的,遵守BSD协议,是当前最热门的NoSql数据库之一。Redis是单进程单线程的,线程安全,Redis运行在内存中,所以读写数据的效率极高,远远超过数据库。
Redis支持事务,操作是原子性的,原子性就是对数据的更改要么全部执行,要么全部不执行。
Redis跟memcache不同的是,储存在Redis中的数据是持久化的,断电或重启后,数据也不会丢失。因为Redis的存储分为内存存储、磁盘存储和log文件三部分,重启后,Redis可以从磁盘重新将数据加载到内存中,这些可以通过配置文件对其进行配置,正因为这样,Redis才能实现持久化。
Redis支持主从模式,可以配置集群,这样更利于支撑起大型的项目,这也是它的一大亮点。
常用有5种数据类型
- String
value可以是String也可以是数字 - list
使用List的数据结构,可以做简单的消息队列的功能。另外还有一个就是,可以利用lrange命令,做基于redis的分页功能,性能极佳,用户体验好。list可以很好的完成排队,先进先出的原则。 - hash(key--filed--value)
这里value存放的是结构化的对象,比较方便的就是操作其中的某个字段。在做单点登录的时候,就是用这种数据结构存储用户信息,以cookieId作为key,设置30分钟为缓存过期时间,能很好的模拟出类似session的效果。 - set
因为set堆放的是一堆不重复值的集合。所以可以做全局去重的功能。利用交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能 - zset(有序集合)
redis的持久化
redis有两种持久化的方式:快照(RDB文件)和追加式文件(AOF文件)
RDB是Redis默认的持久化方式。按照一定的时间周期策略把内存的数据以快照的形式保存到硬盘的二进制文件。即Snapshot快照存储,对应产生的数据文件为dump.rdb,通过配置文件中的save参数来定义快照的周期。
AOF:Redis会将每一个收到的写命令都通过Write函数追加到文件最后,类似于MySQL的binlog。当Redis重启是会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。 当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。
- RDB持久化方式会在一个特定的间隔保存那个时间点的一个数据快照
- AOF持久化方式则会记录每个服务器收到的写操作,在服务启动时,这些记录的操作会逐条执行从而重建出原来的数据
- 在数据量比较大的情况下,RDB的启动速度更快,RDB恢复数据的速度要比AOF快很多
- RDB容易造成数据的丢失,假设每五分钟保存一次快照,如果redis因为某些原因不能正常工作,那么上次产生快照到redis出现问题这段时间的数据就会丢失了
- AOF比RDB可靠,可以指定不同的fsync策略: 不进行fsync,每秒fsync一次和每次查询进行fsync. 默认是每秒fsync一次,这表示你最多丢失一秒钟的数据
- 选择方案: 如不能承受数分钟以内的数据丢失,对业务数据非常敏感,选用AOF,如能承受数分钟以内的数据丢失,且追求大数据集的恢复速度,选用RDB,灾难恢复选用RDB。
- 在工作中我们一般会AOF和RDB一起用,然后使用AOF来来重新构建数据,因为AOF中的数据更加完整。
怎么更改Redis的持久化策略?
RDB是默认打开的,一般我们不用去配置
AOP默认是关闭的,需要我们自己打开,
找到Redis的配置文件 .conf 文件,搜索 appendonly ,然后把后面的no 改成yes,就开启了
会自动生成aof文件。
Redis事务
概念: redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。
事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
Redis事务保证原子性吗,支持回滚吗?
Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。
java RedisTemplete怎么使用事务
RedisTemplate的事务需要自己实现一个SessionCallBack来做事务,所以要这么写
SessionCallback sessionCallback = new SessionCallback() {
@Override
public Object execute(RedisOperations redisOperations) throws DataAccessException {
redisOperations.multi();
// TODO: 2017/11/20 命令1
// TODO: 2017/11/20 命令2
// TODO: 2017/11/20 命令3
return redisOperations.exec();
}
};
redisTemplate.execute(sessionCallback);
Redis集群方案
单机Redis的读写速度非常快,能够支持大量用户的访问。虽然Redis的性能很高,但是对于大型网站来说,每秒需要获取的数据远远超过单台redis服务所能承受的压力,所以就需要一种方案能够解决单台Redis服务性能不足的问题,这就需要使用到Redis的集群了。Redis集群有多种方案,下面分别进行讲解。
1.主从复制Replication
redis支持主从复制的模式。
在主从复制模式下Redis节点分为两种角色:主节点(也称为master)和从节点(也称为slave)。这种模式集群是由一个主节点和多个从节点构成。
原则:Master会将数据同步到slave,而slave不会将数据同步到master。Slave启动时会连接master来同步数据。
这是一个典型的分布式读写分离模型。我们可以利用master来处理写操作,slave提供读操作。这样可以有效减少单个机器的并发访问数量。
实现方式:
要实现主从复制这种模式非常简单,主节点不用做任何修改,直接启动服务即可。从节点需要修改redis.conf配置文件,加入配置:slaveof 主节点ip地址 主节点端口号,例如master的ip地址为192.168.200.129,端口号为6379,那么slave只需要在redis.conf文件中配置slaveof 192.168.200.129 6379即可。
分别连接主节点和从节点,测试主节点的写操作,从节点立刻就能看到相同的数据。但是在从节点进行写操作,提示 READONLY You can't write against a read only slave 不能写数据到从节点。
现在我们就可以通过这种方式配置多个从节点进行读操作,主节点进行写操作,实现读写分离。
2. 哨兵sentinel
出现的原因:在主从复制这种模式下只有一个主节点,一旦主节点宕机,就无法再进行写操作了。也就是说主从复制这种模式没有实现高可用。
高可用介绍:high Available
高可用(HA)是分布式系统架构设计中必须考虑的因素之一,它是通过架构设计减少系统不能提供服务的时间。保证高可用通常遵循下面几点:
- 单点是系统高可用的大敌,应该尽量在系统设计的过程中避免单点。
- 通过架构设计而保证系统高可用的,其核心准则是:冗余。
- 实现自动故障转移。
Redis sentinel介绍
sentinel(哨兵)是用于监控redis集群中Master状态的工具,其本身也是一个独立运行的进程,是Redis 的高可用解决方案,sentinel哨兵模式已经被集成在redis2.4之后的版本中。
sentinel可以监视一个或者多个redis master服务,以及这些master服务的所有从服务;当某个master服务下线时,自动将该master下的某个从服务升级为master服务替代已下线的master服务继续处理请求,并且其余从节点开始从新的主节点复制数据。
在redis安装完成后,会有一个redis-sentinel的文件,这就是启动sentinel的脚本文件,同时还有一个sentinel.conf文件,这个是sentinel的配置文件。
哨兵至少需要 3 个实例,来保证自己的健壮性。
哨兵 + redis 主从的部署架构,是不保证数据零丢失的,只能保证 redis 集群的高可用性。
3.内置集群cluster
Redis cluster介绍
Redis Cluster是Redis的内置集群,在Redis3.0推出的实现方案。Redis Cluster是无中心节点的集群架构,依靠Gossip协议协同自动化修复集群的状态。
Redis cluster在设计的时候,就考虑到了去中心化,去中间件,也就是说,集群中的每个节点都是平等的关系,都是对等的,每个节点都保存各自的数据和整个集群的状态。每个节点都和其他所有节点连接,而且这些连接保持活跃,这样就保证了我们只需要连接集群中的任意一个节点,就可以获取到其他节点的数据。
哈希槽方式分配数据
这种集群模式下集群中每个节点保存的数据并不是所有的数据,而只是一部分数据。
Redis 集群是采用一种叫做哈希槽 (hash slot)的方式来分配数据的。redis cluster 默认分配了 16384 个槽位,当我们set一个key 时,会用CRC16算法来取模得到所属的slot,然后将这个key 分到哈希槽区间的节点上,具体算法就是:CRC16(key) % 16384。
cluster的主从模式
redis cluster 为了保证数据的高可用性,加入了主从模式,一个主节点对应一个或多个从节点,主节点提供数据存取,从节点则是从主节点拉取数据备份,当这个主节点挂掉后,就会在这些从节点中选取一个来充当主节点,从而保证集群不会挂掉。
为了保证可以进行投票,需要至少3个主节点。
每个主节点都需要至少一个从节点,所以需要至少3个从节点。
模型:比如说搭建3个主节点,这三个主节点都存着一部分的数据,然后每个主节点都有三个从节点,如果一个主节点挂掉了,那么它的三个从节点就开始选举,然后生成一个新的节点,保证集群的高可用。数据的话从节点会从主节点拉取数据,所以也不会有数据丢失。
优点
- 无中心架构,支持动态扩容,对业务透明
- 具备Sentinel的监控和自动Failover(故障转移)能力
- 客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
- 高性能,客户端直连redis服务,免去了proxy代理的损耗
缺点
- 运维也很复杂,数据迁移需要人工干预
- 只能使用0号数据库(Redis集群目前无法做数据库选择,默认在0数据库。)
- 不支持批量操作(pipeline管道操作)
- 分布式逻辑和存储模块耦合等
redis做缓存的一些问题
1.缓存穿透
- 缓存穿透是指查询一个数据库一定不存在的数据,大量线程访问这个数据,redis中有没有,就会直接都去数据库中查,就会增大数据库压力导致数据库崩溃。
- 解决方案:
如果从数据库查询的对象为空,也将它放入缓存,查询的值作为key,vulue设为null,存到redis中,设置一个过期时间,这样用户频繁查询这个数据就会从redis中查到null返回,不会去查询数据库。
2.缓存雪崩
- 缓存雪崩是指在某个时间段,缓存集中过期失效,这这个时间段大量的查询就会同时去数据库查询,导致数据库雪崩。
- 解决方案
我们可以将缓存的数据设置不同的失效时间,就可以避免缓存数据在某个时段大量失效。
3.缓存击穿
- 缓存击穿,是指一个key非常热点(例如双十一期间进行抢购的商品数据),在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在redis失效的瞬间,持续的大并发就穿破缓存,直接请求到数据库上,导致数据库崩溃.(还有一种情况是大量的请求直接将redis服务器搞崩溃)。
- 解决方案
- 我们同样可以将这些热点数据设置永不过期就可以解决缓存击穿的问题了。
- 再设立一个redis服务器,专门用来存放热点数据,比如秒杀的商品数据。
4.缓存预热
- 缓存预热 就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
- 解决方案
- 直接写个缓存刷新页面,上线时手工操作一下;
- 数据量不大,可以在项目启动的时候自动进行加载;
- 定时刷新缓存;
- 缓存热点key
- 问题:缓存中的一个Key(比如一个促销商品),在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
- 解决方案
对缓存查询加锁,如果KEY不存在,就加锁,然后查DB入缓存,然后解锁;其他进程如果发现有锁就等待,然后等解锁后返回数据或者进入DB查询.
5.缓存降级
当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。 降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。 以参考日志级别设置预案:
(1)一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级; (2)警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
(3)错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
(4)严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。
服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。
redis和数据库怎么保持数据一致性
延时双删
我们会先去 redis 中判断数据是否存在,如果存在,则直接返回缓存好的数据。而如果不存在的话,就会去数据库中,读取数据,并把数据缓存到 Redis 中。适用场合:如果数据量比较大,但不是经常更新的情况(比如用户排行)
只要使用了缓存就涉及到缓存同步的问题。缓存同步其实就是当缓存的信息发生变化,也就是对后台对缓存涉及到的数据进行增、删、改操作后,数据库中的数据发生了变化同时要把缓存中 的数据对应删除即可。当页面再次请求数据时,缓存中不能命中就会从数据库中查询并且添加到缓存中,即实现了缓存同步。
做法:要执行更新一个key操作时,我们首先redis.del(key),然后db.update(key),之后我们睡眠1s,再执行一次redis.del(key),这样就能保证数据一致性。睡眠的时间需要我们评估修改要用的时间,在此基础上我们增加1s,目的是确保修改的事务已经提交了。