Redis全面解析零:redis从入门到精通

前言

Redis 也是中高级后端工程师技术面试中,面试官最喜欢问的问题之一。特别是那些优秀的、竞争激烈的大型互联网公司,通常要求面试者不仅仅掌握 Redis 基础使用,更要求深层理解 Redis 内部实现的细节原理。毫不夸张地说,能把 Redis 的知识点全部吃透,你的半只脚就已经踏进心仪大公司的技术研发部。

定义

Redis 本质上是一个 Key-Value 类型的内存非关系数据库, 整个数据库加载在内存当中进行操作, 定期通过异步操作把数据库数据 flush 到硬盘上进行保存。

  • Redis 的性能非常出色, 每秒可以处理超过 10 万次读写操作, 是已知性能最快的 Key-Value DB。
  • Redis 最大的魅力是支持保存多种数据结构
  • 单个value 的最大限制是 1GB, 不像 memcached 只能保存 1MB 的数据
  • Redis 也可以对存入的Key-Value 设置 expire 时间
  • Redis 的主要缺点是数据库容量受到物理内存的限制, 不能用作海量数据的高性能读写

Redis和Memcache区别

  • 存储方式:memcache 把数据全部存在内存之中,断电后会挂掉,而redis支持持久化。
  • Redis在存储小数据量时比Memcached性能更高。而在100k以上的数据时,Memcached性能要高于Redis
  • 数据支持类型:redis在数据支持上要比memecache多的多。
  • Memcached本身并不支持分布式,因此只能在客户端通过像一致性哈希这样的分布式算法来实现Memcached的分布式存储。Redis更偏向于在服务器端构建分布式存储

Redis版本新增的一些新特性记录

  • 部分复制(Redis 2.8.18):用来解决主节点在快照同步时大量IO影响效率的问题。
  • Redis在3.0版正式引入了集群这个特性
  • Redis在4.0版正式引入了混合持久化:RDB加AOF混合持久化的方式。
  • Redis 5.0引入的一种新数据类型:Stream数据结构,用来持久化消息队列

Redis 数据类型

Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

①string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB。Redis 常用 SET 和 GET 命令。

②Redis hash 是一个键值(key=>value)对集合。Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。Redis 中每个 hash 可以存储 232 - 1 键值对(40多亿)。命令示例:HMSET runoob field1 "Hello" field2 "World"     HGET runoob field1

③Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。命令示例:  lpush runoob redis       lrange runoob 0 10

④Redis 的 Set 是 string 类型的无序集合。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。命令示例:sadd runoob redis       smembers runoob

⑤Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。命令示例:  zadd runoob 0 redis      ZRANGEBYSCORE runoob 0 1000

Redis 事务

一个事务从开始到执行会经历以下三个阶段:先以 MULTI 开始一个事务, 然后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的所有命令

①开始事务:MULTI命令的执行标志着事务的开始,MULTI命令可以将执行该命令的客户端从非事务状态切换至事务状态。

127.0.0.1:6379> MULTI
OK

② 命令入队:事务队列是一个以先进先出(FIFO)的方式保存入队的命令,较先入队的命令会被放到数组的前面,而较后入队的命令则会被放到数组的后面

127.0.0.1:6379>> MULTI
OK

127.0.0.1:6379>> set name Redis;
QUEUED

127.0.0.1:6379>> get name;
QUEUED

127.0.0.1:6379>> set author "Peter Seibei";
QUEUED

127.0.0.1:6379>> get author;
QUEUED

③ 执行事务:

当一个处于事务状态的客户端向服务器发送EXEC命令时,这个EXEC命令将立即被服务器执行。服务器会遍历这个客户端的事务队列,执行队列中保存的所有的命令,最后将执行命令所得的结果返回给客户端。对于上例子:

127.0.0.1:6379> EXEC
1) OK
2) "Redis"
3) OK
4) "Peter Seibei"

 注:redis事务特性

事务在执行过程中不会被中断,当事务队列中的所有命令都被执行完毕之后,事务
才会结束。

②Redis事务不支持回滚机制,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。

③在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

不支持回滚的理由:

1、redis认为,失败都是由命令使用不当造成

2、redis这样做,是为了保持内部实现简单快速

3、redis还认为,回滚并不能解决所有问题

