Redis脑图
2024-11-14 12:06:53 0 举报
AI智能生成
"Redis脑图"是一份详细的知识图谱,涵盖了Redis的核心内容,包括数据类型、基本操作、高级特性和优化技巧等。它以直观的图形方式呈现出各种概念之间的联系,使读者能够快速理解并掌握Redis的关键知识。这份脑图适用于Redis初学者和有经验的开发者,帮助他们系统地学习和回顾Redis的相关知识。通过这份脑图,读者可以对Redis有更深入的理解,提高开发效率。
作者其他创作
大纲/内容
pipeline、消息订阅、事务、modules
pipeline
可以用来批量发送命令到redis
pub/sub
订阅者订阅一个channel,发布者将消息发送到一个channel,所有订阅这个channel的订阅者都能收到这条消息。
先订阅,才能收到publish的消息
publish ooxx hello
subscribe ooxx ==> 订阅者是收不到hello的,因为消息publish之前,没有订阅
先订阅之后就能收到:
subscribe ooxx
publish ooxx hello
subscribe ooxx ==> 订阅者是收不到hello的,因为消息publish之前,没有订阅
先订阅之后就能收到:
subscribe ooxx
publish ooxx hello
事务
将一组命令放在同一个事务中进行处理
事务的命令会被放在一个队列中。按顺序地执行。事务在执行的过程中,
不会被其他客户端发送来的命令请求所打断。
不会被其他客户端发送来的命令请求所打断。
事务中的命令要么全部被执行,要么全部都不执行。
redis不支持回滚
1. 保持redis的简单性
2. 大多数错误都是由于程序
导致的
1. 保持redis的简单性
2. 大多数错误都是由于程序
导致的
命令
multi
set k1 1
get k1
exec
set k1 1
get k1
exec
multi之后能输入多条命令
输入exec开始执行命令
输入exec开始执行命令
discard
撤销一个事务
watch
相当于CAS,监听某个key的值,如果发生了变化,那么整个事务就不会被执行
watch k1
multi
set k1 1
set k2 2
exec
如果k1发生了改变,那么后面的整个事务就不会执行
multi
set k1 1
set k2 2
exec
如果k1发生了改变,那么后面的整个事务就不会执行
modules
redis的一些扩展库
缓存穿透
一个数据本来就不存在,在缓存中没有,在数据库中也没有。
当查询这类数据的时候,会直接穿透缓存,请求直接打到数据库,这就叫缓存穿透。
当查询这类数据的时候,会直接穿透缓存,请求直接打到数据库,这就叫缓存穿透。
布隆过滤器
用来解决缓存穿透
用来解决缓存穿透
使用了bitmap。
每个元素通过一些映射函数,计算出一个值,映射到bitmap的某一位上,并将这一位设为1。(可能存在不同的元素
对应bitmap中相同的位置,或者不存在的元素计算出来的位置,bitmap都是1)
在查询某个元素的时候,也是通过映射函数,计算出对应的bitmap中的位置。如果对应的位置都是1
就说明元素可能存在,则放行。如果有一位是0,则一定不存在。
例子中,元素1、2是存在的元素,元素3是查询的元素
每个元素通过一些映射函数,计算出一个值,映射到bitmap的某一位上,并将这一位设为1。(可能存在不同的元素
对应bitmap中相同的位置,或者不存在的元素计算出来的位置,bitmap都是1)
在查询某个元素的时候,也是通过映射函数,计算出对应的bitmap中的位置。如果对应的位置都是1
就说明元素可能存在,则放行。如果有一位是0,则一定不存在。
例子中,元素1、2是存在的元素,元素3是查询的元素
概率解决问题,不可能100%阻挡恶意请求
能达到99%。剩下的1%无法拦截
成本低,速度快。
能达到99%。剩下的1%无法拦截
成本低,速度快。
1. 如果不存在的元素穿透了
2. 那么客户端需要增加redis中的key,value标记不存在。
下次查询的时候直接返回不存在,客户端再去处理
下次查询的时候直接返回不存在,客户端再去处理
1. 如果数据库增加了元素
2. 需要完成元素对bloom的添加
使用方法(架子):
客户端实现bloom算法,自己承载bitmap
客户端实现bloom算法,redis承载bitmap
客户端啥也不干,redis承载bloom和bitmap(这种更好)
客户端实现bloom算法,自己承载bitmap
客户端实现bloom算法,redis承载bitmap
客户端啥也不干,redis承载bloom和bitmap(这种更好)
redis作为数据库/缓存的区别
缓存数据不重要
缓存不是全量数据
缓存应该随着访问变化,应该放热数据
缓存不是全量数据
缓存应该随着访问变化,应该放热数据
redis key的内存淘汰策略
key的有效期
1,key的有效期,读操作会将有效期延长(面试)?不对!!
2,发生写,会剔除过期时间(设置的过期时间会被删掉,导致没有过期时间)
3,倒计时,使用expire设置多少秒之后过期,且redis不能延长。
4,定时, 使用expireat, 指定在某个时间过期
redis如何清除过期的key?
定期删除
Redis设定每隔100ms随机抽取设置了过期时间的key,并对其进行检查,
如果已经过期则删除。
Redis设定每隔100ms随机抽取设置了过期时间的key,并对其进行检查,
如果已经过期则删除。
惰性删除
所谓惰性策略就是在客户端访问这个key的时候,
redis对key的过期时间进行检查,如果过期了就立即删除
所谓惰性策略就是在客户端访问这个key的时候,
redis对key的过期时间进行检查,如果过期了就立即删除
如果定期删除没有删掉一些key,而且以后没有访问过这些key,导致惰性删除也没有删掉
这些key,这时候就需要内存淘汰机制。为什么需要内存淘汰机制?(面试)
这些key,这时候就需要内存淘汰机制。为什么需要内存淘汰机制?(面试)
redis内存淘汰机制
redis能够占用的内存是有限的。内存多大呢?
可以通过maxmemory <bytes> 进行设置
可以通过maxmemory <bytes> 进行设置
内存满了,怎么处理?==> 内存淘汰机制
通过maxmemory-policy 设置, 默认是noeviction,达到了就报错
LFU(Least Frequently Used)淘汰使用频率最少的
LRU(least recently used) 淘汰最久没有使用的
Random 随机删除
ttl 淘汰将要过期的
Random 随机删除
ttl 淘汰将要过期的
缓存常见问题
一定是在高并发的情况下才会发生。
一定是在高并发的情况下才会发生。
缓存穿透
查询本来就没有的数据,缓存中没有,数据库中也没有。
解决方案:
1. 接口层增加校验,比如用户鉴权校验,参数做校验,不合法的参数直接代码Return。
2. 布隆过滤器 ==> 缺点: 添加删除数据库中的数据不方便,都得更新布隆过滤器。
可以使用布谷鸟过滤器,添加删除元素方便。
1. 接口层增加校验,比如用户鉴权校验,参数做校验,不合法的参数直接代码Return。
2. 布隆过滤器 ==> 缺点: 添加删除数据库中的数据不方便,都得更新布隆过滤器。
可以使用布谷鸟过滤器,添加删除元素方便。
布隆过滤器
缓存击穿
一条key过期导致缓存中的数据失效,数据库中有这个数据,导致针对这个key的大量请求直接打到数据库。
解决方案:
1. 设置key永不过期
2. 实现锁。
假如某时刻某请求发现key的缓存过期了,则调用SETNX key
设置一个短期内将过期的临时key, 只有设置成功了的线程才有权限去查询DB,
没设置成功的只能等待,DB查询到结果后再设置原来key到缓存。
方案2存在两个问题:
1. 如果第一个线程setnx成功,之后挂了,锁就没有释放,导致
后面的线程都拿不到锁?==> 给 临时key设置一个过期时间(SETNX key expire)。
2. 没有挂,但是锁超时了?==> 再起一个线程,去监控读db的线程是否取回了数据,如果没有取回,
则延长一下锁的时间。
1. 设置key永不过期
2. 实现锁。
假如某时刻某请求发现key的缓存过期了,则调用SETNX key
设置一个短期内将过期的临时key, 只有设置成功了的线程才有权限去查询DB,
没设置成功的只能等待,DB查询到结果后再设置原来key到缓存。
方案2存在两个问题:
1. 如果第一个线程setnx成功,之后挂了,锁就没有释放,导致
后面的线程都拿不到锁?==> 给 临时key设置一个过期时间(SETNX key expire)。
2. 没有挂,但是锁超时了?==> 再起一个线程,去监控读db的线程是否取回了数据,如果没有取回,
则延长一下锁的时间。
缓存雪崩
大量的key同时过期,导致大量的并发请求直接打到数据库。
和击穿的区别就是,击穿指的是一条key过期,导致并发
查询这个key的请求打到数据库。雪崩是大量key同时过期。
和击穿的区别就是,击穿指的是一条key过期,导致并发
查询这个key的请求打到数据库。雪崩是大量key同时过期。
解决方案:
分两种情况:
1. 和时点性无关:什么时候key过期都无所谓。这时候可以使用 随机key过期时间。
2. 和时点性有关,比如必须在零点过期,更新缓存数据,这时候就不能用随机key过期时间了。
==> 强依赖击穿方案,让第一个请求获得锁之后,将所有的数据都更新回来。
分两种情况:
1. 和时点性无关:什么时候key过期都无所谓。这时候可以使用 随机key过期时间。
2. 和时点性有关,比如必须在零点过期,更新缓存数据,这时候就不能用随机key过期时间了。
==> 强依赖击穿方案,让第一个请求获得锁之后,将所有的数据都更新回来。
Redis如何实现分布式锁?
自己实现:
1. setnx
1. setnx
2. 设置临时key过期时间
3. 多线程(守护线程)延长key过期时间
redisson 和 zookeeper 做分布式锁!
redis的持久化RDB、fork、copyonwrite、AOF
RDB (Redis data base)
快照/副本
快照/副本
时点性:保存的是数据在某个时间点上的副本
管道(不重要)
管道:
1,衔接,前一个命令的输出作为后一个命令的输入
2,管道会触发创建【子进程】
echo $$ | more
echo $BASHPID | more
$$ 高于 |
使用linux 父子进程 的时候:
父进程的数据,子进程可不可以看得到?
常规思想,进程是数据隔离的!
进阶思想,父进程其实可以让子进程看到数据!
linux中:
export的环境变量,子进程的修改不会破坏父进程
父进程的修改也不会破坏子进程
save
阻塞,执行命令的同时,redis不在对外提供服务
redis主进程负责生成rdb文件
关机维护的时候,用save
bgsave(background save)
非阻塞,redis同时对外提供服务
fork() + copy on write
fork会创建一个redis子进程,创建出的子进程负责备份数据。
创建子进程的时候,不会将内存中的数据拷贝一份,父子进程共享内存数据。
(速度快,占用空间小)
创建子进程的时候,不会将内存中的数据拷贝一份,父子进程共享内存数据。
(速度快,占用空间小)
写操作都发生在redis父进程,只有真正发生写操作的时候,物理内存会先出现
要写的值,然后将父进程的虚拟地址指向新的物理内存地址。(copy on write)
redis子进程看不到修改,备份数据不受影响。
要写的值,然后将父进程的虚拟地址指向新的物理内存地址。(copy on write)
redis子进程看不到修改,备份数据不受影响。
配置文件中可以配置bgsave规则
(标识是save,但是做的是bgsave,不严谨)
(标识是save,但是做的是bgsave,不严谨)
save 900 1 // 距离上次生成快照,超过了900s,
并且有1个key发生了改变,自动触发bgsave
save 300 10 // 同上
save 60 10000 //同上
并且有1个key发生了改变,自动触发bgsave
save 300 10 // 同上
save 60 10000 //同上
dbfilename dump.rdb //生成的 rdb 文件名
dir /var/lib/redis/6379 // rdb文件存储在哪
还可以手动在redis-cli 输入bgsave,触发
正常运行的时候,用bgsave
弊端/优点
只能生成一个rdb文件,需要自己备份某个时间点的数据
丢失数据相对多一些。
时点与时点之间窗口数据容易丢失。
8得到一个rdb,9刚要做一个rdb,挂机了
记录的是二进制数据,恢复速度相对较快
AOF (Append only File)
日志文件
日志文件
记录写操作到日志文件中
由于记录了所有的写操作,所以丢失数据少
RDB和AOF可以同时开启
如果同时开启了,恢复的时候只会使用AOF文件
4.0以后,AOF中会包含一个RDB全量,同时会记录新的写操作
redis是内存数据库,写AOF文件
会触发IO,导致redis变慢。怎么解决?
配置文件中有参数可以调节什么
时候将数据同步(sync)到硬盘
会触发IO,导致redis变慢。怎么解决?
配置文件中有参数可以调节什么
时候将数据同步(sync)到硬盘
在写文件的时候,会先将数据写入到内核的缓冲区中,
然后内核再将缓冲区中的数据写入硬盘。下面的参数就可以
设置什么时候让内核去将缓冲区中的数据写入磁盘。
然后内核再将缓冲区中的数据写入硬盘。下面的参数就可以
设置什么时候让内核去将缓冲区中的数据写入磁盘。
appendfsync always // 每来一个写命令就同步到磁盘。(基本不丢失,但是速度慢)
appendfsync everysec // 每秒同步一次。(数据丢失相对少,速度快。推荐使用)
appendfsync no // 让操作系统决定什么时候同步到磁盘。(可能会丢失一个缓冲区的数据)
appendfsync everysec // 每秒同步一次。(数据丢失相对少,速度快。推荐使用)
appendfsync no // 让操作系统决定什么时候同步到磁盘。(可能会丢失一个缓冲区的数据)
appendfsync everysec专门由一个线程同步数据到磁盘
no-appendfsync-on-rewrite no 在发生重写的时候,不去将缓冲区中的内容
写入磁盘。(主要是为了避免发生IO争抢。重写的时候可能一个进程正在生产rdb文件,
同步数据到磁盘也是一个线程,就会发生IO争抢)
写入磁盘。(主要是为了避免发生IO争抢。重写的时候可能一个进程正在生产rdb文件,
同步数据到磁盘也是一个线程,就会发生IO争抢)
日志文件可能无限变大,
用AOF文件恢复的时候会很慢
怎么解决?
用AOF文件恢复的时候会很慢
怎么解决?
4.0以前
重写
重写
删除抵消的命令
合并重复的命令
最终AOF文件也是一个纯指令的日志文件
4.0以后
重写
重写
将老的数据RDB到aof文件中
将增量的以指令的方式Append到AOF文件
最终AOF文件是一个混合体,包含RDB和指令。
利用了RDB的快。
同时利用了日志的全量。
aof-use-rdb-preamble yes 参数配置
重写AOF文件是fork一个子进程做的
fork + 管道
父进程通过管道将新产生的写命令传给子进程,
子进程写入AOF文件
父进程通过管道将新产生的写命令传给子进程,
子进程写入AOF文件
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
auto-aof-rewrite-min-size 64mb
aof最小达到64mb的时候,触发rewrite。
当大小增长达到原来大小的一倍的时候,
触发rewrite。
当大小增长达到原来大小的一倍的时候,
触发rewrite。
实操
rdb
二进制文件,最开头是REDIS,其它显示都是乱码
bgsave
AOF
记录的是写命令
*2 代表的是写命令有几个部分。2表示的是两个部分
$6 代表的是下面的命令有几个字节。6表示是6个字节
*2 代表的是写命令有几个部分。2表示的是两个部分
$6 代表的是下面的命令有几个字节。6表示是6个字节
重写aof
bgrewriteaof
bgrewriteaof
4.0以后,会先将aof中老的写命令变成rdb,
也记录在aof文件中。aof文件变成了混合体
也记录在aof文件中。aof文件变成了混合体
恢复速度快,同时是全量数据
用作缓存,丢失数据无所谓,因为后面还有数据库保存了所有的数据,
可以只用rdb,redis启动的时候,保证缓存里面有一部分数据
但是如果用作数据库就需要aof来保证数据的完整性
可以只用rdb,redis启动的时候,保证缓存里面有一部分数据
但是如果用作数据库就需要aof来保证数据的完整性
Redis引入
关系型数据库:表很大,性能下降?
如果表有索引,增删改变慢。因为需要维护索引
查询速度呢?
1. 1个或少量查询依然很快
2. 并发大的时候会受硬盘带宽影响速度(很多查询命中不同的4K page)
解决方案:
HANA:内存级别的关系型数据库。所有的数据都放在内存中
价格太贵。
折中方案:缓存。
memcache和redis
memcache和redis
Redis是一个内存中key value的数据库,
value有五种数据类型。
memcache也是key value数据库,value没有数据类型
value有五种数据类型。
memcache也是key value数据库,value没有数据类型
memcache和redis的区别(面试):
1. redis提供了多种数据结构,例如字符串,列表,集合,有序集合等,而memcache没有,只支持键值对存储
2. redis支持持久化,AOF和RDB两种方式,而memcache不支持持久化,重启之后,数据会丢失
3.redis支持主从复制和分片,分布式系统中更加可用;而memcache不支持分片,需要客户端代码自己支持
4.redis是单线程,memcache是多线程
1. redis提供了多种数据结构,例如字符串,列表,集合,有序集合等,而memcache没有,只支持键值对存储
2. redis支持持久化,AOF和RDB两种方式,而memcache不支持持久化,重启之后,数据会丢失
3.redis支持主从复制和分片,分布式系统中更加可用;而memcache不支持分片,需要客户端代码自己支持
4.redis是单线程,memcache是多线程
安装(不重要)
1,yum install wget
2,cd ~
3,mkdir soft
4,cd soft
5,wget http://download.redis.io/releases/redis-5.0.5.tar.gz
6,tar xf redis...tar.gz
7,cd redis-src
8,看README.md
9,make install PREFIX=/opt/mashibing/redis5 (就是将编译出来的可执行文件放到了这个自定义的目录)
....yum install gcc
.... make distclean (每次编译之前可以执行它,清除上一次编译遗留的东西)
11,cd src ....生成了可执行程序
12,vim /etc/profile
... export REDIS_HOME=/opt/mashibing/redis5
... export PATH=$PATH:$REDIS_HOME/bin (加入path中,install_server.sh会用这个目录,找到可执行的程序目录)
..source /etc/profile
15,cd utils
16,./install_server.sh (可以执行一次或多次)
如果这一步 报systemd错,注释掉install_server.sh里面相关的脚步代码。
a) 一个物理机中可以有多个redis实例(进程),通过port区分
b) 可执行程序就一份在目录,但是内存中未来的多个实例需要各自的配置文件,持久化目录等资源
c) service redis_6379 start/stop/stauts > linux /etc/init.d/****
d)脚本还会帮你启动!
17,ps -fe | grep redis
NIO原理
Redis是单进程,单线程,单实例的,为什么很快?(面试)
1.基于内存的,内存的操作比磁盘块
2.单线程、使用了IO多路复用技术,是一个线程就能处理很多客户端的请求
3.高效的数据结构,经过优化之后,这些结构的操作很快
1.基于内存的,内存的操作比磁盘块
2.单线程、使用了IO多路复用技术,是一个线程就能处理很多客户端的请求
3.高效的数据结构,经过优化之后,这些结构的操作很快
基本数据类型(指的是key-value中
value的类型)
value的类型)
string
byte存储
byte存储
字符串操作。
截取,append等
截取,append等
数值操作。
累加,减法等
累加,减法等
抢购,秒杀,详情页,点赞,评论
规避并发下,对数据库的事务操作
完全由redis内存操作代替
BitMap
Redis是按照字节来存储的,每个字节是8位。
每一位都有一个索引,比如第一个字节是0-7,第二个字节是8-15,
就组成了bitmap,可以通过bitmap来操作二进制位,效率高
每一位都有一个索引,比如第一个字节是0-7,第二个字节是8-15,
就组成了bitmap,可以通过bitmap来操作二进制位,效率高
Redis提供了setbit、setbit、bitcount、bitpos、
bitop等方法来操作某个位
bitop等方法来操作某个位
使用例子(面试)
1,有用户系统,需要统计某个用户在某一个时间段内总共登录的天数
如果使用mysql,关系型数据库,就需要存储用户的id,ip,登录时间等信息,
用户每登录一次就得记录一条数据,浪费空间。查询的时候也很慢
用户每登录一次就得记录一条数据,浪费空间。查询的时候也很慢
使用bitmap。一年有365天,可以用46个字节足以表示。
46个字节就是368位,每一位代表一年中的一天,当用户在某天登录的时候,就将
对应的位设置成1。效率高,而且统计的时候速度也快,都是二进制操作。
46个字节就是368位,每一位代表一年中的一天,当用户在某天登录的时候,就将
对应的位设置成1。效率高,而且统计的时候速度也快,都是二进制操作。
2. 618做活动:送礼物 , 大库备货多少礼物
假设京东有2亿用户,需要去掉僵尸用户
,冷热用户/忠诚用户
需要统计活跃用户!随即窗口
比如说 1号~3号 要连续登录,同时要去重
使用bitmap,天为key,用户为value。value的每一位表示的是该位对应的用户是否登录,1表示登录过
u1 u2 u3
20190101 0 1 0 0 0 0 1 020190102 1 1 0 1 0 1 0 1
20190101 这天登录的有u2,20190102登录的有u1,u2
20190101 这天登录的有u2,20190102登录的有u1,u2
在key的属性里面(key相当于Object),会记录value对应的类型type,
还会记录value的encoding
还会记录value的encoding
比如说,一个字符串encoding是raw,执行+1操作,redsi会先取出这个字符串,转成int,
然后加1,如果加成功了,还会将encoding变成int。下次就不用转了,直接使用。
然后加1,如果加成功了,还会将encoding变成int。下次就不用转了,直接使用。
Redis是二进制安全的。存储的时候存储的是字节流,每一个字符一个字节,是二进制,
不会做出任何改变,客户端取的时候也是一个个字节取。
只要知道了encoding,就能保证数据的正确性。
不会做出任何改变,客户端取的时候也是一个个字节取。
只要知道了encoding,就能保证数据的正确性。
公司中使用redis需要约定编码,一般使用UTF-8
hash
相当于HashMap。key-value的value是一个hashmap,
value也有key-value
value也有key-value
hset k1 age 18 ==> k1位key, age为hasamp中的key,18位hashmap中的value
hmset k2 age 18 name gq
hget k1 age
hmget k2 age name ==> 输出 18 gq
hkeys k1 ==> age name
hvalues k2 ==> 18 gq
hmset k2 age 18 name gq
hget k1 age
hmget k2 age name ==> 输出 18 gq
hkeys k1 ==> age name
hvalues k2 ==> 18 gq
还能进行数值计算 ==>HINCRBY 、 HINCRBYFLOAT
场景:点赞,收藏,详情页
list
可重复,有序(添加
元素的顺序)
可重复,有序(添加
元素的顺序)
底层是一个双向链表, key中有head和tail两个属性,分别为头尾指针,
指向链表的头和尾。
指向链表的头和尾。
栈
同向命令
同向命令
lpush k1 a b c ==> 每次放元素的时候都是放在上一个元素的左边
lpop ==> 从左边取先取出来的是c
这不就是栈? ==> 后进先出
rpush 和 rpop同理
只要使用同向命令就可以实现栈
lpop ==> 从左边取先取出来的是c
这不就是栈? ==> 后进先出
rpush 和 rpop同理
只要使用同向命令就可以实现栈
队列
反向命令
反向命令
lpush k2 a b c
rpop ==> 取右边,取出来的是a
这不就是队列? ==> 先进先出
rpush和lpop同理
只要使用反向命令就可以实现队列
rpop ==> 取右边,取出来的是a
这不就是队列? ==> 先进先出
rpush和lpop同理
只要使用反向命令就可以实现队列
数组
lindex k1 2 ==> 根据索引取值(取k1中索引为2的位置的值)
lset k1 2 3 ==> 根据索引设置值(设置k1中索引为2的位置的值为3)
这不就是数组?
lset k1 2 3 ==> 根据索引设置值(设置k1中索引为2的位置的值为3)
这不就是数组?
阻塞队列, 单播(阻塞时间最长的那个
先拿到值,结束阻塞)
先拿到值,结束阻塞)
blpop k1 0 ==> 等着k1有值,没有就阻塞,0表示一直阻塞
如果大于0的数,例如10,就表示阻塞10s ,如果没有数据就停止
blpop k1 0 ==> 第二个客户端也阻塞相同的key
rpush k1 13 ==> 上面的阻塞就会停止,拿出压入k1的数据13
如果大于0的数,例如10,就表示阻塞10s ,如果没有数据就停止
blpop k1 0 ==> 第二个客户端也阻塞相同的key
rpush k1 13 ==> 上面的阻塞就会停止,拿出压入k1的数据13
其他命令
ltrim k1 0 2 ==> 保留在指定区间内的元素,删除不在区间内的元素
redis具有正负索引,取元素可以用索引取
lrange 0 -1 可以取出所有元素
lrange 0 -1 可以取出所有元素
set
【无序】&&【随机性】
放入的多少不同,元素存储的顺序不同
去重
命令
sadd k1 1 2 3 2 4 3 ==> 添加元素。最终1 2 3 4
集合操作
(用的多)
(用的多)
sinter 、 sinterstore ==> 交集
sinter k2 k3
sintertore dest k2 k3 ==> 取交集放在key为dest的set中
sunion 、 sunionstore ==> 并集
sdiff ==> 差集
sinter k2 k3
sintertore dest k2 k3 ==> 取交集放在key为dest的set中
sunion 、 sunionstore ==> 并集
sdiff ==> 差集
随机事件
SRANDMEMBER key count
count如果为,
count如果为,
正数:取出一个去重的结果集,尽量满足指定的正数(不能超过已有集,例如集合中有
7个元素,指定的count为5,则取出5个不重复的元素;指定的count为10,
则会取出7个元素,尽量接近指定的count)
7个元素,指定的count为5,则取出5个不重复的元素;指定的count为10,
则会取出7个元素,尽量接近指定的count)
负数:取出一个带重复的结果集,一定满足你要的数量。 指定-5则取出5个,元素会重复;
-10就取出10个带重复的结果集
-10就取出10个带重复的结果集
如果:0,不返回
场景:抽奖。
人数小于礼物数。存的是人的名字。
例如7个人抽20张购物卡
srandmember k2 -20 ==> 一定生成含有重复中奖
的20个元素
人数小于礼物数。存的是人的名字。
例如7个人抽20张购物卡
srandmember k2 -20 ==> 一定生成含有重复中奖
的20个元素
spop 一次随机取出一个
年会抽奖,一等奖一个,二等奖多个
使用spop k2
第一次pop的就是一等奖,第二次就是二等奖第一个
使用spop k2
第一次pop的就是一等奖,第二次就是二等奖第一个
sorted-set(zset)
去重,按分值排序(有正序和顺序)
命令
物理内存左小右大,不随命令发生变化。默认是正序存储
zrange
zrevrange
zadd k1 8 apple 2 banana 3 orange
zrange k1 0 -1 ==> 默认正序输出(banana orange apple)
zrange k1 0 -1 withscores
zrangebyscore k1 3 8 ==> 取出分值3和8的元素
zscore k1 apple ==> 取出分值
zrank k1 apple ==> 取出排名
zincrby k1 2.5 banana ==> 将banana的分值增加2.5
zrange k1 0 -1 ==> 默认正序输出(banana orange apple)
zrange k1 0 -1 withscores
zrangebyscore k1 3 8 ==> 取出分值3和8的元素
zscore k1 apple ==> 取出分值
zrank k1 apple ==> 取出排名
zincrby k1 2.5 banana ==> 将banana的分值增加2.5
集合操作
并集、交集
并集、交集
两个集合都有的元素怎么处理?
权重/聚合指令
权重/聚合指令
weight:可以对每个key指定权重,计算的时候分值会乘以权重
聚合
SUM、MIN、MAX
分值求和、取最小值、取最大值
SUM、MIN、MAX
分值求和、取最小值、取最大值
排序怎么实现的?
增删改查的速度?(面试)
增删改查的速度?(面试)
Skip List
跳表
跳表
redis的集群:主从复制、CAP、PAXOS
单机存在的问题
1.单点故障
2.容量有限
3.能够承受的压力有限
2.容量有限
3.能够承受的压力有限
怎么解决
AFK
X:全量,镜像
Y:业务,功能
Z:优先级,逻辑再拆分
X轴是redis的一个全量镜像,使用多个redis,能够解决单点故障的问题,同时
采用读写分离的模式,读操作打到一个redis,写操作打到别的redis,能够解决
一部分压力问题。但是解决不了容量有限的问题
Y轴按业务功能再进行划分,不同功能的数据存储在不同的redis中,能够解决
容量有限的问题。 X轴方向同时可以做一个备份,全量镜像。
Z轴按优先级、逻辑再拆分,一个业务的数据拆分到两个redis里面。例如以前是
1-1000的数据放在一个redis里面,可以拆分成1-500和501-1000两个redis
采用读写分离的模式,读操作打到一个redis,写操作打到别的redis,能够解决
一部分压力问题。但是解决不了容量有限的问题
Y轴按业务功能再进行划分,不同功能的数据存储在不同的redis中,能够解决
容量有限的问题。 X轴方向同时可以做一个备份,全量镜像。
Z轴按优先级、逻辑再拆分,一个业务的数据拆分到两个redis里面。例如以前是
1-1000的数据放在一个redis里面,可以拆分成1-500和501-1000两个redis
数据一致性
使用AKF,数据变多,怎么解决一致性问题?
强一致性
redis触发一个写命令之后,必须等后面的redis都写成功,返回OK之后,
才能给客户端返回OK。
如果有一个redis写入不成功,就会给客户端返回失败,就会破坏可用性
强一致性一定会破坏可用性、
才能给客户端返回OK。
如果有一个redis写入不成功,就会给客户端返回失败,就会破坏可用性
强一致性一定会破坏可用性、
弱一致性
容忍丢失一部分数据
前面的redis立即给客户端返回OK,后面的redis异步写入数据
前面的redis立即给客户端返回OK,后面的redis异步写入数据
redis使用的是弱一致性
特点是低延迟和高性能
最终一致性
写操作同步阻塞放入消息队列中,后面的redis什么时候有时间再去消息队列里面取,
然后写入数据。使用消息队列保证最终数据一致性
但是,另一个客户端从后面的redis中取数据的时候,可能取到不一致的数据
然后写入数据。使用消息队列保证最终数据一致性
但是,另一个客户端从后面的redis中取数据的时候,可能取到不一致的数据
主从复制
主备和主从
主备是有主节点,和多个备份节点,
但是备份节点不参与业务,客户端只能访问主,
主挂了,备份节点接替
但是备份节点不参与业务,客户端只能访问主,
主挂了,备份节点接替
主从复制是有主节点和从节点,客户端能够访问主节点也
能够访问从节点
能够访问从节点
主也是单点。怎么解决
对主做高可用。监控主的状态。如何进行监控?
从节点进行投票,一部分给出主节点OK,另一部分给出主节点不OK。
给出OK节点的数量,达到 n/2 + 1,表示主节点OK
给出不OK节点的数量,达到 n/2 + 1,表示主节点不OK
从节点进行投票,一部分给出主节点OK,另一部分给出主节点不OK。
给出OK节点的数量,达到 n/2 + 1,表示主节点OK
给出不OK节点的数量,达到 n/2 + 1,表示主节点不OK
投票数:n/2 + 1
从节点的数量建议是奇数台
why?
拿3台和4台来说,都只允许有一台挂掉,但是成本不一样,
4台比3台更容易出现一台故障。4台比3台更容易出现风险。
拿3台和4台来说,都只允许有一台挂掉,但是成本不一样,
4台比3台更容易出现一台故障。4台比3台更容易出现风险。
配置
命令: replicaof
从节点默认只能读
从节点挂了
刚开始追随的时候(以前没有追随过主节点),从节点会先flush掉自己老的数据,主节点会生成rdb文件,
然后从主节点生成的rdb文件中读取数据。
追随过后,从节点挂了,然后又起来,并且没有开启AOF,从节点就不会flush掉自己老的数据,主节点
也不会生成rdb文件。
如果开启了AOF,挂了又起来,从节点会flush掉老的数据,主节点会生成rdb文件,同时最后还会进行
rewrite,生成AOF文件
然后从主节点生成的rdb文件中读取数据。
追随过后,从节点挂了,然后又起来,并且没有开启AOF,从节点就不会flush掉自己老的数据,主节点
也不会生成rdb文件。
如果开启了AOF,挂了又起来,从节点会flush掉老的数据,主节点会生成rdb文件,同时最后还会进行
rewrite,生成AOF文件
主节点挂了
主可以知道有哪些从节点连接了它
sentinel (哨兵)
启动多个哨兵,用哨兵监控主节点的状态,当主挂了之后,哨兵会自动选举新的主节点出来
配置
port 26379 // 哨兵的端口
sentinel monitor mymaster 127.0.0.1 6381 2 // 监控哪个master,master端口号,2表示投票有效数量
(哨兵配置文件写法)
(哨兵配置文件写法)
redis-server 哨兵配置文件 --sentinel // 启动一个哨兵节点
redis-sentienl // 也可以启动一个哨兵节点
哨兵节点能够通过发布订阅的方式知道主节点上有哪些其它哨兵节点。
同时能够直接从master知道,现在有哪些从节点。
同时能够直接从master知道,现在有哪些从节点。
replica-serve-stale-data yes 是否从节点要从主节点同步完数据,才能对外提供服务
replica-read-only yes 从节点是否是read only的
repl-diskless-sync no 主节点直接将rdb文件直接通过网络IO发给从节点,而不是先在磁盘上
生成一个rdb文件,再通过网络IO发给从节点。 如果网络快,建议使用这种方式
生成一个rdb文件,再通过网络IO发给从节点。 如果网络快,建议使用这种方式
repl-backlog-size 1mb #增量复制 redis里面维护了一个队列,当从节点挂了一会然后又起来了,这时候
要去主节点复制数据,会给主节点一个offset偏移量,将偏移量以后的数据从队列都复制过来。这个参数
指定的就是这个队列的大小。
如果指定的过小,则有可能再挂掉再起来的时间,数据比较多,一下就写满了1M,找不到offser以后的数据了,
就会触发一个全量rdb。如果指定的够大,而且写的数据较少,那么就能从队列里面拿到需要复制的数据,就不需要
全量复制。
要去主节点复制数据,会给主节点一个offset偏移量,将偏移量以后的数据从队列都复制过来。这个参数
指定的就是这个队列的大小。
如果指定的过小,则有可能再挂掉再起来的时间,数据比较多,一下就写满了1M,找不到offser以后的数据了,
就会触发一个全量rdb。如果指定的够大,而且写的数据较少,那么就能从队列里面拿到需要复制的数据,就不需要
全量复制。
min-replicas-to-write 3 master最少得有多少个健康的slave存活才能执行写命令
min-replicas-max-lag 10 延迟小于min-replicas-max-lag秒的slave才认为是健康的slave
min-replicas-max-lag 10 延迟小于min-replicas-max-lag秒的slave才认为是健康的slave
CAP原则
一致性
可用性
分区容忍性
只能同时满足两个,不可能同时满足三个
sharding分片/分区
redis容量有限的问题,可以在客户端代码里面进行控制,将数据按照业务逻辑放到不同的redis上。
如果数据不能拆分怎么办?
如果数据不能拆分怎么办?
sharding分片,客户端实现
三种模式
三种模式
hash取模:modula
弊端:
取模的数必须固定
取模的数必须固定
影响分布式下的扩展性。
添加或者删除节点,得rehash
添加或者删除节点,得rehash
random
消息队列,类似kafka
ketama,一致性hash算法
hash
crc16
crc32
fnv等
常用的映射算法
常用的映射算法
没有取模运算
data和node节点同时参与映射计算
data和node节点同时参与映射计算
抽象出来了一个环形hash环,node经过映射算法,映射到环上的某一个点上。
有数据来了,也通过相同的映射算法,映射到环上的一个点上,顺时针找一个
最近的物理点,然后将数据放入物理点对应的node。
(环上的点是虚拟的,但是node对应的点是物理的。)
有数据来了,也通过相同的映射算法,映射到环上的一个点上,顺时针找一个
最近的物理点,然后将数据放入物理点对应的node。
(环上的点是虚拟的,但是node对应的点是物理的。)
优点:
你加节点,的确可以分担其他节点的压力,不会造成全局洗牌
缺点:
新增节点造成一小部分数据不能命中
1,问题,击穿,压到mysql
2,方案:没去取离我最近的2个物理节点
更倾向于 作为缓存,而不是数据库用!!!!
虚拟节点,解决数据倾斜问题。
一台物理node,映射到环上的多个点上。
一台物理node,映射到环上的多个点上。
如果分片逻辑在client实现,所有的client都会
连接redis,导致redis压力过大。
代理层实现分区。
连接redis,导致redis压力过大。
代理层实现分区。
加代理层。
代理层实现分片逻辑,而不是客户端实现。
客户端变得简单轻盈。
分为三种方式:modula 、random 、ketama
代理层实现分片逻辑,而不是客户端实现。
客户端变得简单轻盈。
分为三种方式:modula 、random 、ketama
代理实现:twitter官方的
twemproxy。用的较多
twemproxy。用的较多
代理服务器只有一台的时候,代理压力又比较大。
这时候,就可以将代理做成集群,代理集群前面再加一个LVS。LVS
做一个主备,主备直接靠keepalived进行监控,进行主备切换,
同时keepalived去监控所有的代理服务器的健康状态,如果发现有的
代理挂了,会让LVS将请求发到活着的服务器上。
LVS提供一个VIP,无论多少个客户端,项目组,api都只需要去通过这个VIP去
访问后面的服务。
这时候,就可以将代理做成集群,代理集群前面再加一个LVS。LVS
做一个主备,主备直接靠keepalived进行监控,进行主备切换,
同时keepalived去监控所有的代理服务器的健康状态,如果发现有的
代理挂了,会让LVS将请求发到活着的服务器上。
LVS提供一个VIP,无论多少个客户端,项目组,api都只需要去通过这个VIP去
访问后面的服务。
预分区
查询路由。Redis Cluster实现分区的方式。
客户端可以任意连接一台redis,然后redis会将转发给正确的redis。
相当于redirect重定向。
相当于redirect重定向。
Hash算法新增节点,所有的数据需要rehash
一致性hash算法新增节点,造成数据无法被请求到
一致性hash算法新增节点,造成数据无法被请求到
收藏
0 条评论
下一页