Redis(Remote Dictionary Service)远程字典服务,内存数据库,kv数据库,数据结构数据库
1. 应用:
朋友圈点赞数、评论、点击数(hash)
记录朋友圈说说列表(排序)、快速显示(list)
记录文章的标题、摘要、作者和封面,列表页显示(hash)
朋友圈点赞用户ID、评论ID、显示去重计数(zset)
缓存热点数据,减少数据库压力(hash)
朋友圈说说id(计数器string)
集合交并差记录好友关系(set)
游戏战绩(list)
2. 数据结构&存储结构
string:二进制安全字符串(长度 = std::string)
hash:散列表/字典(=unordered_map)【核心】
list:首尾相接双向队列
set:集合(无序唯一)
zset:集合(有序唯一)【核心——分布式定时器、限流】
3. value编码规则
内存分配器认为,小于64byte=小字符串,大于64byte=大字符串
4. 使用
redis-server //开启Redis服务器
redis-cli -h 127.0.0.1 -a 123456 //开启Redis客户端
string
- 字符数组,动态字符串,长度小于1M时,加倍扩容;超过1M每次多扩容1M;最大长度为512M
- 二进制安全字符串,可以存图片、二进制协议等数据
- 字符串长度小于20且能转为整数,用int存储;字符串长度小于等于44,用embstr存储;字符串长度大于44,用raw存储
SET GET INCR INCRBY DECR DECRBY DEL SETNX
设置 获取 原子+1 原子+num 原子-1 原子-num 删除 设置不存在的key
SETBIT key offset value GETBIT key offset BITCOUNT key
设置value在offset偏移的bit值 返回key对应string在offset位的bit值 统计bit1的个数
应用:对象存储 、累加器(incr reads)、分布式锁(setnx lock 1)、位运算
list
- 双向链表实现,列表首尾操作(删除和增加)时间复杂度 O(1) ;查找中间元素时间复杂度为O(n)
- gzip压缩:元素长度小于48不压缩;元素压缩前后长度差不超过8,不压缩(存储:双向列表、压缩列表)
LPUSH LPOP RPUSH RPOP LRANGE LREM BRPOP DEL
LRANGE key start end LREM key count value BRPOP key timeout 队列为空时阻塞(超时时间s+延时队列)
应用:栈(LPUSH+LPOP) 队列(LPUSH+RPOP) 阻塞队列(LPUSH+BRPOP/RPUSH+RLPOP)
异步消息队列(web和server之间使用redis)获取固定窗口记录(LPUSH+LTRIM lists 0 4 固定长度为5)
hash
- 散列表,在很多高级语言当中包含这种数据结构;c++ unordered_map 通过 key 快速索引value;
- 节点数量<=512且字符串长度<=64用压缩列表存储,否则字典存储
HGET HSET HMSET HMGET HINCRBY HLEN HDEL
应用:存储对象,购物车
set
- 集合, 用来存储唯一性字段,不要求有序;(存储是有序的,但是使用过程中不要求有序)
- 元素为整数且节点数量<=512 用整数数组存储,元素有一个不是整数或数量大于512用字典存储
SADD SCARD SMEMBERS SISMENBER SRANDMEMBER SPOP SDIFF SINTER SUNION
key加member 计算个数 返回成员 判断是否为成员 随机返回成员 移除并返回 求集合的成员差集 交集 并集
应用:抽奖,共同关注,推荐好友
zset
有序集合;用来实现排行榜;它是一个有序唯一(member唯一)结构 【zset: 集合key 分数score 成员member】
数量>128或有一个字符串长度>64,跳表存储;子节点数量<=128且字符串长度<=64压缩列表存储
数量少,压缩列表,节省空间O(n);数量多时,访问性能,O(1) O(logn).
ZADD ZREM ZSCORE ZINCRBY ZCARD ZRANK ZRANGE ZREVRANGE
给集合key添加score-member 删除 返回score score增量 元素个数 返回排名 返回范围内元素 返回逆序
应用:百度热榜、延时队列(zset的有序性,到期时间作为score,在分布式系统:分布式定时器)、
时间窗口限流 (限定userid在period中发生action的max_count)
延时队列:如果一件事A交给B处理,B在指定时间内未完成,就把任务转发给C或回退给(回退、转发方法),可以使用延时队列来实现。A分配给B任务时,把当前时间的记录放入延时队列,获取到超时记录就执行转发或回退删除操作。
一般,加锁失败有几种处理办法:
1、直接抛出异常,通知用户稍后重试;
2、sleep 一会再重试;
3、将请求转移至延时队列,过一会再试;
使用zset来实现延时队列,用到了zset的有序性,把到期时间作为score,zrangebyscore找出到期任务,执行zrem删除。
为了避免多线程竞争,可以采用lua脚本原子执行这两个命令,避免多个线程同时获取到任务,但是无法删除,白执行了操作。
解决:漏斗限流
时间窗口限流:系统限定用户的某个行为在指定的时间里只能发生N次
维护一次时间窗口,将窗口外的记录全部清理掉,只保留窗口内的记录;
缺点:记录了所有时间窗口内的数据,如果这个量很大,不适合做这样的限流