redis缓存雪崩和穿透 

缓存雪崩:可能是因为数据未加载到缓存中,或者缓存同一时间大面积的失效,从而导致所有请求都去查数据库,导致数据库CPU和内存负载过高,甚至宕机。解决思路:

  • 加锁计数(即限制并发的数量,可以用semphore)或者起一定数量的队列来避免缓存失效时大量请求并发到数据库。但这种方式会降低吞吐量。
  • 分析用户行为,然后失效时间均匀分布。或者在失效时间的基础上再加1~5分钟的随机数。
  • 如果是某台缓存服务器宕机,则考虑做主备。

缓存穿透:指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库中查询。解决思路:

  • 如果查询数据库也为空,直接设置一个默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库。设置一个过期时间或者当有值的时候将缓存中的值替换掉即可。
  • 可以给key设置一些格式规则,然后查询之前先过滤掉不符合规则的Key。

缓存并发:如果网站并发访问高,一个缓存如果失效,可能出现多个进程同时查询DB,同时设置缓存的情况,如果并发确实很大,这也可能造成DB压力过大,还有缓存频繁更新的问题。解决思路:

  • 对缓存查询加锁,如果KEY不存在,就加锁,然后查DB入缓存,然后解锁;其他进程如果发现有锁就等待,然后等解锁后返回数据或者进入DB查询。

redis如何保障数据热点-数据淘汰策略

场景:

数据库中有1000w的数据,而redis中只有50w数据,如何保证redis中10w数据都是热点数据?

方案1:

 限定 Redis 占用的内存,Redis 会根据自身数据淘汰策略,留下热数据到内存。所以,计算一下 50W 数据大约占用的内存,然后设置一下 Redis 内存限制即可,并将淘汰策略为volatile-lru或者allkeys-lru。  

redis 内存数据集大小上升到一定大小的时候,会施行数据淘汰策略。redis 提供 6种数据淘汰策略:

  • volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
  • volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
  • volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
  • allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
  • allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
  • no-enviction(驱逐):禁止驱逐数据

方案2:

只是把Redis当缓存来用.提供一种简单实现缓存失效的思路: LRU(最近少用的淘汰)即redis的缓存每命中一次,就给命中的缓存增加一定ttl(过期时间)(根据具体情况来设定, 比如10分钟).一段时间后, 热数据的ttl都会较大, 不会自动失效, 而冷数据基本上过了设定的ttl就马上失效了.

redis缓存与数据库一致性解决方案

一致性解决方式

  • 缓存双淘汰,传统的玩法在进行写操作的时候,先淘汰cache再写主库。在主从同步时间窗口之内可能有脏数据入cache,此时如果再发起一个异步的淘汰,即使不一致时间窗内脏数据入了cache,也会再次淘汰掉。
  • 使用中间件,业务层不直接访问数据库,而是通过中间件访问数据库,这个中间件会记录哪一些key上发生了写请求,在数据主从同步时间窗口之内,如果key上又出了读请求,就将这个请求也路由到主库上去,使用这个方法来保证数据的一致性。
  • 使用队列,创建几个队列如20个,根据key值去做hash值,然后对队列量进行取模决定放到哪个队列中。先把它放进队列里,当有数据更新请求时,先看下缓存有没有数据,如果没有再去队列中查看是否有相同的key在做更新,如果有也把查询的请求放到队列中,然后同步等待缓存更新成功。当更新完时从队列里去除。

常见问题:

查看Redis使用情况及状态信息用什么命令?

info

Jedis与Redisson对比有什么优缺点?

Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持;Redisson是一个高级的分布式协调Redis客服端,Redisson实现了分布式和可扩展的Java数据结构,和Jedis相比,功能较为简单,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。Redisson的宗旨是促进使用者对Redis的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。

 

文章参考:

https://www.toutiao.com/a6747926805744730627/

 

 

 

 

 

 

 

只是把Redis当缓存来用.提供一种简单实现缓存失效的思路: LRU(最近少用的淘汰)即redis的缓存每命中一次,就给命中的缓存增加一定ttl(过期时间)(根据具体情况来设定, 比如10分钟).

一段时间后, 热数据的ttl都会较大, 不会自动失效, 而冷数据基本上过了设定的ttl就马上失效了.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章