Redis

1、redis简介

简单来说redis就是一个数据库,不过与传统数据库不同的是redis的数据是存在内存中,所以读写的速度非常快,因此redis被广泛的应用于缓存方向,另外,redis也经常用来做分布式锁。redis提供了多种数据类型来支持不同的业务场景。

2、redis和memcached的区别

2.1、redis支持更丰富的数据类型:redis不仅仅支持简单的K/V类型的数据,同时还提供list,set,zset,hash等数据结构的存储。memchache支持简单的数据类型:String

2.2、Redis支持数据持久化,可以将内存的数据保存在磁盘中,重启的时候可以再次加载进行使用,而Memecache把数据全部存在内从中。

2.3、集群模式:memcached没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但redis目前是原生支持cluster模式的。

2.4、Memcached是多线程,非租塞IO复用的网络模式;Redis使用单线程多路IO复用模式

3、redis常用数据结构以及使用场景分析

3.1、String

常用命令:set,get,decr,incr,mget等

String数据结构树简单的key-value类型,value其实不仅可以是String,也可以是数字。常规key-value缓存应用;常规计数:微博树,粉丝数等

3.2、Hash

常用命令:hget,hset,hgetall等

hash是一个string类型的field和value的映射变,hash特别适合用于存储对象,后续操作的时候,可以直接仅仅修改这个对象中某个字段的值。比如我们可以使用hash数据结构来存储用户信息,商品信息等;例如使用hash类型存放个人的一些信息:

key=JavaUser293847

value={

“id”:1,

“name”:"jack",

“age”:22

“address”:“ankang,shanxi”

}

3.3、

常用命令:lpush,rpush,lpop,rpop,lrange等

list就是链表,Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如微博的关注列表,粉丝列表,消息列表等功能都可以用Redis的list结构来实现。

Redis list的实现为一个双向链表,既可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。

另外可以通过lrange命令,就是从某个元素开始读取多少个元素,可以基于list实现分页查询,这是个很棒的功能,基于redis实现简单的高性能分页,可以做类似微博那种下拉不断分页的东西(一页一页的往下走),性能高

4、Set

常用命令:sadd,spop,smember,sunion等

set对外提供的功能与list类似是一个列表的功能,特殊之处在于set可以自动排重的。

当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。可以基于set轻易的实现交集、并集、差集的操作。

例如:在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有的粉丝存在一个集合。Redis可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能。这个过程也是求交集的过程,具体命令如下:

sinterstore key1 key2 key3 将交集存在key1中

5、Sorted set 常用命令

常用命令:zadd,zrange,zrem,zcard等

和set相比,sorted set增加了一个权重参数score,使得集合中的元素能够按照score进行有序排列

例如:在直播系统中,实时排行信息包含直播在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息,适合使用Redis中的Sorted Set结构进行存储

5、redis 设置过期时间

Redis中有个设置时间过期的功能,即对存储在 redis 数据库中的值可以设置一个过期时间。作为一个缓存数据库,这是非常实用的。如我们一般项目中的 token 或者一些登录信息,尤其是短信验证码都是有时间限制的,按照传统的数据库处理方式,一般都是自己判断过期,这样无疑会严重影响项目性能。

我们 set key 的时候,都可以给一个 expire time,就是过期时间,通过过期时间我们可以指定这个 key 可以存活的时间。

如果假设你设置了一批 key 只能存活1个小时,那么接下来1小时后,redis是怎么对这批key进行删除的?

  • 定期删除:redis默认是每隔 100ms 就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。注意这里是随机抽取的。为什么要随机呢?你想一想假如 redis 存了几十万个 key ,每隔100ms就遍历所有的设置过期时间的 key 的话,就会给 CPU 带来很大的负载!
  • 惰性删除 :定期删除可能会导致很多过期 key 到了时间并没有被删除掉。所以就有了惰性删除。假如你的过期 key,靠定期删除没有被删除掉,还停留在内存里,除非你的系统去查一下那个 key,才会被redis给删除掉。这就是所谓的惰性删除,也是够懒的哈!

6、redis 持久化机制(怎么保证 redis 挂掉之后再重启数据可以进行恢复)

