Redis 与其他 key - value 缓存产品有以下几个特点:
字符串类型是 Redis 中最为基础的数据存储类型,它在 Redis 中是二进制安全的,这便意味着该类型可以接受任何格式的数据,如JPEG图像数据或Json对象描述信息等。在Redis中字符串类型的Value最多可以容纳的数据长度是512M。
10.23.117.188:6379> select 0
OK
10.23.117.188:6379> set name laowang
OK
10.23.117.188:6379>
10.23.117.188:6379> get name
"laowang"
10.23.117.188:6379>
10.23.117.188:6379> setex name 5 lisi
OK
10.23.117.188:6379> get name
"lisi"
10.23.117.188:6379>
10.23.117.188:6379> get name
(nil)
10.23.117.188:6379> ttl name
10.23.117.188:6379> mset a1 python a2 java a3 c++
OK
10.23.117.188:6379> get a1
"python"
10.23.117.188:6379> get a2
"java"
10.23.117.188:6379> get a3
"c++"
10.23.117.188:6379>
10.23.117.188:6379> append a1 "I love you"
(integer) 16
10.23.117.188:6379> get a1
"pythonI love you"
10.23.117.188:6379>
10.23.117.188> set name laowang
OK
10.23.117.188> strlen name
(integer) 7
10.23.117.188:6379> get a2
"java"
10.23.117.188:6379>
10.23.117.188:6379> mget a1 a2 a3
1) "pythonI love you"
2) "java"
3) "c++"
10.23.117.188:6379>
delete key
在大型企业级应用中,分表操作是基本操作,使用多张表存储同类型数据,但是对应的主键 id 必须保证统一性
,不能重复。Oracle 数据库具有 sequence 设定,可以解决该问题,但是 MySQL数据库并不具有类似的机制,那么如何解决?
incr key
incrby key increment
incrbyfloat key increment
decr key
decrby key increment
设置数据具有指定的生命周期
setex key seconds value
psetex key milliseconds value
主页高频访问信息显示控制,例如新浪微博大V主页显示粉丝数与微博数量
set user:id:123456:fans 26970000
set user:id:123456:blogs 428
set user:id:123456:focuss 182
set user:id:123456 {
"id":123456,"name":"李子柒","fans":26970000,"blogs":428, "focus":182}
10.23.117.188:6379> keys *
1) "backup1"
2) "a1"
3) "backup2"
4) "backup4"
5) "a3"
6) "a2"
7) "backup3"
10.23.117.188:6379>
10.23.117.188:6379> keys "a*"
1) "a1"
2) "a3"
3) "a2"
10.23.117.188:6379> exists a1
(integer) 1
10.23.117.188:6379> exists a4
(integer) 0
10.23.117.188:6379>
10.23.117.188:6379> type a1
string
10.23.117.188:6379>
10.23.117.188:6379> del a1 a3
(integer) 2
10.23.117.188:6379>
10.23.117.188:6379> expire a2 5
(integer) 1
10.23.117.188:6379> get a2
"java"
10.23.117.188:6379> get a2
(nil)
10.23.117.188:6379>
10.23.117.188:6379> setex a1 10 python
OK
10.23.117.188:6379> ttl a1
(integer) 3
10.23.117.188:6379> ttl a1
(integer) -2
10.23.117.188:6379> ttl a1
(integer) -2
10.23.117.188:6379> ttl a1
(integer) -2
10.23.117.188:6379>
对象类数据的存储如果具有较频繁的更新需求操作,,则用string类型会显得笨重
hset key filed value [field value……]
10.23.117.188:6379> hset person name laowang age 18
(integer) 2
10.23.117.188:6379>
hget key field
10.23.117.188:6379> type person
hash
10.23.117.188:6379> hget person name
"laowang"
10.23.117.188:6379> hget person age
"18"
10.23.117.188:6379>
10.23.117.188:6379> hmset person name lisi age 20
OK
10.23.117.188:6379> hmget person name age
1) "lisi"
2) "20"
10.23.117.188:6379>
10.23.117.188:6379> hgetall person
1) "name"
2) "lisi"
3) "age"
4) "20"
10.23.117.188:6379>
10.23.117.188:6379> hkeys person
1) "name"
2) "age"
10.23.117.188:6379> hvals person
1) "lisi"
2) "20"
10.23.117.188:6379>
10.23.117.188:6379> hdel person
(error) ERR wrong number of arguments for 'hdel' command
10.23.117.188:6379> hdel person name age
(integer) 2
10.23.117.188:6379>
del key
10.23.117.188:6379> hset person name laowang age 18
(integer) 2
10.23.117.188:6379> del person
(integer) 1
10.23.117.188:6379> hget person
(error) ERR wrong number of arguments for 'hget' command
10.23.117.188:6379> hget person name
10.23.117.188:6379> hset person name laowang age 18
(integer) 2
10.23.117.188:6379> hgetall person
1) "name"
2) "laowang"
3) "age"
4) "18"
10.23.117.188:6379> hset person name lisi age 20
(integer) 0
10.23.117.188:6379> hgetall person
1) "name"
2) "lisi"
3) "age"
4) "20"
10.23.117.188:6379>
代码实现:
127.0.0.1:6379> hset userid:001 goodsid:100 5
(integer) 1
127.0.0.1:6379> hget userid:001 goodsid:100
"5"
127.0.0.1:6379> hset userid:001 goodsid:101 10
(integer) 1
127.0.0.1:6379> hgetall userid:001
1) "goodsid:100"
2) "5"
3) "goodsid:101"
4) "10"
127.0.0.1:6379> hincrby userid:001 goodsid:101 10
(integer) 20
127.0.0.1:6379> hgetall userid:001
1) "goodsid:100"
2) "5"
3) "goodsid:101"
4) "20"
127.0.0.1:6379>
127.0.0.1:6379> hdel userid:001 goodsid:101
(integer) 1
127.0.0.1:6379> hgetall userid:001
1) "goodsid:100"
2) "5"
127.0.0.1:6379>
127.0.0.1:6379> del userid:001
(integer) 1
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379>
注意:
思考:当前设计是否加速了购物车的呈现
当前仅仅是将数据存储到了redis中,并没有起到加速的作用,商品信息还需要二次查询关联的关系型数据库
新方案:
127.0.0.1:6379> hmset userid:002 goodsid:100:nums 10 goodsid:100:info {
.....}
OK
127.0.0.1:6379> hgetall userid:002
1) "goodsid:100:nums"
2) "10"
3) "goodsid:100:info"
4) "{.....}"
127.0.0.1:6379>
通常,由于不同的人可能买的商品一样,因此fields2会重复,思路是把fields2独立出一个hash,并且可以用
hsetnx key field value 去重
127.0.0.1:6379> hmset userid:01 c30 1000 c50 1000 c100 1000
OK
127.0.0.1:6379> hgetall userid:01
1) "c30"
2) "1000"
3) "c50"
4) "1000"
5) "c100"
6) "1000"
127.0.0.1:6379>
商家01卖出c50 10张
127.0.0.1:6379> hincrby userid:01 c50 -10
(integer) 990
127.0.0.1:6379> hgetall userid:01
1) "c30"
2) "1000"
3) "c50"
4) "990"
5) "c100"
6) "1000"
127.0.0.1:6379>
列表的元素类型为string,最多223-1个元素
数据存储需求:存储多个数据,并对数据进入存储空间的顺序进行区分
需要的存储结构:一个存储空间保存多个数据,且通过数据可以体现进入顺序
10.23.117.188:6379> lpush a1 a b c
(integer) 3
10.23.117.188:6379> lrange a1 0 2
1) "c"
2) "b"
3) "a"
10.23.117.188:6379> rpush a1 0 1
(integer) 5
10.23.117.188:6379> lrange a1 0 5
1) "c"
2) "b"
3) "a"
4) "0"
5) "1"
10.23.117.188:6379>
在指定元素的前或后插⼊新元素
linsert key before或after 现有元素 新元素
例3:在键为’a1’的列表中元素’b’前加⼊’3’
10.23.117.188:6379> linsert a1 before b 3
(integer) 6
10.23.117.188:6379> lrange a1 0 6
1) "c"
2) "3"
3) "b"
4) "a"
5) "0"
6) "1"
10.23.117.188:6379>
10.23.117.188:6379> lrange a1 0 0
1) "c"
10.23.117.188:6379> lrange a1 0 5
1) "c"
2) "3"
3) "b"
4) "a"
5) "0"
6) "1"
10.23.117.188:6379> lrange a1 0 6
1) "c"
2) "3"
3) "b"
4) "a"
5) "0"
6) "1"
10.23.117.188:6379> lrange a1 0 7
1) "c"
2) "3"
3) "b"
4) "a"
5) "0"
6) "1"
10.23.117.188:6379> lrange a1 0 -1
1) "c"
2) "3"
3) "b"
4) "a"
5) "0"
6) "1"
10.23.117.188:6379> lrange a1 0 -2
1) "c"
2) "3"
3) "b"
4) "a"
5) "0"
10.23.117.188:6379>
lset key index element
例如 将a1 下标为2 的元素修改为 haha
10.23.117.188:6379> lset a1 2 haha
OK
10.23.117.188:6379> lrange a1 0 -1
1) "c"
2) "3"
3) "haha"
4) "a"
5) "0"
6) "1"
10.23.117.188:6379>
10.23.117.188:6379> lpush a2 a b a b a b
(integer) 6
10.23.117.188:6379> lrange a2 0 -1
1) "b"
2) "a"
3) "b"
4) "a"
5) "b"
6) "a"
10.23.117.188:6379>
从a2 右侧删除两个a
10.23.117.188:6379> lrem a2 -2 a
(integer) 2
10.23.117.188:6379> lrange a2 0 -1
1) "b"
2) "a"
3) "b"
4) "b"
10.23.117.188:6379>
127.0.0.1:6379> lpush list1 a b c
(integer) 3
127.0.0.1:6379> lrange list1 0 -1
1) "c"
2) "b"
3) "a"
127.0.0.1:6379> blpop list1 5
1) "list1"
2) "c"
127.0.0.1:6379> blpop list1 5
1) "list1"
2) "b"
127.0.0.1:6379> blpop list1 5
1) "list1"
2) "a"
127.0.0.1:6379> blpop list1 5
(nil)
(5.09s)
127.0.0.1:6379>
场景2
twitter、新浪微博、腾讯微博,keep应用中个人用户的关注列表需要按照用户的关注顺序进行展示,粉丝列表需要将最
近关注的粉丝列在前面
场景3
新闻、资讯类网站如何将最新的新闻或资讯按照发生的时间顺序展示?
企业运营过程中,系统将产生出大量的运营数据,如何保障多台服务器操作日志的统一顺序输出?
解决方案
总结:redis 应用于最新消息展示 ,redis 应用于具有操作先后顺序的数据控制 。
,
sadd key member [member]
例1:向键’a3’的集合中添加元素’zhangsan’、‘lisi’、‘wangwu’
10.23.117.188:6379> sadd a3 zhangsan lisi wangwu
(integer) 3
10.23.117.188:6379>
smembers key
获取a3 所有元素
10.23.117.188:6379> smembers a3
1) "wangwu"
2) "zhangsan"
3) "lisi"
10.23.117.188:6379>
删除指定元素
srem key member [member]
10.23.117.188:6379> srem a3 wangwu
(integer) 1
10.23.117.188:6379> smembers a3
1) "zhangsan"
2) "lisi"
10.23.117.188:6379>
获取集合数据总量
scard key
判断集合中是否包含指定数据
sismember key member
场景1
每位用户首次使用今日头条时会设置几项爱好的内容,但是后期为了增加用户的活跃度、兴趣点,必须让用户对其他信息类别逐渐产生兴趣,增加客户留存度,如何实现?
srandmember key [count]
spop key [count]
127.0.0.1:6379> sadd news n1 n2 n3 n4 n5 # 添加新闻
(integer) 5
127.0.0.1:6379> scard news
(integer) 5
127.0.0.1:6379> smembers news # 查看新闻
1) "n4"
2) "n3"
3) "n2"
4) "n1"
5) "n5"
127.0.0.1:6379> srandmember news 2 # 随机推送两条,不删除
1) "n4"
2) "n2"
127.0.0.1:6379> smembers news
1) "n4"
2) "n3"
3) "n2"
4) "n1"
5) "n5"
127.0.0.1:6379> spop news 3 # 随机推送三条,删除
1) "n4"
2) "n2"
3) "n3"
127.0.0.1:6379> smembers news
1) "n1"
2) "n5"
127.0.0.1:6379>
Trips: redis 应用于随机推荐类信息检索,例如热点歌单推荐,热点新闻推荐,热卖旅游线路,应用APP推荐, 大V推荐等
场景2
脉脉为了促进用户间的交流,保障业务成单率的提升,需要让每位用户拥有大量的好友,事实上职场新人不具有更多的职场好友,如何快速为用户积累更多的好友?
新浪微博为了增加用户热度,提高用户留存性,需要微博用户在关注更多的人,以此获得更多的信息或热门话题,如何提高用户关注他人的总量?
QQ新用户入网年龄越来越低,这些用户的朋友圈交际圈非常小,往往集中在一所学校甚至一个班级中,如何帮助用户快速积累好友用户带来更多的活跃度?
微信公众号是微信信息流通的渠道之一,增加用户关注的公众号成为提高用户活跃度的一种方式,如何帮助用户积累更多关注的公众号?
美团外卖为了提升成单量,必须帮助用户挖掘美食需求,如何推荐给用户最适合自己的美食?
业务分析
1. 用户与用户之间的关联度(交集,并集,差集),用户与其他用户附加信息之间的关联度。
求两个集合的交、并、差集
sinter key1 [key2]
sunion key1 [key2]
sdiff key1 [key2]
求两个集合的交、并、差集并存储到指定集合中
sinterstore destination key1 [key2]
sunionstore destination key1 [key2]
sdiffstore destination key1 [key2]
将指定数据从原始集合中移动到目标集合中
smove source destination member
127.0.0.1:6379> sadd s1 a b c d # 创建集合s1
(integer) 4
127.0.0.1:6379> sadd s2 b c e f g # 创建集合s2
(integer) 5
127.0.0.1:6379> sinter s1 s2 # 交集
1) "b"
2) "c"
127.0.0.1:6379> sunion s1 s2 # 并集
1) "b"
2) "d"
3) "a"
4) "e"
5) "g"
6) "f"
7) "c"
127.0.0.1:6379> sdiff s1 s2 # 差集
1) "a"
2) "d"
127.0.0.1:6379> sinterstore s3 s1 s2 # 将s1 s2的交集放入s3
(integer) 2
127.0.0.1:6379> smembers s3
1) "b"
2) "c"
127.0.0.1:6379> smove s1 s2 a # 将s1 中的a放入s2
(integer) 1
127.0.0.1:6379> smembers s1
1) "b"
2) "d"
3) "c"
127.0.0.1:6379> smembers s2
1) "b"
2) "a"
3) "e"
4) "g"
5) "c"
6) "f"
Tips:
set注意事项
场景3
公司对旗下新的网站做推广,统计网站的PV (Page View)(访问量),UV (Unique Visitor)(独立访客),IP(Internet Protocol)(独立IP)。
PV:网站被访问次数,可通过刷新页面提高访问量
UV:网站被不同用户访问的次数,可通过cookie统计访问量,相同用户切换IP地址,UV不变
IP:网站被不同IP地址访问的总次数,可通过IP地址统计访问量,相同IP不同用户访问,IP不变
解决方案
# 记录ip
127.0.0.1:6379> sadd ip 1.2.3.4 # 记录ip1
(integer) 1
127.0.0.1:6379> sadd ip 1.2.4.5 # 记录ip2
(integer) 1
127.0.0.1:6379> sadd ip 1.2.3.5 # 记录ip3
(integer) 1
127.0.0.1:6379> sadd ip 1.2.4.5 # 重复ip访问
(integer) 0
127.0.0.1:6379> scard ip # 查看访问统计量
(integer) 3
127.0.0.1:6379>
# 记录Pv
127.0.0.1:6379> incrby pv 1
(integer) 1
127.0.0.1:6379> incr pv
(integer) 2
127.0.0.1:6379> incr pv
(integer) 3
127.0.0.1:6379> incr pv
(integer) 4
127.0.0.1:6379> get pv
"4"
场景 4
黑名单
资讯类信息类网站追求高访问量,但是由于其信息的价值,往往容易被不法分子利用,通过爬虫技术,快速获取信息,个别特种行业网站信息通过爬虫获取分析后,可以转换成商业机密进行出售。例如第三方火车票、机票、酒店刷票代购软件,电商刷评论、刷好评。 同时爬虫带来的伪流量也会给经营者带来错觉,产生错误的决策,有效避免网站被爬虫反复爬取成为每个网站都要考虑的基本问题。在基于技术层面区分出爬虫用户后,需要将此类用户进行有效的屏蔽,这就是黑名单的典型应用。
ps:不是说爬虫一定做摧毁性的工作,有些小型网站需要爬虫为其带来一些流量。
白名单
对于安全性更高的应用访问,仅仅靠黑名单是不能解决安全问题的,此时需要设定可访问的用户群体,依赖白名单做更为苛刻的访问验证。
解决方案
Tips 12:
redis 应用于基于黑名单与白名单设定的服务控制
zadd key score member [score member……]
例:向键’a4’的集合中添加元素’lisi’、‘wangwu’、‘zhaoliu’、‘zhangsan’,权重分别为4、5、6、3
10.23.117.188:6379> zadd a4 4 lisi 5 wangwu 6 zhaoliu 3 zhangsan
(integer) 4
10.23.117.188:6379>
zrange key start stop
反向查看
zrevrange key start stop [WITHSCORES]
10.23.117.188:6379> zrange a4 0 -1
1) "zhangsan"
2) "lisi"
3) "wangwu"
4) "zhaoliu"
127.0.0.1:6379> zrange a4 0 -1 withscores
1) "zhangsan"
2) "3"
3) "lisi"
4) "4"
5) "wangwu"
6) "5"
7) "zhaoliu"
8) "6"
127.0.0.1:6379>
返回score值在min和max之间的成员
zrangebyscore key min max
获取键’a4’的集合中权限值在5和6之间的成员
zrangebyscore a4 5 6
10.23.117.188:6379> zrangebyscore a4 5 6
1) "wangwu"
2) "zhaoliu"
10.23.117.188:6379>
zscore key member
例:获取键’a4’的集合中元素’zhangsan’的权重
10.23.117.188:6379> zscore a4 zhangsan
"3"
10.23.117.188:6379>
获取集合数据总量
zcard key
zcount key min max
交集并集操作
zinterstore destination numkeys key [key ...]
zunionstore destination numkeys key [key ...]
zrem key member1 member2 ...
例:删除集合’a4’中元素’zhangsan’
zrem a4 zhangsan
10.23.117.188:6379> zrem a4 zhangsan
(integer) 1
10.23.117.188:6379> zrange a4 0 -1
1) "lisi"
2) "wangwu"
3) "zhaoliu"
10.23.117.188:6379>
zremrangebyscore key min max
例:删除集合’a4’中权限在5、6之间的元素
zremrangebyscore a4 5 6
10.23.117.188:6379> zremrangebyscore a4 5 6
(integer) 2
10.23.117.188:6379> zrange a4 0 -1
1) "lisi"
10.23.117.188:6379>
注意:
场景1
各类资源网站TOP10(电影,歌曲,文档,电商,游戏等)
聊天室活跃度统计
游戏好友亲密度
热点直播间
业务分析
为所有参与排名的资源建立排序依据
解决方案
zrank key member
zrevrank key member
127.0.0.1:6379> zadd movies 150 aaa 87 bbb 345 ccc 29 ddd # 添加电影点击量
(integer) 4
:6379> zcard movies
(integer) 4
127.0.0.1:6379> zrank movies aaa # aaa的电影点击排名
(integer) 2
127.0.0.1:6379> zrank movies ccc
(integer) 3
127.0.0.1:6379> zrank movies bbb
(integer) 1
127.0.0.1:6379> zrank movies ddd
(integer) 0
127.0.0.1:6379> zscore movies aaa # 查看aaa的电影点击量
"150"
127.0.0.1:6379> zincrby movies 1 aaa # 给aaa的电影增加点击量
"151"
127.0.0.1:6379>
sorted_set 类型数据操作的注意事项
score保存的数据存储空间是64位,如果是整数范围是-9007199254740992~9007199254740992
score保存的数据也可以是一个双精度的double值,基于双精度浮点数的特征,可能会丢失精度,使用时
候要慎重
sorted_set 底层存储还是基于set结构的,因此数据不能重复,如果重复添加相同的数据,score值将被反
复覆盖,保留最后一次修改的结果
场景2
127.0.0.1:6379> time
1) "1604388343"
2) "453297"
127.0.0.1:6379> zadd huiyuan 11111233 uid:001 # 设置11111233时过期
(integer) 1
127.0.0.1:6379> zadd huiyuan 22223333 uid:002
(integer) 1
127.0.0.1:6379> zadd huiyuan 6374638 uid:003
(integer) 1
127.0.0.1:6379> zrange huiyuan 0 -1 wthscores
(error) ERR syntax error
127.0.0.1:6379> zrange huiyuan 0 -1 withscores
1) "uid:003"
2) "6374638"
3) "uid:001"
4) "11111233"
5) "uid:002"
6) "22223333"
127.0.0.1:6379>
场景3
任务/消息权重设定应用
当任务或者消息待处理,形成了任务队列或消息队列时,对于高优先级的任务要保障对其优先处理,如何实现任务权重管理。
对于带有权重的任务,优先处理权重高的任务,采用score记录权重即可
多条件任务权重设定
解决方案如下:
连接redis后运行 config set stop-writes-on-bgsave-error no 命令
关闭配置项stop-writes-on-bgsave-error解决该问题。
业务场景一
127.0.0.1:6379> setex uid:00415 60 1 # 设置uid:00415过期时间并统计计数
OK
127.0.0.1:6379> get uid:00415
"1"
127.0.0.1:6379> incr uid:00415
(integer) 2
127.0.0.1:6379> incr uid:00415
(integer) 3
127.0.0.1:6379> incr uid:00415
(integer) 4
127.0.0.1:6379> get uid:00415
"4"
解决方案改良
127.0.0.1:6379> get uid:00415
(nil)
127.0.0.1:6379> set uid:00415 9223372036854775797 # 最大值减10
OK
127.0.0.1:6379> incr uid:00415 # 加1
(integer) 9223372036854775798
127.0.0.1:6379> incr uid:00415
(integer) 9223372036854775799
127.0.0.1:6379> incr uid:00415
(integer) 9223372036854775800
127.0.0.1:6379> incr uid:00415
(integer) 9223372036854775801
127.0.0.1:6379> incr uid:00415
(integer) 9223372036854775802
127.0.0.1:6379> incr uid:00415
(integer) 9223372036854775803
127.0.0.1:6379> incr uid:00415
(integer) 9223372036854775804
127.0.0.1:6379> incr uid:00415
(integer) 9223372036854775805
127.0.0.1:6379> incr uid:00415
(integer) 9223372036854775806
127.0.0.1:6379> incr uid:00415
(integer) 9223372036854775807
127.0.0.1:6379> incr uid:00415
(error) ERR increment or decrement would overflow # 超过加10 报错,从而可以捕获错误判断
127.0.0.1:6379>
业务场景二
使用微信的过程中,当微信接收消息后,会默认将最近接收的消息置顶,当多个好友及关注的订阅号同时发
送消息时,该排序会不停的进行交替。同时还可以将重要的会话设置为置顶。一旦用户离线后,再次打开微
信时,消息该按照什么样的顺序显示?
解决方案
# 放入消息的顺序为 200 300 400 200 300
127.0.0.1:6379> lrem 100 1 200
(integer) 0
127.0.0.1:6379> lpush 100 200
(integer) 1
127.0.0.1:6379> lrem 100 1 300
(integer) 0
127.0.0.1:6379> lpush 100 300
(integer) 2
127.0.0.1:6379> lrem 100 1 400
(integer) 0
127.0.0.1:6379> lpush 100 400
(integer) 3
127.0.0.1:6379> lrem 100 1 200
(integer) 1
127.0.0.1:6379> lpush 100 200
(integer) 3
127.0.0.1:6379> lrem 100 1 300
(integer) 1
127.0.0.1:6379> lpush 100 300
(integer) 3
127.0.0.1:6379> lrange 100 0 -1
1) "300"
2) "200"
3) "400" # 最终从右到左(从新到旧)的顺序为300 200 400
127.0.0.1:6379>