Redis的介绍及使⽤
redis 简介
简单来说 redis 就是⼀个数据库,不过与传统数据库不同的是 redis 的数据是存在内存中的,所以读写速度⾮常快,因此 redis 被⼴泛应⽤于缓存⽅向。另外,redis 也经常⽤来做分布式锁。redis 提供了多种数据类型来⽀持不同的业务场景。除此之外,redis ⽀持事务、持久化、LUA脚本、LRU驱动事件、多种集⽅案。
为什么要⽤ redis?/为什么要⽤缓存?
主要从“⾼性能”和“⾼并发”这两点来看待这个问题。
⾼性能:
假如⽤户第⼀次访问数据库中的某些数据。这个过程会⽐较慢,因为是从硬盘上读取的。将该⽤户访问的数据存在缓存中,这样下⼀次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可!
⾼并发:
直接操作缓存能够承受的请求是远远⼤于直接访问数据库的,所以我们可以考虑把数据库中的部分数
据转移到缓存中去,这样⽤户的⼀部分请求会直接到缓存这⾥⽽不⽤经过数据库。
为什么要⽤ redis ⽽不⽤ map/guava 做缓存?
缓存分为本地缓存和分布式缓存。以 Java 为例,使⽤⾃带的 map 或者 guava 实现的是本地缓存,最主要的特点是轻量以及快速,⽣命周期随着 jvm 的销毁⽽结束,并且在多实例的情况下,每个实例都需要各⾃保存⼀份缓存,缓存不具有⼀致性。
使⽤ redis 或 memcached 之类的称为分布式缓存,在多实例的情况下,各实例共⽤⼀份缓存数据,缓存具有⼀致性。缺点是需要保持 redis 或 memcached 服务的⾼可⽤,整个程序架构上较为复杂。
redis 的线程模型
redis 内部使⽤⽂件事件处理器 file event handler,这个⽂件事件处理器是单线程的,所以 redis 才叫做单线程的模型。它采⽤ IO 多路复⽤机制同时监听多个 socket,根据 socket 上的事件来选择对应的事件处理器进⾏处理。
⽂件事件处理器的结构包含 4 个部分:
1. 多个 socket
2. IO 多路复⽤程序
3. ⽂件事件分派器
4. 事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)
多个 socket 可能会并发产⽣不同的操作,每个操作对应不同的⽂件事件,但是 IO 多路复⽤程序会监听多个 socket,会将 socket 产⽣的事件放⼊队列中排队,事件分派器每次从队列中取出⼀个事件,把该事件交给对应的事件处理器进⾏处理。
redis 和 memcached 的区别
对于 redis 和 memcached 我总结了下⾯四点。现在公司⼀般都是⽤ redis 来实现缓存,⽽且 redis ⾃⾝也越来越强⼤了!
1、redis⽀持更丰富的数据类型(⽀持更复杂的应⽤场景):Redis不仅仅⽀持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。memcache⽀持简单的数据类型,String。
2、Redis⽀持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进⾏使⽤,⽽Memecache把数据全部存在内存之中。
3、集模式:memcached没有原⽣的集模式,需要依靠客户端来实现往集中分⽚写⼊数据;但是 redis ⽬前是原⽣⽀持 cluster 模式的.
4、Memcached是多线程,⾮阻塞IO复⽤的⽹络模型;Redis使⽤单线程的多路 IO 复⽤模型。
redis 常见数据结构以及使⽤场景分析
1.String
常⽤命令: set,get,decr,incr,mget 等。
String数据结构是简单的key-value类型,value其实不仅可以是String,也可以是数字。常规key-value缓存应⽤;常规计数:微博数,粉丝数等。
2.Hash
常⽤命令: hget,hset,hgetall 等。
hash 是⼀个 string 类型的 field 和 value 的映射表,hash 特别适合⽤于存储对象,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值。⽐如我们可以 hash 数据结构来存储⽤户信息,商品信息等等。⽐如下⾯我就⽤ hash 类型存放了我本⼈的⼀些信息:
key=JavaUser293847
value={
“id”: 1,
“name”: “SnailClimb”,
“age”: 22,
“location”: “Wuhan, Hubei”
}
3.List
常⽤命令: lpush,rpush,lpop,rpop,lrange等
list 就是链表,Redis list 的应⽤场景⾮常多,也是Redis最重要的数据结构之⼀,⽐如微博的关注列表,粉丝列表,消息列表等功能都可以⽤Redis的 list 结构来实现。
Redis list 的实现为⼀个双向链表,即可以⽀持反向查和遍历,更⽅便操作,不过带来了部分额外的内存开销。
另外可以通过 lrange 命令,就是从某个元素开始读取多少个元素,可以基于 list 实现分页查询,这个很棒的⼀个功能,基于 redis 实现简单的⾼性能分页,可以做类似微博那种下拉不断分页的东西(⼀页⼀页的往下⾛),性能⾼。
4.Set
常⽤命令: sadd,spop,smembers,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 结构进⾏存储。
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给删除掉。这就是所谓的惰性删除,也是够懒的哈!
但是仅仅通过设置过期时间还是有问题的。我们想⼀下:如果定期删除漏掉了很多过期 key,然后你也没及时去查,也就没⾛惰性删除,此时会怎么样?如果⼤量过期key堆积在内存⾥,导致redis内存块耗尽了。怎么解决这个问题呢? redis 内存淘汰机制。
redis 内存淘汰机制(MySQL⾥有2000w数据,Redis中只存20w的数据,如何保证Redis中的数据都是
热点数据?)
redis 配置⽂件 f 中有相关注释,我这⾥就不贴了,⼤家可以⾃⾏查阅或者通过这个⽹址查看:dis.io/f
redis支持的五种数据类型 redis 提供 6种数据淘汰策略:
1. volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使⽤的数据淘汰
2. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
3. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
4. allkeys-lru:当内存不⾜以容纳新写⼊数据时,在键空间中,移除最近最少使⽤的key(这个是最常⽤的)
5. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
6. no-eviction:禁⽌驱逐数据,也就是说当内存不⾜以容纳新写⼊数据时,新写⼊操作会报错。这个应该没⼈使⽤吧!
4.0版本后增加以下两种:
7. volatile-lfu:从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使⽤的数据淘汰
8. allkeys-lfu:当内存不⾜以容纳新写⼊数据时,在键空间中,移除最不经常使⽤的key
redis 持久化机制(怎么保证 redis 挂掉之后再重启数据可以进⾏恢复)
很多时候我们需要持久化数据也就是将内存中的数据写⼊到硬盘⾥⾯,⼤部分原因是为了之后重⽤数据(⽐如重启机器、机器故障之后恢复数据),或者是为了防⽌系统故障⽽将数据备份到⼀个远程位置。
Redis不同于Memcached的很重⼀点就是,Redis⽀持持久化,⽽且⽀持两种不同的持久化操作。Redis的⼀种持久化⽅式叫快照
(snapshotting,RDB),另⼀种⽅式是只追加⽂件(append-only file,AOF)。这两种⽅法各有千秋,下⾯我会详细这两种持久化⽅法是什么,怎么⽤,如何选择适合⾃⼰的持久化⽅法。
快照(snapshotting)持久化(RDB)
Redis可以通过创建快照来获得存储在内存⾥⾯的数据在某个时间点上的副本。Redis创建快照之后,可以对快照进⾏备份,可以将快照复制到其他服务器从⽽创建具有相同数据的服务器副本(Redis主从结构,主要⽤来提⾼Redis性能),还可以将快照留在原地以便重启服务器的时候使⽤。
快照持久化是Redis默认采⽤的持久化⽅式,在f配置⽂件中默认有此下配置:
save 900 1 #在900秒(15分钟)之后,如果⾄少有1个key发⽣变化,Redis就会⾃动触发BGSAVE命令创建快照。
save 300 10 #在300秒(5分钟)之后,如果⾄少有10个key发⽣变化,Redis就会⾃动触发BGSAVE命令创建快照。
save 60 10000 #在60秒(1分钟)之后,如果⾄少有10000个key发⽣变化,Redis就会⾃动触发BGSAVE命令创建快照。
AOF(append-only file)持久化
与快照持久化相⽐,AOF持久化的实时性更好,因此已成为主流的持久化⽅案。默认情况下Redis没有开启AOF(append only file)⽅式的持久化,可以通过appendonly参数开启:
appendonly yes
开启AOF持久化后每执⾏⼀条会更改Redis中的数据的命令,Redis就会将该命令写⼊硬盘中的AOF⽂件。AOF⽂件的保存位置和RDB⽂件的位置相同,都是通过dir参数设置的,默认的⽂件名是appendonly.aof。
在Redis的配置⽂件中存在三种不同的 AOF 持久化⽅式,它们分别是:
appendfsync always #每次有数据修改发⽣时都会写⼊AOF⽂件,这样会严重降低Redis的速度
appendfsync everysec #每秒钟同步⼀次,显⽰地将多个写命令同步到硬盘
appendfsync no #让操作系统决定何时进⾏同步
为了兼顾数据和写⼊性能,⽤户可以考虑 appendfsync everysec选项,让Redis每秒同步⼀次AOF⽂件,Redis性能⼏乎没受到任何影响。⽽且这样即使出现系统崩溃,⽤户最多只会丢失⼀秒之内产⽣的数据。当硬盘忙于执⾏写⼊操作的时候,Redis还会优雅的放慢⾃⼰的速度以便适应硬盘的最⼤写⼊速度。
Redis 4.0 对于持久化机制的优化
Redis 4.0 开始⽀持 RDB 和 AOF 的混合持久化(默认关闭,可以通过配置项 aof-use-rdb-preamble 开启)。
如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF ⽂件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF ⾥⾯的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。
补充内容:AOF 重写
AOF重写可以产⽣⼀个新的AOF⽂件,这个新的AOF⽂件和原有的AOF⽂件所保存的数据库状态⼀样,但体积更⼩。
AOF重写是⼀个有歧义的名字,该功能是通过读取数据库中的键值对来实现的,程序⽆须对现有AOF⽂件进⾏任何读⼊、分析或者写⼊操作。
在执⾏ BGREWRITEAOF 命令时,Redis 服务器会维护⼀个 AOF 重写缓冲区,该缓冲区会在⼦进程创建新AOF⽂件期间,记录服务器执⾏的所有写命令。当⼦进程完成创建新AOF⽂件的⼯作之后,服务器会将重写缓冲区中的所有内容追加到新AOF⽂件的末尾,使得新旧两个AOF⽂件所保存的数据库状态⼀致。最后,服务器⽤新的AOF⽂件替换旧的AOF⽂件,以此来完成AOF⽂件重写操作
redis 事务
Redis 通过 MULTI、EXEC、WATCH 等命令来实现事务(transaction)功能。事务提供了⼀种将多个
命令请求打包,然后⼀次性、按顺序地执⾏多个命令的机制,并且在事务执⾏期间,服务器不会中断事务⽽改去执⾏其他客户端的命令请求,它会将事务中的所有命令都执⾏完毕,然后才去处理其他客户端的命令请求。
在传统的关系式数据库中,常常⽤ ACID 性质来检验事务功能的可靠性和安全性。在 Redis 中,事务总是具有原⼦性(Atomicity)、⼀致性(Consistency)和隔离性(Isolation),并且当 Redis 运⾏在某种特定的持久化模式下时,事务也具有持久性(Durability)。
注意:redis同⼀个事务中如果有⼀条命令执⾏失败,其后的命令仍然会被执⾏,没有回滚。
缓存雪崩和缓存穿透问题解决⽅案
缓存雪崩
简介:缓存同⼀时间⼤⾯积的失效,所以,后⾯的请求都会落到数据库上,造成数据库短时间内承受⼤量请求⽽崩掉。
解决办法:
事前:尽量保证整个 redis 集的⾼可⽤性,发现机器宕机尽快补上。选择合适的内存淘汰策略。
事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL崩掉
事后:利⽤ redis 持久化机制保存的数据尽快恢复缓存
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论