6.1、一种持久化方式叫快照(snapshotting,RDB)

6.2、另一种方式是只追加文件(append-only file,AOF)

7、缓存雪崩和缓存穿透问题解决方案

缓存雪崩

什么是缓存雪崩?

简介:缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。

有哪些解决办法?

        事前:尽量保证整个 redis 集群的高可用性,发现机器宕机尽快补上。选择合适的内存淘汰策略。

        事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL崩掉

        事后:利用 redis 持久化机制保存的数据尽快恢复缓存

缓存穿透

什么是缓存穿透?

缓存穿透说简单点就是大量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层。举个例子:某个黑客故意制造我们缓存中不存在的 key 发起大量请求,导致大量请求落到数据库。下面用图片展示一下(这两张图片不是我画的,为了省事直接在网上找的,这里说明一下):

有哪些解决办法?

最基本的就是首先做好参数校验,一些不合法的参数请求直接抛出异常信息返回给客户端。比如查询的数据库 id 不能小于 0、传入的邮箱格式不对的时候直接返回错误消息给客户端等等。

1)缓存无效 key : 如果缓存和数据库都查不到某个 key 的数据就写一个到 redis 中去并设置过期时间,具体命令如下:SET key value EX 10086。这种方式可以解决请求的 key 变化不频繁的情况,如果黑客恶意攻击,每次构建不同的请求key,会导致 redis 中缓存大量无效的 key 。很明显,这种方案并不能从根本上解决此问题。如果非要用这种方式来解决穿透问题的话,尽量将无效的 key 的过期时间设置短一点比如 1 分钟。

2)布隆过滤器:布隆过滤器是一个非常神奇的数据结构,通过它我们可以非常方便地判断一个给定数据是否存在与海量数据中。我们需要的就是判断 key 是否合法,有没有感觉布隆过滤器就是我们想要找的那个“人”。具体是这样做的:把所有可能存在的请求的值都存放在布隆过滤器中,当用户请求过来,我会先判断用户发来的请求的值是否存在于布隆过滤器中。不存在的话,直接返回请求参数错误信息给客户端,存在的话才会走下面的流程。总结一下就是下面这张图(这张图片不是我画的,为了省事直接在网上找的):

如何对Redis进行原子操作

什么时候需要进行需要原子操作?

很常见的例子,就是利用Redis实现分布式锁。

实现锁需要哪些条件?

我们知道要实现锁,就需要一个改变锁状态的方法。这个方法能原子地对锁的状态进行检查并修改。如果修改成功,则意味着获得了锁。对于硬件,它提供的就是test-and-set,compare-and-swap等原语。

Redis有没有提供类似的原语呢?

有的。Redis有提供setnx(),它会提供这样的原子操作:如果key没有值,则将值设置进去,如果已有值就不做处理,提示失败。

这样就可以基于这个原语来实现锁,简单原理就是:key就是对应的锁,如果key有值就说明锁被占用。删除值代表释放锁。如果插入值成功,则代表获得锁。再加上过期时间,基本就可以满足分布式锁的需求了。

除了锁,还有哪些地方需要原子操作?

假如我们在操作Redis数据的时候,需要判断Redis中某个值是否满足条件,只有满足条件才做这个操作

我随便举个例子,例如:如果key-xxx的值不为0,则加1,如果为0,则删除。

这种情况Redis可以处理吗?

可以,Lua脚本。Redis支持Lua脚本。针对上面的问题,我们只要写这样的Lua脚本就可以了。

local a = redis.call('get', 'xxx') //调用redis的get方法,key为'xxx'
if(tonumber(a) > 0) then  //redis都是以String进行存储的,需要转型
    redis.call('incr', 'xxx') //调用redis的incr方法,key为'xxx'
    return 'OK'
else
    return 'FAIL'

为什么Lua脚本可以实现原子操作, 看不出来它有用锁啊?

这与Redis的请求处理有关。Redis只用一个线程来处理客户端的请求。所以在执行lua脚本的时候,没有其他客户端的请求在处理。所以在lua脚本中的对redis数据的修改操作就是原子的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值