redis
2021-02-14 22:49:03 35 举报
AI智能生成
redis
作者其他创作
大纲/内容
管道
一次请求/响应服务器能实现处理新的请求即使旧的请求还未被响应。<br>这样就可以将多个命令发送到服务器,而不用等待回复,最后在一个步骤中读取该答复<br>
$ (printf "PING\r\nPING\r\nPING\r\n"; sleep 1) | nc localhost 6379<br>+PONG<br>+PONG<br>+PONG
使用管道时,服务器会通过队列返回数据,有可能占用大量内存。<br>如果响应数据较大,最好分批次调用
布隆过滤器
持久化
rdb
指定的时间间隔对数据进行快照存储
优点
数据紧凑,适合备份数据
fork一个子进程,最大化利用redis性能,大量数据恢复比aof快
缺点
redis意外断电,会有数据丢失
数据量较大的时候,fork子进程会比较慢
工作方式
fork一个子进程
将数据集写到临时rdb文件
写完后用新rdb文件替换原有rdb文件,并删除元rdb文件
原理:写时复制
aof
记录每次服务器的操作,追加到文件末尾,重启时执行这些命令
优点
可以使用无fsync或者每秒fsync
最多丢失1s的数据
只进行日志追加,写入失败可以用redis-check-aof工具修复
文件过大时会进行重写,包含恢复数据集的最小命令集
BGREWRITEAOF
顺序保存所有命令,易读,易处理
缺点
相同数据集比rdb的大
每秒fsync会比rdb慢,关闭fsync基本一样快
开启aof
appendonly yes
持久化方式
每次有新命令追加时,执行一次fsync,非常慢,也非常安全
每秒执行一次fsync,和rdb一样快,但是有肯能会丢失1s的数据
从不fsync,交给操作系统执行,更快,但是也更不安全
写入停机,损坏修复方案
1.创建一个备份
2.用redis-check-aof –fix修复
3.可以用diff -u对比两个文件的不同,手工修复
混合型
重启时优先采用aof,因为数据会比rdb完整
redis可能发生的问题
缓存击穿
某个非热点key,在失效瞬间,有大量并发访问
处理方案
1.设置key为永不过期
2.使用stnx加锁
缓存穿透
客户端请求的数据在缓存和数据库中都不存在
解决方案
1.将空值也缓存到redis,下次直接从redis取,并设置一个过期时间
2.可以设置key的格式,不符合规则的直接过滤掉
缓存雪崩
缓存同一时间大面积失效,请求都打db,导致cpu和负载过高,甚至db直接宕机
处理方案
1.使用哨兵和主从保证高可用
2.进行限流,避免db被打死
3.数据持久化,重启可以恢复缓存
LUA脚本
基本数据类型
String
二进制安全
任何二进制序列都可以作为key
添加,取出
> set mykey somevalue<br>OK<br>> get mykey<br>"somevalue"
原子递增
> set counter 100<br>OK<br>> incr counter<br>(integer) 101<br>> incr counter<br>(integer) 102<br>> incrby counter 50<br>(integer) 152
INCRBY
DECR
DECRBY
一次存储或获取多个key
> mset a 10 b 20 c 30<br>OK<br>> mget a b c<br>1) "10"<br>2) "20"<br>3) "30"
修改或查询键空间
EXISTS
> set mykey hello<br>OK<br>> exists mykey<br>(integer) 1<br>> del mykey<br>(integer) 1<br>> exists mykey<br>(integer) 0
DEL
DEL命令返回1或0标识值是被删除(值存在)或者没被删除(key对应的值不存在)
TYPE
> set mykey x<br>OK<br>> type mykey<br>string<br>> del mykey<br>(integer) 1<br>> type mykey<br>none
Redis超时:数据在限定时间内存活
设置超时
> set key some-value<br>OK<br>> expire key 5<br>(integer) 1<br>> get key (immediately)<br>"some-value"<br>> get key (after some time)<br>(nil)
PERSIST去除超时时间
剩余存活时间TTL
> set key 100 ex 10<br>OK<br>> ttl key<br>(integer) 9
List
LinkedList实现,不是数组
1.能快速在列表上添加元素
2.可以再常数时间获取常数长度
命令
LPUSH
向list的左边(头部)添加一个新元素
RPUSH
向list的右边(尾部)添加一个元素
LRANGE
取list一定范围内的元素
带有2个索引,-1表示最后一个元素,-2表示倒数第二个元素
使用
> rpush mylist A<br>(integer) 1<br>> rpush mylist B<br>(integer) 2<br>> lpush mylist first<br>(integer) 3<br>> lrange mylist 0 -1<br>1) "first"<br>2) "A"<br>3) "B"
> rpush mylist 1 2 3 4 5 "foo bar"<br>(integer) 9<br>> lrange mylist 0 -1<br>1) "first"<br>2) "A"<br>3) "B"<br>4) "1"<br>5) "2"<br>6) "3"<br>7) "4"<br>8) "5"<br>9) "foo bar"
pop删除元素,并返回删除的值
> rpush mylist a b c<br>(integer) 3<br>> rpop mylist<br>"c"<br>> rpop mylist<br>"b"<br>> rpop mylist<br>"a"
没有可以弹出的元素
> rpop mylist<br>(nil)
LTRIM把list从左边截取指定长度
> rpush mylist 1 2 3 4 5<br>(integer) 5<br>> ltrim mylist 0 2<br>OK<br>> lrange mylist 0 -1<br>1) "1"<br>2) "2"<br>3) "3"
应用场景
聊天系统
不同进程传递消息的队列
按添加顺序访问,不需要orderby,可以很容易实现百万级别
实现生产者消费者模型
非安全队列
BRPOP
BLPOP
安全队列
RPOPLPUSH
BRPOPLPUSH
Set
Set 是 String 的无序排列
命令
sadd添加
> sadd myset 1 2 3<br>(integer) 3
smembers获取成员
> smembers myset<br>1. 3<br>2. 1<br>3. 2
sismember成员是否存在
> sismember myset 3<br>(integer) 1<br>> sismember myset 30<br>(integer) 0
sinter取交集
> sinter tag:1:news tag:2:news tag:10:news tag:27:news<br>... results here ...
sunion取并集
sdiff取差集
spop获取一个元素,并删除
SRANDMEMBER获取随机元素不删除
应用
假设新闻 ID 1000 被打上了 1,2,5 和 77 四个标签,<br>我们可以使用一个 set 把 tag ID 和新闻条目关联起来:<br>
表示对象间的关系
> sadd news:1000:tags 1 2 5 77
(integer) 4
所有被打上相同标签的新闻列表:
> sadd tag:1:news 1000<br>(integer) 1<br>> sadd tag:2:news 1000<br>(integer) 1<br>> sadd tag:5:news 1000<br>(integer) 1<br>> sadd tag:77:news 1000<br>(integer) 1
获取一个对象的所有 tag
> smembers news:1000:tags
1. 5
2. 1
3. 77
4. 2
扑克游戏
创建一副牌
> sadd deck C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 CJ CQ CK<br> D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 DJ DQ DK H1 H2 H3<br> H4 H5 H6 H7 H8 H9 H10 HJ HQ HK S1 S2 S3 S4 S5 S6<br> S7 S8 S9 S10 SJ SQ SK<br> (integer) 52
复用这副牌<br>sunionstore(取并集)
> sunionstore game:1:deck deck<br>(integer) 52
发牌
> spop game:1:deck
"C6"
> spop game:1:deck
"CQ"
> spop game:1:deck
"D1"
> spop game:1:deck
"CJ"
> spop game:1:deck
"SJ"
scard获取set的基数
> scard game:1:deck<br>(integer) 47
Hash
由键值对组成
添加删除
hset
hget
hmset
hgetAll
> hmset user:1000 username antirez birthyear 1977 verified 1<br>OK<br>> hget user:1000 username<br>"antirez"<br>> hget user:1000 birthyear<br>"1977"<br>> hgetall user:1000<br>1) "username"<br>2) "antirez"<br>3) "birthyear"<br>4) "1977"<br>5) "verified"<br>6) "1"
原子增加
HINCRBY
> hincrby user:1000 birthyear 10
(integer) 1987
> hincrby user:1000 birthyear 10
(integer) 1997
小的hash用特殊的编码存储,节省内存
Sorted Set
像set和hash的混合型
双端口的结构实现包含一个链表和一个hash表
命令
zadd添加
> zadd hackers 1940 "Alan Kay"<br>(integer) 1<br>> zadd hackers 1957 "Sophie Wilson"<br>(integer 1)<br>> zadd hackers 1953 "Richard Stallman"<br>(integer) 1<br>> zadd hackers 1949 "Anita Borg"<br>(integer) 1<br>> zadd hackers 1965 "Yukihiro Matsumoto"<br>(integer) 1<br>> zadd hackers 1914 "Hedy Lamarr"<br>(integer) 1<br>> zadd hackers 1916 "Claude Shannon"<br>(integer) 1<br>> zadd hackers 1969 "Linus Torvalds"<br>(integer) 1<br>> zadd hackers 1912 "Alan Turing"<br>(integer) 1
zadd key score value
时间复杂度O(log(N))
zrange返回范围内已经排序好的元素
> zrange hackers 0 -1<br>1) "Alan Turing"<br>2) "Hedy Lamarr"<br>3) "Claude Shannon"<br>4) "Alan Kay"<br>5) "Anita Borg"<br>6) "Richard Stallman"<br>7) "Sophie Wilson"<br>8) "Yukihiro Matsumoto"<br>9) "Linus Torvalds"
ZREVRANGE倒叙排序
> zrevrange hackers 0 -1<br>1) "Linus Torvalds"<br>2) "Yukihiro Matsumoto"<br>3) "Sophie Wilson"<br>4) "Richard Stallman"<br>5) "Anita Borg"<br>6) "Alan Kay"<br>7) "Claude Shannon"<br>8) "Hedy Lamarr"<br>9) "Alan Turing"
WITHSCORES返回结果带store
> zrange hackers 0 -1 withscores<br>1) "Alan Turing"<br>2) "1912"<br>3) "Hedy Lamarr"<br>4) "1914"<br>5) "Claude Shannon"<br>6) "1916"<br>7) "Alan Kay"<br>8) "1940"<br>9) "Anita Borg"<br>10) "1949"<br>11) "Richard Stallman"<br>12) "1953"<br>13) "Sophie Wilson"<br>14) "1957"<br>15) "Yukihiro Matsumoto"<br>16) "1965"<br>17) "Linus Torvalds"<br>18) "1969"
ZRANGEBYSCORE返回指定区间的列表
-inf小于等于
> zrangebyscore hackers -inf 1950<br>1) "Alan Turing"<br>2) "Hedy Lamarr"<br>3) "Claude Shannon"<br>4) "Alan Kay"<br>5) "Anita Borg"
+inf大于等于
zremrangebyscore返回移除2个score区间的元素的数量
> zremrangebyscore hackers 1940 1960<br>(integer) 4
zrank元素所在位置
> zrank hackers "Anita Borg"<br>(integer) 4
按字典顺序
ZRANGEBYLEX
> zrangebylex hackers [B [P<br>1) "Claude Shannon"<br>2) "Hedy Lamarr"<br>3) "Linus Torvalds"
ZREVRANGEBYLEX
ZREMRANGEBYLEX
ZLEXCOUNT
操作
添加
> zadd hackers 0 "Alan Kay" 0 "Sophie Wilson" 0 "Richard Stallman" 0<br> "Anita Borg" 0 "Yukihiro Matsumoto" 0 "Hedy Lamarr" 0 "Claude Shannon"<br> 0 "Linus Torvalds" 0 "Alan Turing"
查询
> zrange hackers 0 -1<br>1) "Alan Kay"<br>2) "Alan Turing"<br>3) "Anita Borg"<br>4) "Claude Shannon"<br>5) "Hedy Lamarr"<br>6) "Linus Torvalds"<br>7) "Richard Stallman"<br>8) "Sophie Wilson"<br>9) "Yukihiro Matsumoto"
Leader boards排行榜
bitmap
不是一个实际的数据类型,是在String类型上定义的位操作
命令
存取
setbit
SETBIT命令的第一个参数是位号,第二个参数是要设置位的值,即1或0。<br>如果地址位超出当前字符串长度,该命令会自动扩大字符串。<br>
getbit
> setbit key 10 1<br>(integer) 1<br>> getbit key 10<br>(integer) 1<br>> getbit key 11<br>(integer) 0
BITOP<br>对多个bitmap做与,或,异或,非的运算
AND
OR
XOR
NOT
BITCOUNT统计二进制位中值为1的数量
> setbit key 0 1<br>(integer) 0<br>> setbit key 100 1<br>(integer) 0<br>> bitcount key<br>(integer) 2
BITPOS返回第一个值为bit的二进制位置
> SETBIT bits 3 1 # 1000<br>(integer) 0<br>> BITPOS bits 0<br>(integer) 0<br>> BITPOS bits 1<br>(integer) 3
hyperloglogs<br>基数统计
在统计数量很大的情况下,基数统计需要的空间固定,而且小
只会根据输入的元素统计基数,不能像集合,返回具体元素
命令
pfadd添加基数元素
pfcount返回基数估算值
pfmerge将多个基数合并
> pfadd hll a b c d<br> (integer) 1<br> > pfcount hll<br> (integer) 4
地理空间
GEOADD key longitude latitude member [longitude latitude member ...]
redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"<br>(integer) 2<br>redis> GEODIST Sicily Palermo Catania<br>"166274.15156960039"<br>redis> GEORADIUS Sicily 15 37 100 km<br>1) "Catania"<br>redis> GEORADIUS Sicily 15 37 200 km<br>1) "Palermo"<br>2) "Catania"<br>redis>
redis介绍
redis是单线程的
使用传统Blocking IO模型,在等待IO响应的时候,其他客户端的请求都会被阻塞<br>因此采用多路复用IO
原理
调用select函数,同时监控多个文件描述符的可读可写状态
某些文件描述符是可读,可写状态,select 就会返回可读,可写文件描述符的个数
reactor模型
文件事件处理器使用 I/O 多路复用模块同时监听多个 FD,当 accept、read、write 和 close 文件事件产生时,文件事件处理器就会回调 FD 绑定的事件处理器
多路复用
select
保底方案
epoll
linux
evport
solaries
kqueue
macOS/FreeBSD
发布订阅
推送消息的格式
解耦
发布者将消息发布到不同的频道,不需要知道订阅者
订阅者订阅1个或多个频道,不需要知道发布者
订阅者和发布者之间解耦
命令
subscribe
订阅某个频道
> SUBSCRIBE runoobChat
publish
向某个频道推送消息
> PUBLISH runoobChat "Redis PUBLISH test"
unsubscribe
取消订阅
psubscribe
订阅符合条件的多个频道
PUNSUBSCRIBE news.*
punsubscribe
取消所有符合条件的频道
事务
子主题
命令
MULTI
开启一个事务
> MULTI<br>OK<br>> INCR foo<br>QUEUED<br>> INCR bar<br>QUEUED<br>> EXEC<br>1) (integer) 1<br>2) (integer) 1
EXEC
触发并执行事务中的所有命令
DISCARD
放弃事务
> SET foo 1<br>OK<br>> MULTI<br>OK<br>> INCR foo<br>QUEUED<br>> DISCARD<br>OK<br>> GET foo<br>"1"
WATCH
为 Redis 事务提供 check-and-set (CAS)行为
wahch的key被修改,则事务失败
WATCH mykey<br>val = GET mykey<br>val = val + 1<br>MULTI<br>SET mykey $val<br>EXEC
程序不断重试这个操作,知道没有碰撞为止
UNWATCH取消对键的监控
实现原子弹出元素
WATCH zset<br>element = ZRANGE zset 0 0<br>MULTI<br>ZREM zset element<br>EXEC
不支持回滚
只会因为语法而失败,是由于人为因素
保证快速
淘汰策略
always lru
近似LRU,选取少量key,回收最久未被访问的
maxmemory内存限制大小
回收策略
noeviction:返回错误当内存限制达到并且客户端尝试执行会让更多内存被使用的命令(大部分的写入指令,但DEL和几个例外)
allkeys-lru: 尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。
volatile-lru: 尝试回收最少使用的键(LRU),但仅限于在过期集合的键,使得新添加的数据有空间存放。
allkeys-random: 回收随机的键使得新添加的数据有空间存放。
volatile-random: 回收随机的键使得新添加的数据有空间存放,但仅限于在过期集合的键。
volatile-ttl: 回收在过期集合的键,并且优先回收存活时间(TTL)较短的键,使得新添加的数据有空间存放。
子主题
集群
主从复制
涉及3个机制
1.master会发送数据流保证slave的更新
2.断开重连,会进行增量同步
3.无法进行增量同步的时候,会通过快照进行全量同步
默认使用异步复制
复制在master和slave侧都是非阻塞的,可以同时处理查询请求
复制在slave侧大部分都是非阻塞的,可以同时处理查询请求
当 master 关闭持久化时,复制的安全性
应该配置实例来避免重置后自动重启。
master故障自动重启,会导致数据情况,在slave同步时slave也会被清空
sentinel下自动重启做够快也会导致这个问题,因此需要关闭自动重启功能
无需磁盘参与的复制
哨兵
作用:
1.监控
2.提醒
3.自动故障迁移
启动哨兵
redis-server /path/to/sentinel.conf --sentinel
运行一个哨兵最少配置
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
sentinel monitor resque 192.168.1.3 6380 4
sentinel down-after-milliseconds resque 10000
sentinel failover-timeout resque 180000
sentinel parallel-syncs resque 5
下线
主观下线
Subjectively Down, 简称 SDOWN
单个sentinel对服务器做下线判断
客观下线
Objectively Down, 简称 ODOWN
多个sentinel对服务器做sDOWN判断
代理的使用
cluster
sharding分片
未使用一致性hash
redis没使用这种方式
优点:不会全局洗牌
缺点:会有一小部分数据不能命中
hash槽
Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽.集群的每个节点负责一部分hash槽
不保证强一致性
redis Api
高低阶代码实现
hash代码实现
自定义template代码实现
发布订阅代码实现
分布式锁
具备3个特性
1.安全属性(Safety property),排他,同一时间只有一个客户端持有锁
2.活性A(Liveness property A),无死锁,持有锁的客户端宕机,或者网格分裂,仍然可以获取锁
3.活性B(Liveness property B),容错,只要大部分redis节点或者,就能获取锁
SET my_key my_value NX PX milliseconds
风险:如果任务执行时间超过了超时时间,释放的锁,就有可能是其他客户端获得的锁
设置key的时候对value设置一个随机值,释放锁的时候只释放自己随机值的锁
通过lua脚本,实现原子操作
0 条评论
下一页