一、redis介绍:
- redi是一个C语言编写的NoSql的Key-Value数据库;
- 它支持存储的value类型包括string(字符串)、Linkedlist(链表)、set(集合)、zset(sorted set有序集合)和hash(哈希类型)。新版本新增支持类型 BitMaps位图、hyperloglog超小内存唯一值计算、geo地图定位
- 单个value容量可达到512M,主要受物理内存限制。
- 数据加载在内存中,部分数据会异步flush持久化到磁盘,断电后数据不会丢失;50个并发每秒读写10万次(10万QPS,即query per second没秒内查询次数);
- 提供丰富的功能,如 慢查询、发布订阅、lua脚本、pipeline、事务、高可用、分布式支持
- 支持多种语言(java PHP python)
1、redis应用场景
- 缓存:提高响应速度和并发能力
- 计数:统计视频网站播放次数,单线程下速度快、也不会出错(string类型,incr、decr)
- 消息队列系统:简单的消息中间件功能
- 排行榜:游戏排行
- 社交网络:粉丝数、关注数
- 实时系统:
2、多路复用 和 线程模型
阻塞模型:阻塞就是当前任务等待也不能执行别的任务他。
非阻塞模型:就是在当前线程等待时可以去处理别的任务。
使用非阻塞多路复用器模式处理客户端请求;redis根据不同的事件类型交给事件分配器分配,依据不同的事件处理器进行应答/处理和响应客户端。
- Redis 采用了 非阻塞I/O 多路复用机制(epoll)处理大量的客户端 Socket 请求。
- Redis 采用单线程模型可以避免了多线程之间的竞争(频繁的上下文切换),避免死锁造成的性能损耗。
- Redis的全部操作是基于内存的。因此 Redis 瓶颈可能是机器的内存或者网络带宽,而并非 CPU。
2.1 单线程概念
Redis是单线程的原因:
- Redis 单线程指的是「接收客户端请求->解析请求 ->进行数据读写等操作->发送数据给客户端」这个过程是由一个线程(主线程)来完成的
2.2 Redis 6.0 之后为什么引入了多线程?
-
在 Redis 6.0 版本之后,也采用了多个 I/O 线程来处理网络请求,这是因为随着网络硬件的性能提升,Redis 的性能瓶颈有时会出现在网络 I/O 的处理上。
-
Redis引入多线程来处理网络I/O,仍然使用单线程来执行Redis命令。
-
注意:对于命令的执行,Redis 仍然使用单线程来处理,不要误解 Redis 有多线程同时执行命令。
3、不同value类型应用场景
String:存放对象的序列化信息,或者用作计数
hash:存放键对应不同属性信息,对于一个整体信息比较直观好管理并且节省空间,因为有可能key1需要1属性,key2不需要;或者频繁更新属性值。缺点不好控制失效时间。
list:列表有序,可以重复值特性;微博消息,更新的消息lpush放在队列最左边,lrang获取10条信息,然后在具体获取其中的微博内容。
set:无序,无重复;抽奖活动中已经中奖的排除可以使用spop。
名称 | 类型 | 数据存储选项 | 查询类型 | 附加功能 |
---|---|---|---|---|
12 | 2113 | 2312 | 32 | 421 |
[]:
4、Redis和Memcached的区别
Memcached支支持简单的string类型,redis支持更丰富的类型。
Memcached不支持持久化,redis支持持久化。
三、数据结构和内部编码
四、redis发布订阅
基于消息做的一个发布与订阅,首先先订阅发布者,然后通过消息队列获取订阅的消息。(类似于微信公众号)实际运用很少
五、redis持久化
1、RDB二进制文件
某一时间点快照,把内存中的数据写入磁盘的临时文件,作为快照,恢复的时候把快照文件读进内存。
1.1 RDB优劣势
优势
- 某一时间点,全量备份
- 灾备简单,可以远程传输
- 子进程备份的时候,主进程不会有任何io操作(不会有写入修改或删除),保证备份数据的的完整性
- 相对AOF来说,当有更大文件的时候可以快速重启恢复
劣势
-
发生故障是,有可能会丢失最后一次的备份数据
-
子进程所占用的内存比会和父进程一模一样,如会造成CPU负担
-
由于定时全量备份是重量级操作,所以对于实时备份,就无法处理了。
触发生成RDB存储的三种方式:
- save(同步命令,会造成阻塞):文件策略是将新生成的RDB文件替换老的文件
- bgsave(异步,默认):创建一个fork线程去处理生成RDB文件,文件策略和save一样
- 自动生成RDM(不使用):配置间隔时间内有改变就触发生成dump.rdb文件
命令 | save | bgsave |
---|---|---|
IO类型 | 同步 | 异步 |
阻塞 | 是 | 非阻塞 |
优点 | 不消耗额外内存 | 不占用客户端命令 |
缺点 | 阻塞客户端命令 | 需要fork消耗内存 |
配置redis.conf |
修改 /usr/local/redis/redis.conf,Redis核心配置文件,可以设置不同时间保存不同明明的rdb文件,以方便恢复数据
stop-writes-on-bgsave-error yes 如果bgsave出现错误时停止写操作,no:可能造成数据不一致
rdbcomperssion yes 保存RDB文件使用压缩格式
rdbchecksum yes 采用校验方式
save 300 10 设置更新10次缓存数据,在300秒后备份到RDB文件中
dir /usr/local/redis/working RDB文件保存路径
耗时、耗性能 ,所有的数据dump到硬盘,fork会消耗内存、大数据量对IO性能也是消耗,中间宕机会造成数据丢失情况。
2、AOF二进制文件
将redis写操作命令以实时日志形式追加到AOF文件中。
2.1、 AOF特点
-
以日志的形式来记录用户请求的写操作。读操作不会记录,因为写操作才会存存储。
-
文件以追加的形式而不是修改的形式。
-
redis的aof恢复其实就是把追加的文件从开始到结尾读取执行写操作。
2.2、 优势
-
AOF更加耐用,可以以秒级别为单位备份,如果发生问题,也只会丢失最后一秒的数据,大大增加了可靠性和数据完整性。所以AOF可一次,使用fsync操作。
-
以log日志形式追加,如果磁盘满了,会执行 redis-check-aof 工具
-
当数据太大的时候,redis可以在后台自动重写aof。当redis继续把日志追加到老的文件中去时,重写也是非常安全的,不会影响客户端作。
-
AOF 日志包含的所有写操作,会更加便于redis的解析恢复。
2.3、 劣势
-
相同的数据,同一份数据,AOF比RDB大
-
针对不同的同步机制,AOF会比RDB慢,因为AOF每秒都会备份做写操作,这样相对与RDB来说就略低。 每秒备份fsync没毛病,但是的每次写入就做一次备份fsync的话,那么redis的性能就会下降。
-
AOF发生过bug,就是数据恢复的时候数据不完整,这样显得AOF会比较脆弱,容易出现bug,因为AOF没有RDB那么简单,但是呢为
的产生,AOF就不会根据旧的指令去重构,而是根据当时缓存中存在的数据指令去做重构,这样就更加健壮和可靠了。appendonly no # AOF 默认关闭,yes可以开启 appendfilename "appendonly.aof" # AOF 的文件名 # no:不同步 # everysec:每秒备份,推荐使用 # always:每次操作都会备份,安全并且数据完整,但是慢性能差 appendfsync everysec no-appendfsync-on-rewrite no #重写的时候是否要同步,no可以保证数据安全 # 重写机制:避免文件越来越大,自动优化压缩指令,会fork一个新的进程去完成重写动作,新进程里的内存数据会被重写,此时 # 当前AOF文件的大小是上次AOF大小的100% 并且文件体积达到64m,满足两者则触发重写 auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb
2.3、AOF三种策略:
- always:写命令每条命令都刷新到缓冲区,都写入硬盘
- everysec(默认):写命令刷新到缓冲区,每秒定时写入到硬盘
- no:写命令刷新到缓存,根据操作系统调度写入硬盘
命令 | always | everysec | no |
---|---|---|---|
优点 | 不丢失数据 | 每秒一次刷新到硬盘 | 不用管理 |
缺点 | IO开销比较大 | 丢失一秒数据 | 管控不了 |
AOF重写:
当AOF文件由于业务量而增大而增大,造成恢复很慢、写入速度影响,进行AOF重写,去掉一些键值的中间值留下最终值、过期、删除等,这样可以加快恢复速度、减少硬盘存储量。使用后台线程执行。
3、 AOF与RDB对比
命令 | RDB | AOF |
---|---|---|
启动优先级 | 低 | 高 |
体积 | 小 | 大 |
恢复速度 | 快 | 慢 |
数据安全性 | 丢失数据 | 根据策略决定,一般是eversec丢一秒数据 |
- AOF的配置
到底采用RDB还是AOF呢?
- 如果你能接受一段时间的缓存丢失,那么可以使用RDB
- 如果你对实时性的数据比较care,那么就用AOF
六、主从复制
由于单个节点的redis并发能力大概5/6万,使用主从复制横向扩展结点提高并发能力,提供副本保证高可用,扩展读性能。
/usr/local/redis/redis.conf Redis核心配置文件位置,密码配置root,端口6379
/etc/init.d/redis_init_script Redis启动脚本位置
修改 /usr/local/redis/redis.conf文件
replicaof 192.168.220.3 6379 #设置master节点ip和端口
masterauth root #设置访问redis密码
replica-read-only yes #默认就是从节点只读模式
查询状态
[root@root ~]# redis-cli #链接redis客户端
127.0.0.1:6379> auth root #输入密码
OK
127.0.0.1:6379> info replication #查询当前主机状态
# Replication
role:master #当前节点角色 master主机,slave从节点
connected_slaves:1 #从节点数量
slave0:ip=192.168.220.31,port=6379,state=online,offset=42,lag=0 #当前从节点信息
master_replid:ed2215ee72cd7f5a52087f1bdca62ce48b9f6a1d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:42
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:42
开启主从模式,slave会全量同步master的RDB文件到磁盘,然后读取到内存;使用旧数据同步,保证master写操作slave读操作不影响;
slave只能读取,master可读可写;
默认master挂了不会自动选举,恢复后将数据继续同步到slave节点。
1、slaveof 命令
在从节点执行slaveof 主节点id 端口,异步执行;
主节点写,从节点读保持主从数据一致;
主节点通过bgsave触发RDB类型同步数据到从节点,从节点会清除自己独有数据和主节点完全一致数据。
无需重启,不便于管理。
使用命令slaveof no one可以解除自己的主节点关系
2、全量复制和部分复制
全量复制需要消耗大量的cpu、io、内存、RDB网络传输时间、从节点加载RDB时间;部分复制就是先全量复制然后对比1M缓存区的偏移量,在范围内直接传输偏移量,超过进行全量同步操作。
3、读写分离
主写从读,生产情况下度多写少;可能会出现网络延迟、从节点阻塞数据不一致、读到过期数据(3.2版本解决,从节点不能清除过期数据)、从节点故障
七、Redis Sentinel 哨兵模式
主从集群中出现master挂了,slave选取一个master节点,然后其他从节点设置新的主节点等操作,这些都需要手动完成;sentinel就是解决这一问题,提供监控、通知客户端、故障转移。
因为master写,slave只读,所以必须重新选举,并且重新选举后master恢复后成为slave。
客户端通过sentinel访问redis,不用管谁是master、slave;当master被多个sentinel监控有问题时,会从多个slave中选取一个master,然后其他slave slaveof这个master,之前挂了的master重启后为slave。
[root@root redis]# cp /home/xiao/software/redis-6.0.6/sentinel.conf /usr/local/redis/
[root@root redis]# redis-sentinel sentinel.conf #后台启动哨兵
[root@root redis]# ps -ef|grep redis
root 1825 1781 0 19:16 pts/0 00:00:00 redis-cli
root 3242 1 0 20:51 ? 00:00:02 /usr/local/bin/redis-server 0.0.0.0:6379
root 3624 1 0 21:27 ? 00:00:00 redis-sentinel *:26379 [sentinel]
root 3630 2707 0 21:27 pts/1 00:00:00 grep --color=auto redis
[root@root redis]#scp sentinel.conf root@192.168.220.31:/usr/local/redis/
home/xiao/software/redis-6.0.6/sentinel.conf 哨兵文件配置
port 26379 #端口
dir "/usr/local/redis/sentinel" #工作路径
daemonize yes # 守护进程模式,后台运行
logfile "/usr/local/redis/sentinel/sentinel.log" # 指明日志文件名
#哨兵监控的master,主从配置一样,在进行主从切换时6379会变成当前的master端口,2表示2个哨兵标识为挂掉后重新选举
sentinel monitor mymaster 192.168.220.3 6379 2
# master或slave多长时间(默认30秒)不能使用后标记为s_down状态。
sentinel down-after-milliseconds mymaster 5000
#若sentinel在该配置值内未能完成failover操作(即故障时master/slave自动切换),则认为本次failover失败。
sentinel failover-timeout mymaster 18000
#设置master和slaves验证密码
sentinel auth-pass mymaster root
#哨兵程序自动添加的部分
# Generated by CONFIG REWRITE
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 1
###指明了当前群集的从库的ip和端口,在主从切换时该值会改变
sentinel known-slave mymaster 192.168.137.40 6380
###除了当前的哨兵还有哪些监控的哨兵
sentinel known-sentinel mymaster 192.168.137.40 26379 7a88891a6147e202a53601ca16a3d438e9d55c9d
sentinel current-epoch 1
查看相关信息
# 查看mymaster下的master节点信息
sentinel master mymaster
# 查看mymaster下的slaves节点信息
sentinel slaves mymaster
# 查看imooc-master下的哨兵节点信息
sentinel sentinels mymaster
1、 哨兵整合spring boot,在application.yml中配置
spring:
redis:
password: root
database: 1
sentinel:
master: mymaster #监控的master名字
nodes: 192.168.220.3:26379,192.168.220.31:26379 #sentinel监控的所有节点,26379为哨兵线程端口
按如上配置即可有spring控制读写redis切换节点,由sentinel控制节点宕机后选举动作。
八、集群
前面我们一起学习了主从复制以及哨兵,他们可以提高读的并发,但是单个master容量有限,数据达到一定程度会有瓶颈,这个时候可以通过水平扩展为多master多slave集群。
redis-cluster:他可以支撑多个master-slave,支持海量数据,实现高可用与高并发。
哨兵模式其实也是一种集群,他能够提高读请求的并发,但是容错方面可能会有一些问题,比如master同步数据给slave的时候,这其实是异步复制吧,这个时候如果master挂了,那么slave上的数据就没有master新,数据同步需要时间的,1-2秒的数据会丢失。master恢复并转换成slave后,新数据则丢失。
特点
-
每个节点知道彼此之间的关系,也会知道自己的角色,当然他们也会知道自己存在与一个集群环境中,他们彼此之间可以交互和通信(ping/ pong)。那么这些关系都会保存到某个配置文件中(nodes-6379.conf),每个节点都有,这个我们在搭建的时候会做配置的。
-
客户端要和集群建立连接的话,只需要和其中一个建立关系就行。
-
某个节点挂了,也是通过超过半数的节点来进行的检测,客观下线后主从切换,和我们之前在哨兵模式中提到的是一个道理。
-
Redis中存在很多的插槽,又可以称之为槽节点,用于存储数据,这个先不管,后面再说。
集群容错
构建Redis集群,需要至少3个节点作为master,以此组成一个高可用的集群,此外每个master都需要配备一个slave,所以整个集群需要6个节点,这也是最经典redis集群模式,称作三主三从,容错性更佳。所以在搭建的时候需要有6台虚拟机。可以通过克隆去构建,使用单实例的Redis 去克隆即可,集群也可以在单服务器构建,称之为伪集群,但是生产环境肯定是真的,所以建议用6台。克隆后务必关闭Redis。
1、集群环境配置
修改 /usr/local/redis/redis.conf 文件配置
# 开启集群模式
cluster-enabled yes
# 每一个节点需要有一个配置文件,需要6份。每个节点处于集群的角色都需要告知其他所有节点,彼此知道,这个文件用于存储集群角色状态关系信息
cluster-config-file nodes-6379.conf
# 超时时间,超时则认为master宕机,随后主备切换
cluster-node-timeout 5000
# 开启AOF
appendonly yes
[root@root xiao]# /etc/init.d/redis_init_script stop
[root@root xiao]# /etc/init.d/redis_init_script start
[root@root xiao]# ps -ef|grep redis
root 2824 1 0 19:48 ? 00:00:00 /usr/local/bin/redis-server 0.0.0.0:6379 [cluster]
root 2838 2330 0 19:48 pts/0 00:00:00 grep --color=auto redis
#如下命令创建三主三从集群,cluster-replicas表示master与slave映射比值
[root@root ~]# redis-cli -a root --cluster create 1192.168.217.128:8001 192.168.217.128:8002 192.168.217.128:8003 192.168.217.128:8004 192.168.217.128:8005 192.168.217.128:8006 --cluster-replicas 1
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 192.168.217.128:8005 to 192.168.217.128:8001
Adding replica 192.168.217.128:8006 to 192.168.217.128:8002
Adding replica 192.168.217.128:8004 to 192.168.217.128:8003
M: 02d47d69650337ec4a967f5311718939c65d61ee 192.168.217.128:8001
slots:[0-5460] (5461 slots) master
M: 736fe6711ab1c1abfaace923fa553661bf956c37 192.168.217.128:8002
slots:[5461-10922] (5462 slots) master
M: 160224afce6c868c653d8e26e0cc5bdaf8da17be 192.168.217.128:8003
slots:[10923-16383] (5461 slots) master
S: 1e10e6ac6562918ee95480feb7a48e4679c3db62 192.168.217.128:8004
replicates 160224afce6c868c653d8e26e0cc5bdaf8da17be
S: 7952c769a0adb08753554e8767fdd415ed7a4e43 192.168.217.128:8005
replicates 02d47d69650337ec4a967f5311718939c65d61ee
S: 0f3e37ec085d26448898129af9388f1a74a2c19e 192.168.217.128:8006
replicates 736fe6711ab1c1abfaace923fa553661bf956c37
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
....
>>> Performing Cluster Check (using node 192.168.217.128:8001)
M: 02d47d69650337ec4a967f5311718939c65d61ee 192.168.217.128:8001
slots:[0-5460] (5461 slots) master
1 additional replica(s)
M: 160224afce6c868c653d8e26e0cc5bdaf8da17be 192.168.217.128:8003
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 0f3e37ec085d26448898129af9388f1a74a2c19e 192.168.217.128:8006
slots: (0 slots) slave
replicates 736fe6711ab1c1abfaace923fa553661bf956c37
S: 1e10e6ac6562918ee95480feb7a48e4679c3db62 192.168.217.128:8004
slots: (0 slots) slave
replicates 160224afce6c868c653d8e26e0cc5bdaf8da17be
M: 736fe6711ab1c1abfaace923fa553661bf956c37 192.168.217.128:8002
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
S: 7952c769a0adb08753554e8767fdd415ed7a4e43 192.168.217.128:8005
slots: (0 slots) slave
replicates 02d47d69650337ec4a967f5311718939c65d61ee
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
2、slot槽节点
- 总共有16384个槽节点,数据都存储于槽节点中,槽节点平均分布于各个master节点,不会存储在slave节点。
- 槽节点按照顺序平均分配不同频段到各个master
- 数据存储与查询通过 hash(key)%16384 到不同master的槽节点上。
3、
00:0c:29:b6:26:84 00:0c:29:33:8b:e1
vim /etc/udev/rules.d/70-persistent-ipoib.rules
vim /etc/sysconfig/network-scripts/ifcfg-ens33
service network restart
/etc/init.d/redis_init_script stop
/etc/init.d/redis_init_script start
ps -ef|grep redis
九、常见问题
1、redis缓存过期机制
设置了expire过期时间的key缓存过期了,但是服务器的内存还是会被占用,这是因为redis所基于的两种删除策略
redis有两种策略:
-
(主动)定时删除
定时随机的检查过期的key,如果过期则清理删除。(每秒检查次数在redis.conf中的hz配置) -
(被动)惰性删除
当客户端请求一个已经过期的key的时候,那么redis会检查这个key是否过期,如果过期了,则删除,然后返回一个nil。这种策略
友好,不会有太多的损耗,但是内存占用会比较高。
所以,虽然key过期了,但是只要没有被redis清理,那么其实内存还是会被占用着的。
2、 内存淘汰管理机制
那么如果内存被Redis缓存占用慢了咋办?内存占满了,可以使用硬盘,来保存,但是没意义,因为硬盘没有内存快,会影响redis性能。
所以,当内存占用满了以后,redis提供了一套缓存淘汰机制:MEMORY MANAGEMENT
maxmemory :当内存已使用率到达,则开始清理缓存
- noeviction:旧缓存永不过期,新缓存设置不了,返回错误(默认)
- allkeys-lru:清除最少用的旧缓存,然后保存新的缓存(推荐使用)
- allkeys-random:在所有的缓存中随机删除(不推荐)
- volatile-lru:在那些设置了expire过期时间的缓存中,清除最少用的旧缓存,然后保存新的缓存
- volatile-random:在那些设置了expire过期时间的缓存中,随机删除缓存
- volatile-ttl:在那些设置了expire过期时间的缓存中,删除即将过期的
3、缓存穿透
缓存和数据库都不存在的数据
由于业务自身问题、恶意攻击、爬虫等,绕过缓存直接访问数据库,会造成数据库非常大的压力。
解决方案就是将key和value=空设置到缓存,并设置一个过期时间,这样就不会直接访问数据库了。
4、布隆过滤器
使用二进制存储形式保存,设置哪些请求可以访问
5、缓存击穿
由于缓存过期失效,导致请求到达数据库。
6、缓存雪崩
同一时刻大量的key失效或者redis缓存挂了,导致大量查询访问数据库。
- 永不过期
- 错开失效时间
- 多层缓存
十、redis和memcached对比:
1、memcached只支持简单的字符串类型,redis支持多种复杂类型value;
2、memcached和redis都是将数据存在内存中。
3、memcached掉电后数据丢失,redis支持异步flush到磁盘,掉电后可以保存部分数据。
4、支持的value大小不一致,redis最大可达到1G,memcached只有1M。
5、memcached不支持主从同步,redis支持。
6、memcached不支持分片,redis支持。