一. memcache和Redis的区别
二. 为什么Redis那么快?
- 完全基于内存,绝对部分请求是纯粹的内存操作。
- 数据结构结构简单,对数据操作也简单。不使用表,不需要对多个表进行关联,存储结构是键值对,查找速度比较快.
- redis采用单线程(并不是说redis server也是单线程!), 天然的不会出现同步问题,锁竞争,线程切换等额外开销。而之所以使用单线程是因为,cpu并不是限制redis性能的瓶颈,真正的瓶颈依然是磁盘,网路的IO带宽。并且,又多线程需求时,可以开启多个redis来完成对应的需求。
- 使用多路IO复用模型,非阻塞IO
三. 数据类型
数据类型 | 可以存储的值 | 操作 |
---|---|---|
STRING | 字符串、整数或者浮点数 | 对整个字符串或者字符串的其中一部分执行操作 对整数和浮点数执行自增或者自减操作 |
LIST | 列表 | 从两端压入或者弹出元素 对单个或者多个元素进行修剪, 只保留一个范围内的元素 |
SET | 无序集合 | 添加、获取、移除单个元素 检查一个元素是否存在于集合中 计算交集、并集、差集 从集合里面随机获取元素 |
HASH | 包含键值对的无序散列表 | 添加、获取、移除单个键值对 获取所有键值对 检查某个键是否存在 |
ZSET | 有序集合 | 添加、获取、删除元素 根据分值范围或者成员来获取元素 计算一个键的排名 |
四. Redis的使用场景
4.1. 从海量Key里查询出某一固定前缀的Key
kyes pattern: 查找所有符合给定模式pattern的key
- 一次性返回所有匹配的key
- 键的数量过大会使服务卡顿
可以使用scan cursor [Match pattern][COUNT count]
:
- 基于游标的迭代器,需要基于上一次的游标延续之前的迭代过程。
- 以0作为游标开始一次新的迭代,直到命令返回游标0完成一次遍历。
- 不保证每次执行都返回给定数量的元素(count数大于key总数的时候)一次返回数量不可控,只能是大概率符合count。
- 支持模糊查询,即能够返回满足pattern匹配的key
例如:scan 0 match k1* count 10
,注意,返回的值有可能的是重复的! 因此需要去重!(例如写程序的时候用hashset)
blpop
4.2. 如何通过Redis实现分布式锁
即分布式系统中,访问共同资源时的一种锁的实现。
在分布式场景下,无法使用单机环境下的锁来对多个节点上的进程进行同步。
可以使用 Redis 自带的 SETNX 命令实现分布式锁,setnx key val 就是如果不存在key的话,那么就设置key为val。设置成功返回1, 失败返回0
SET lock_key random_value NX PX 5000
一定要放到一个语句里,保证“获取锁”和“设置超时时间”的原子性。如果设置完setnx以后,程序就挂掉了,那么这个key(锁)就一直被占用!
4.3. 使用Redis做异步队列
除了使用list的rpush, lpop, blpop 以外,可以用pub/sub:主题订阅者模式,来做。
例子:
- 订阅一个频道:
- redis-cli1:
subscribe myTopic
,之后进入监听状态 - redis-cli2:
subscribe myTopic
,之后进入监听状态 - redis-cli3:
subscribe myTopic
,之后进入监听状态 - redis-cli4:
publish myTopic "hello!!"
:- 这条消息发送出去之后,监听myTopic的3个客户端都收到了“hello!!”这个字符串。
- redis-cli1:
缺点:
但需要注意的是,消息的发布是无状态的,也就是无法保证可达。对于发布者来说,消息是即发即失的。 若想解决这个问题,需要用专业的消息队列,如kafka,rocketmq等。
五、Redis如何做持久化
5.1. RDB ( 快照 ) 持久化:保存某一个时间点的全数据快照.
redis服务器加载时,会启用reids.conf文件中的配置信息,里面的:
....
save 900 1 # 就是900秒内如果有1条是写入指令,那么就触发一次快照
save 300 10
save 60 10000
...
stop-writes-on-bgsave-error yes #当设置成yes,
# 就是备份进程若出错了,则主进程就停止
# 接受新的写入操作, 这是为了保证数据一致性!
....
可以根据不同的情况来合理配置。 src
目录下的dump.rdb
文件,就是redis系统定期备份的rdb文件. 它是一个二进制文件。
5.1.1 生成RDB备份文件的方式:
-
主动生成
- SAVE: 阻塞Redis的服务器进程,直到RDB文件被创建完毕。 很少被使用,因为占用了主线程!! 主线程是用来处理client的请求的!!
- BGSAVE:Fork一个子进程创建RBD文件,不阻塞服务器进程!此时,主进程依然继续工作,子进程将内存中的数据写入临时文件中,因为copy-on-write的机制,父子进程此时会共享相同的物理页面,当(主)父进程处理写请求时,os会为父进程要写的页面创建一个副本(这个副本用于备份),而不是写入共享的页面! RDB文件的载入,一般情况下是自动的,redis服务器启动时,若检测到rdb文件的存在,那么会载入这个文件
在fork时,子进程和父进程共享同一块资源空间,只有当父进程对此空间进行修改时,才会触发给子进程的资源复制,这一机制为copy-on-write. ,juc的copyOnWriteArrayList也是使用的这一原理。
-
被动生成
- 根据redis.conf里的save m n 定时触发 (用的是BGSAVE)
- 主从复制时,主节点自动触发(主节点发送rdb文件给从结点,这时,主节点会触发一次!)
- 执行debug reload
- 执行shutdown且没有开启AOF持久化,那么会触发一次RDB持久化
RDB持久化的缺点:
- 内存数据的全部同步! 数据量大的时候会因为IO而严重影响性能!
- 可能会因为redis挂掉而丢失从当前到最近一次备份期间的所有数据!
5.2 AOF ( Append-Only-File )持久化:保存写状态
- 记录下除了查询以外的所有变更数据库状态的指令
- 以append的形式追加保存到AOF文件中
- AOF持久化默认是关闭的,可以修改redis.conf来让其生效:
- appendonly yes # 启动 aof
- appendfsync everysec/always/no:
- always: 一旦缓存区发生改变,就立刻将内容写到文件中!
- everysec: 每隔1s,写入一次
- no: 什么时候写交给os判断, 一般是等缓存区写满了就写入一次。
日志重写解决AOF文件大小不断增大的问题(例如100条incr 可以用一条add 100实现), 其原理如下:
- 调用fork(), 创建一个子进程。
- 子进程把新的AOF写到一个临时文件里,新的AOF是根据内存数据生成对应的命令,并不需要区依赖原来的AOF文件。
- 主进程持续将新的变动写到内存中,并更新到“旧”的AOF文件里。
- 重写结束之后,会给主进程一个信号,然后把内存的buff追加到新生成的AOF文件。
- 用新的AOF替换掉旧的AOF。
5.3 从Redis中恢复数据
其实只要重启就可以了。。
- 检查AOF是否存在,若存在则直接加载AOF,不再去找RDB
- 若不存在AOF,则尝试加载RDB
5.4 RDB和AOF的优缺点
-
RDB优点:创建RDB那一瞬间的全部内存数据快照,文件小,恢复快
-
RDB缺点:无法保存最近一次快照之后的数据
-
AOF优点:可读性高,适合保存增量数据,数据不易丢失
-
AOF缺点:文件体积大,恢复时间长
5.5 redis 4.0之后的备份就是混合模式,即RDB-AOF.
rdb用于全量备份,aof用于增量备份,为redis4.0之后的默认备份方式。
bgsave做全量持久化,aof做增量持久化
六、Redis主从同步
主从同步原理
redis的一个Master用于写操作,其他的Slave都是用于读操作。每一个Master和Slave都是一个Redis server实例。期间只保证主从的最终一致性!
主从同步分为全同步和增量同步。
-
全同步过程(上图中的1,2,3,4)
- Salve发送sync命令给Master请求同步
- Master进行一次BGSAVE(注意,如果是diskless模式的话,可以不用在本地生成rdb,参考)
- 在进行BGSAVE期间,将所有写操作命令保存至缓存区中。
- Slave得到Master发送的rdb文件之后进行数据载入。
- Master再将期间生成的增量写命令发送给slave端。
-
增量同步(上图中的5):
-
Master接受到用户的操作命令,如果是写命令,就先将操作记录追加到AOF文件中;然后确定将此条写命令传给哪些Salve,然后往响应缓存中写入这条指令。最后将缓存中的数据发送给Slave。(注意,这里并不是收到一条就发送一条,因为需要保证的是最终一致性!)
七、Redis哨兵
但Redis这种主从结构,如果Master服务器当机了,那就全完了。。因此,解决主从同步Master宕机后的主从切换问题: 哨兵模式(Redis Sential)
Redis Sential
Redis Sential是redis官方提供的集群管理工具,其本身也是个独立运行的进程,提供以下几点服务:
- 监控:检查主从服务器是否运行正常
- 提醒:当被监控的某个redis服务器出现问题时,sential通过API向管理员或其他应用程序发送故障通知
- 自动故障迁移:主从切换(主服务器挂了以后,将一个Salve服务器升级成Master服务器,做的事情包括:通知之前的Slave已经修改了Master并告诉新的Master是哪个,当用户发送请求到来时,会返回一个通知,并包括新的Master的地址,使得集群能够正常运行)