![9d1d17dfcd239af8ae6e8ba94e3db40f.png](https://i-blog.csdnimg.cn/blog_migrate/2fbfdb745e4967a98468dbb93bf5bfe7.png)
一、简介
Redis(Remote Dictionary Server ),即远程字典服务。
redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
Redis 是一个高性能的key-value数据库。 redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部 分场合可以对关系数据库起到很好的补充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客户端,使用很方便。 [1]
Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。存盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。
1.数据结构
![ece6a9be1a31e15a74354ae6ddba2a48.png](https://i-blog.csdnimg.cn/blog_migrate/752d6cc11c35eff0fff7160cbe78a1bb.png)
2.为什么要用 Redis /为什么要用缓存
主要从“高性能”和“高并发”这两点来看待这个问题。
- 高性能
假如用户第一次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户访问的数据存在数缓存中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可!
![84987db7d7d1c16f79cc1edebce67082.png](https://i-blog.csdnimg.cn/blog_migrate/743b3ff9d0c540bda1b508b7b1c8b057.png)
- 高并发
直接操作缓存能够承受的请求是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。
![23bf32edd4d6aea48dc3d725cd65377c.png](https://i-blog.csdnimg.cn/blog_migrate/ba4886ee8ef4af87b2b125a5d4f2fa49.png)
3.Redis为什么这么快
1)完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度都是O(1);
2)数据结构简单,对数据操作也简单,Redis 中的数据结构是专门进行设计的;
3)采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
4)使用多路 I/O 复用模型,非阻塞 IO;
5使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis 直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
4.优缺点
- 优点
- 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。
- 数据结构丰富,除了支持string类型的value外还支持hash、set、zset、list等数据结构。
- 支持事务,Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并和的原子性执行
- 读写性能优异,Redis能读的速度是110000次/s,写的速度是81000次/s。
- 支持数据持久化,支持AOF和RDB两种持久化
- 缺点
- 数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上
- Redis 不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
- 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
- Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。
二、持久化
1.什么是Redis持久化?
持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。
2.Redis 的持久化机制是什么?各自的优缺点?
Redis 的持久化机制 RDB(默认)和AOF 机制
- RDB
RDB:是Redis DataBase缩写快照
RDB是Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb。通过配置文件中的save参数来定义快照的周期。
- AOF
AOF:持久化
AOF持久化(即Append Only File持久化),则是将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis会重新将持久化的日志中文件恢复数据。
当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。
优缺点是什么?
AOF文件比RDB更新频率高,优先使用AOF还原数据。
AOF比RDB更安全也更大
RDB性能比AOF好
如果两个都配了优先加载AOF
3.如何选择合适的持久化方式
一般来说, 如果想达到足以媲美PostgreSQL的数据安全性,你应该同时使用两种持久化功能。在这种情况下,当 Redis 重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。
如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失,那么你可以只使用RDB持久化。
有很多用户都只使用AOF持久化,但并不推荐这种方式,因为定时生成RDB快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比AOF恢复的速度要快,除此之外,使用RDB还可以避免AOF程序的bug。
如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式。
三、过期键的删除策略
我们都知道,Redis是key-value数据库,我们可以设置Redis中缓存的key的过期时间。Redis的过期策略就是指当Redis中缓存的key过期了,Redis如何处理。
- 定时过期
每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
对应源码 expire.c/activeExpireCycle 方法
void activeExpireCycle(int type) {
...
do {
...
if (num > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP)
// 选过期键的数量,为 20
num = ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP;
while (num--) {
dictEntry *de;
long long ttl;
// 随机选 20 个过期键
if ((de = dictGetRandomKey(db->expires)) == NULL) break;
...
// 尝试删除过期键
if (activeExpireCycleTryExpire(db,de,now)) expired++;
...
}
...
// 只有过期键比例 < 25% 才跳出循环
} while (expired > ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP/4);
}
...
}
- 惰性过期
只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
对应源码 db.c/expireIfNeeded 方法
int expireIfNeeded(redisDb *db, robj *key) {
// 键未过期返回0
if (!keyIsExpired(db,key)) return 0;
// 如果运行在从节点上,直接返回1,因为从节点不执行删除操作,可以看下面的复制部分
if (server.masterhost != NULL) return 1;
// 运行到这里,表示键带有过期时间且运行在主节点上
// 删除过期键个数
server.stat_expiredkeys++;
// 向从节点和AOF文件传播过期信息
propagateExpire(db,key,server.lazyfree_lazy_expire);
// 发送事件通知
notifyKeyspaceEvent(NOTIFY_EXPIRED,
"expired",key,db->id);
// 根据配置(默认是同步删除)判断是否采用惰性删除(这里的惰性删除是指采用后台线程处理删除操做,这样会减少卡顿)
return server.lazyfree_lazy_expire ? dbAsyncDelete(db,key) :
dbSyncDelete(db,key);
}
- 定期过期
每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
AOF、RDB 和复制功能对过期键的处理
RDB文件
生成 RDB 文件
在执行 save 命令或 bgsave 命令创建一个新的 RDB文件时,程序会对数据库中的键进行检查,已过期的键就不会被保存到新创建的 RDB文件中
载入 RDB 文件
主服务器:载入 RDB 文件时,会对键进行检查,过期的键会被忽略
从服务器:载入 RDB文件时,所有键都会载入。但是会在主从同步的时候,清空从服务器的数据库,所以过期的键载入也不会造成啥影响
AOF文件
AOF 文件写入
当过期键被惰性删除或定期删除后,程序会向 AOF 文件追加一条 del 命令,来显示的记录该键已经被删除
AOF 重写
重启过程会对键进行检查,如果过期就不会被保存到重写后的 AOF 文件中
复制
从服务器的过期键删除动作由主服务器控制
主服务器在删除一个过期键后,会显示地向所有从服务器发送一个 del 命令,告知从服务器删除这个过期键
从服务器收到在执行客户端发送的读命令时,即使碰到过期键也不会将其删除,只有在收到主服务器的 del 命令后,才会删除,这样就能保证主从服务器的数据一致性
四、应用场景
![ea27e7b9bd46124ff4d94de65c971b48.png](https://i-blog.csdnimg.cn/blog_migrate/9a951e399a299fac3f980283ea749bc4.jpeg)