- 1. Redis基本操作
- 1. 启动redis服务并连接redis客户端
- 2. 切换数据库
- 3. 删除键
- 4. 删除当前数据库的所有键
- 5. 为指定键设置过期时间
- 2. String类型的使用场景
- 1. String数据类型的API
- 2. 单个值缓存
- 3. 对象缓存
- 4. 计数器
- 5. 唯一自增的ID或者流水号
- 6. 文章阅读数或者网页浏览数统计
- 7. 分布式锁
- 3. Hash类型的使用场景
- 1. Hash数据类型的常规操作
- 2. 实现购物车
- 3. 作为计数器
- 4. List类型的使用场景
- 1. List数据类型的基本操作
- 2. List模拟数据结构
- 5. Set类型的使用场景
- 1. Set数据类型的基本操作
- 1. 微博瘫痪
- 2. 抽奖逻辑
- 3. 文章点赞或者投票
- 4. 共同好友统计
- 6. Zset类型的使用场景
- 1. Zset基本操作
- 2. 利用Zset实现限流
- 3. 新闻排行榜
- 4. 直播打赏排名
redis命令大全可参考:http://doc.redisfans.com/index.html
1. Redis基本操作 1. 启动redis服务并连接redis客户端// 启动redis服务 [root@node1 etc]# redis-server /etc/redis.conf // 连接redis客户端,指定服务器,端口,密码 [root@node1 etc]# cat ~/.passwd/local_redis_passwd d9be7d760e129d02 [root@node1 etc]# redis-cli -h localhost -p 6379 -a d9be7d760e129d02 // 测试是否连接成功 localhost:6379> ping PONG localhost:6379>2. 切换数据库
Redis单机服务器总共有16(0~15)个数据库,而且各个数据库之间是不能共享数据的。登录之后默认选择db0。要切换数据库,可以使用如下命令:
localhost:6379> select 1 OK localhost:6379[1]> set username zhangsan OK localhost:6379[1]> get username "zhangsan" localhost:6379[1]>3. 删除键
如果要在业务中删除不需要的键,可以使用del命令:
localhost:6379[1]> del username (integer) 1 localhost:6379[1]>4. 删除当前数据库的所有键
要删除当前数据库的所有键,可以执行flushdb命令:
localhost:6379[1]> set username lisi OK localhost:6379[1]> flushdb OK localhost:6379[1]>5. 为指定键设置过期时间
为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。
EXPIRE key seconds
localhost:6379> set user_id 1 OK localhost:6379> expire user_id 20 (integer) 1 localhost:6379> ttl user_id (integer) 14 localhost:6379> get user_id "1" localhost:6379>2. String类型的使用场景 1. String数据类型的API
1、设置与获取键的内容
localhost:6379[1]> set user zhangsan OK localhost:6379[1]> get user "zhangsan"
2、为键设置新的内容并返回旧值
localhost:6379[1]> keys * (empty list or set) localhost:6379[1]> set user zhangsan OK localhost:6379[1]> get user "zhangsan" localhost:6379[1]> getset user lisi "zhangsan" localhost:6379[1]> get user "lisi" localhost:6379[1]>
3、设置key的过期时间,以秒为时间单位
将值 value 关联到 key ,并将 key 的生存时间设为 seconds (以秒为单位)。如果 key 已经存在, SETEX 命令将覆写旧值。 SETEX 是一个原子性(atomic)操作,关联值和设置生存时间两个动作会在同一时间内完成,该命令在 Redis 用作缓存时,非常实用。
SETEX key seconds value
localhost:6379[1]> setex cache_user 60 zhangsan OK localhost:6379[1]> ttl cache_user (integer) 53 localhost:6379[1]> setex cache_user 60 lisi OK localhost:6379[1]> ttl cache_user (integer) 55 localhost:6379[1]>
4、设置key的过期时间,以毫秒为时间单位
这个命令和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间,而不是像 SETEX 命令那样,以秒为单位。
PSETEX key milliseconds value
localhost:6379[1]> psetex cache_user 10000 "haha" OK localhost:6379[1]> get cache_user "haha" localhost:6379[1]> pttl cache_user (integer) 2206 localhost:6379[1]> pttl cache_user (integer) -2 localhost:6379[1]> get cache_user (nil) localhost:6379[1]>
5、设置key的过期时间,以毫秒为时间单位
SETNX key value
将 key 的值设为 value ,当且仅当 key 不存在。若给定的 key 已经存在,则 SETNX 不做任何动作。
localhost:6379> setnx user_id 1 (integer) 1 localhost:6379> setnx user_id 1 (integer) 0
设置值得同时设置过期时间
localhost:6379> set product_id true ex 20 nx OK localhost:6379> set product_id true ex 20 nx (nil)2. 单个值缓存
鉴权中心的token缓存:
1)第一次请求的服务节点生成一个用户token,并存储到Redis服务器,然后可以根据需要设置对应的过期时间,从而保证token过期之后的信息从Redis服务器移除。如果有需要,也可以不设置token在内存中存储的过期时间。
2)之后请求的节点只需要通过get命令从缓存中获取token验证合法性
localhost:6379[1]> psetex cache_userid 10000 "userinfo token" OK localhost:6379[1]> get cache_userid "userinfo token" localhost:6379[1]> pttl cache_userid (integer) 3110 localhost:6379[1]> pttl cache_userid (integer) 523 localhost:6379[1]> pttl cache_userid (integer) -2 localhost:6379[1]> get cache_userid (nil) localhost:6379[1]>3. 对象缓存
用户信息既可能是一个简单的字符串,也可能是一个对象或者高频率访问但是很少修改的数据,这些数据可以作为一个整体对象,如果想要减轻数据库访问的压力,那么可以将其对象进行json序列化后缓存起来。这样的数据可以采用String数据类型进行存储。
localhost:6379[1]> set user_i "{'name':'zhagnsan','age':1}" OK localhost:6379[1]> get user "lisi" localhost:6379[1]>4. 计数器
在高并发的业务场景中,可能会遇到各种各样的业务场景,比如接口限流。在双十一或者其他大型节日中,业务服务器的资源有限,但是在高并发的场景下,过多的请求可能会导致服务器宕机等问题,所以会对接口请求做一些限制,比如限制每秒请求总数为200次,超过200次就等待,等下一秒再次请求,这里用Redis作为计数器的模式来实现。
实现流程如下:
1)首先在接到请求之后设置一个键,然后自增1,如果不存在,则值会被初始化为0。
incr mykey
这里的mykey可能需要特殊处理,因为是根据秒来的,所以当接口得到一个请求之后,应该获取当前时间生成动态的键,如20200101235959:mykey。
2)当接口得到请求后,执行incr yyyyMMddHHmmss:mykey,如果返回结果小于200则进行处理,超过200将等待下一秒处理。
3)因为每秒会生成一个键,为了节省内存空间,可能需要一个定时任务,定时删除这些已经使用过的键。
localhost:6379[1]> incr 202208091101:mykey (integer) 1 localhost:6379[1]> incr 202208091101:mykey (integer) 2 localhost:6379[1]> incr 202208091101:mykey5. 唯一自增的ID或者流水号
在有些业务中,为了提高数据库的读写能力,可能会将一个库根据业务拆分成多个小库、将一张大表拆分成几个小表,比如user01表、user02表,但是又要保证两张表中的ID是全局统一自增的,所以数据库表自带的自增属性是无法使用的。
这时也可以利用Redis的String数据类型实现自增,在新增数据行时,通过调用Redis服务自增功能生成一个自增的数值来实现多张表的唯一自增。
假设目前有2台单独的Redis服务器,那么生成的方式就是这样的,第1台服务器从初始值1开始生成的序列号 :
localhost:6379> incrby id 1 (integer) 1 localhost:6379> incrby id 2 (integer) 3 localhost:6379> incrby id 2 (integer) 5 localhost:6379>
第2台服务器从初始值2开始生成的序列号 :
localhost:6379> incrby id 2 (integer) 2 localhost:6379> incrby id 2 (integer) 4 localhost:6379> incrby id 2 (integer) 6 localhost:6379>
每一台服务器从初始值1~2开始,然后每次累加数值2,则多个节点生成自增且不重复的序列号。
以上只是利用Redis的自增API处理多个表的唯一自增ID而额外提供的一种方案。如果了解雪花算法或其他更简易的方式,则完全没有必要使用Redis的这种操作。
6. 文章阅读数或者网页浏览数统计当我们打开浏览器时,会面临各种各样的网页信息,比如技术博客或者新闻博客等,这些网页里面无不充斥着各种统计数量,比如用户的总点赞数、关注数、粉丝数、帖子的评论数、热度、文章的阅读数和收藏数等。实现这些需求并想要减轻数据库压力,而且对数据的实效性和写文章的频率要求高时,则完全可以使用Redis。
实现文章阅读数量统计需要执行如下命令:
localhost:6379> incr read:count (integer) 1 localhost:6379> get read:count "1" localhost:6379> incr read:count (integer) 27. 分布式锁
随着互联网业务发展和需求的不断变化,应用程序从单体架构一路进化到当下的分布式和微服务架构。在使用单体架构时,多线程操作是通过线程锁来实现资源抢占控制的,但是在分布式系统中需要用其他手段来实现,可以借助于Redis中的setnx命令来完成。
具体操作流程如下:如果键没有值就能新增成功,就表示抢到锁了,否则失败。等到操作成功时释放锁,也就是删除键。
// 给 product_id设置值,如果之前值不存在,则新增成功,返回1,抢到了锁 localhost:6379> setnx product_id true (integer) 1 // 当其他线程想要执行此命令时,返回0,获取锁失败 localhost:6379> setnx product_id true (integer) 0 // 当第一个抢到锁的线程执行完业务之后,就可以删除键,让其他线程抢到锁 localhost:6379> del product_id (integer) 1 // 其他线程抢锁成功 localhost:6379> setnx product_id true (integer) 1 localhost:6379>
其中还存在一个问题:如果第一个线程拿到锁之后还没有执行删除键这个线程就宕机了,就会导致这个锁永久被占用,其他线程的业务将无法执行。对于这个问题,可以通过给键设置过期时间来解决,到达过期时间后让Redis服务自动把这个键删除——释放。
// 抢到锁 localhost:6379> setnx product_id true (integer) 1 // 设置过期时间20毫秒,到期之后键会被删除掉 localhost:6379> expire product_id 20 (integer) 1 // 查询键还有多久过期,值为负数时,说明键已经过期被删除 localhost:6379> ttl product_id (integer) -2 // 键已经被删除 localhost:6379> get product_id (nil) localhost:6379>
上面赋值和设置过期时间是分为两步的。如果要保证原子性,则可以在设置值的同时再次设置过期时间:
localhost:6379> set product_id true ex 20 nx OK localhost:6379> ttl product_id (integer) 17 localhost:6379> get product_id "true" localhost:6379> ttl product_id (integer) 10 localhost:6379> ttl product_id (integer) 3 localhost:6379>3. Hash类型的使用场景
在Redis中,哈希数据类型是指Redis键值对中的值本身又是一个键值对结构。
1. Hash数据类型的常规操作1、设置和获取指定key的值
HSET key field value
将哈希表 key 中的域 field 的值设为 value 。如果 key 不存在,一个新的哈希表被创建并进行 HSET 操作。如果域 field 已经存在于哈希表中,旧值将被覆盖。
HGET key field
返回哈希表 key 中给定域 field 的值。
// 如果 `field` 是哈希表中的一个新建域,并且值设置成功,返回 `1` 。 localhost:6379> hset user name zhangsan (integer) 1 localhost:6379> hset user age 18 (integer) 1 localhost:6379> hget user name "zhangsan" localhost:6379> hget user age "18" // 如果哈希表中 `field` 已经存在且旧值已被新值覆盖,返回 `0` 。 localhost:6379> hset user name list (integer) 0 localhost:6379> hset user age 19 (integer) 0 localhost:6379>
2、返回哈希表 key 中,所有的域和值
返回哈希表 key 中,所有的域和值。在返回值里,紧跟每个域名之后是域的值,所以返回值的长度是哈希表大小的两倍。
localhost:6379> hset user name zhangsan (integer) 1 localhost:6379> hset user age 19 (integer) 1 localhost:6379> hgetall user 1) "name" // field 2) "zhangsan" // value 3) "age" // field 4) "19" // value localhost:6379> hset user name lisi (integer) 0 localhost:6379> hset user age 19 (integer) 0 localhost:6379> hgetall user 1) "name" 2) "lisi" 3) "age" 4) "19" localhost:6379>
3、为哈希表 key 中的域 field 的值加上增量 increment
HINCRBY key field increment
为哈希表 key 中的域 field 的值加上增量 increment 。增量也可以为负数,相当于对给定域进行减法操作。如果 key 不存在,一个新的哈希表被创建并执行 HINCRBY 命令。如果域 field 不存在,那么在执行命令前,域的值被初始化为 0 。
localhost:6379> hexists counter page_view (integer) 0 localhost:6379> hincrby counter page_view 1 (integer) 1 localhost:6379> hget counter page_view "1" localhost:6379> hincrby counter page_view -50 (integer) -49 localhost:6379>2. 实现购物车
购物车的定义非常简单:以每个用户的userid作为Redis的键(Key),每个用户的购物车都是一个哈希表,它存储了商品ID与商品订购数量之间的映射关系。在商品的订购数量出现变化时,操作Redis哈希对购物车进行更新。
如果用户订购某件商品的数量大于0,那么程序会将这件商品的ID以及用户订购该商品的数量添加到Hash中。
localhost:6379> hset userId:1 productId:1 1 (integer) 1 localhost:6379> hgetall userId:1 1) "productId:1" 2) "1"
如果用户购买的商品已经存在于哈希表中,那么新的订购数量会覆盖已有的订购数量。
localhost:6379> hset userId:1 productId:1 5 (integer) 0 localhost:6379> hgetall userId:1 1) "productId:1" 2) "5"
如果用户购买的商品数量不大于0,那么程序将从哈希表中删除该项。
localhost:6379> hdel userId:1 productId:1 (integer) 1 localhost:6379> hgetall userId:1 (empty list or set)3. 作为计数器
Redis中的String数据类型可以用作计数器,实际上Redis的Hash作为计数器的使用也非常广泛,它常被用于记录网站一天、一月、一年的访问数量。每次访问,在对应的field上自增1即可。
记录博客文章每月访问量的命令:
localhost:6379> hincrby mylog 202208 1 (integer) 1 localhost:6379> hincrby mylog 202208 1 (integer) 2 localhost:6379> hincrby mylog 202208 1 (integer) 3
记录商品的好评数量、差评数量的命令:
localhost:6379> hincrby good productId 1 (integer) 1 localhost:6379> hincrby good productId 1 (integer) 2 localhost:6379> hincrby bad productId 1 (integer) 1
记录网站实时在线人数的命令:
localhost:6379> hincrby mysite 202208 1 (integer) 1 localhost:6379> hincrby mysite 202208 1 (integer) 24. List类型的使用场景
Redis的List数据类型是通过链表实现的。这意味着即使用户在列表中有数百万个元素,在列表的开头或结尾添加新元素的操作也会在固定时间内执行。使用LPUSH命令将新元素添加到具有100个元素的列表开头的速度与将元素添加到具有1000万个元素的列表开头的速度相同。
在使用Array实现的列表中,按索引访问元素的速度非常快,而在通过链表实现的列表中访问速度却不是那么快,Redis的List数据类型是通过链表实现的,因为对于数据库系统而言,至关重要的是能够以非常快的方式将元素添加到很长的列表中。我们可以用两种不同的方式来操作列表:第一种是队列,按照先进先出的顺序操作;第二种是栈,按照先进后出的顺序操作。
1. List数据类型的基本操作1、LPUSH 添加元素到列表头部
LPUSH key value [value …]
将一个或多个值 value 插入到列表 key 的表头。如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表头: 比如说,对空列表 mylist 执行命令 LPUSH mylist a b c ,列表的值将是 c b a ,这等同于原子性地执行 LPUSH mylist a 、 LPUSH mylist b 和 LPUSH mylist c 三个命令。如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。
2、LPOP 移除并返回列表 key 的头元素
LPOP key
移除并返回列表 key 的头元素,当 key 不存在时,返回 nil 。
3、LRANGE 返回列表 key 中指定区间内的元素
LRANGE key start stop
返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定。下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
// 添加单个元素到列表头部 localhost:6379> lpush user zhangsan (integer) 1 localhost:6379> lpush user lisi (integer) 2 // 添加重复元素,列表允许重复元素 localhost:6379> lpush user lisi (integer) 3 // 返回列表中的所有元素 localhost:6379> lrange user 0 -1 1) "lisi" 2) "lisi" 3) "zhangsan" // 移除列表中的头部元素 localhost:6379> lpop user "lisi" localhost:6379> lrange user 0 -1 1) "lisi" 2) "zhangsan" // 添加多个元素到列表头部 localhost:6379> lpush user wangwu zhaoliu (integer) 4 localhost:6379> lrange 0 -1 (error) ERR wrong number of arguments for 'lrange' command localhost:6379> lrange user 0 -1 1) "zhaoliu" 2) "wangwu" 3) "lisi" 4) "zhangsan"2. List模拟数据结构
List数据类型有以下特点:
1)List中的元素是有序的,可以通过下标来获取某个元素或者某个范围内的元素列表。
2)List中的元素可以是重复的。
3)可以实现顺序排队和插队的操作。
1、用Redis的List数据类型模拟栈
用Redis的List数据类型模拟栈的数据结构实现先进后出的操作,执行命令如下:
localhost:6379> lpush mystack a (integer) 1 localhost:6379> lpush mystack b (integer) 2 localhost:6379> lpush mystack c (integer) 3 localhost:6379> lpush mystack d e f (integer) 6 localhost:6379> lrange mystack 0 2 1) "f" 2) "e" 3) "d" localhost:6379> lrange mystack 0 -1 1) "f" 2) "e" 3) "d" 4) "c" 5) "b" 6) "a" localhost:6379> lpop mystack "f"
2、Redis的List数据类型模拟队列
用Redis的List数据类型模拟队列的数据结构来实现先进先出的操作,执行命令如下:
localhost:6379> lpush myqueue a (integer) 1 localhost:6379> lpush myqueue b (integer) 2 localhost:6379> lpush myqueue c d e (integer) 5 localhost:6379> lrange myqueue 0 -1 1) "e" 2) "d" 3) "c" 4) "b" 5) "a" localhost:6379> rpop myqueue "a" localhost:6379> lrange myqueue 0 -1 1) "e" 2) "d" 3) "c" 4) "b"
3、阻塞队列
Redis中的列表还具有一项特殊功能,使其适用于实现队列,通常用作进程间通信系统的构建模块:阻止操作。
1)将数据项推入列表,生产者调用lpush命令。
2)从列表中提取数据项,消费者调用rpop。有时列表可能为空,没有任何要处理的数据项,此时rpop就返回null。在这种情况下,消费者被迫等待一段时间,然后使用rpop重试,这称为轮询。不过这样会有一个缺点:强制Redis和客户端处理无用的命令。因为消费处理端在收到null之后会等待一段时间,所以会增加数据项处理的延迟。为了减小延迟,我们可以在两次调用rpop之间等待更少的时间,即对Redis的调用变得越来越无用。
按照先进先出的顺序,如果列表没有元素就等待,执行命令如下:
localhost:6379> lpush mymq a (integer) 1 localhost:6379> lpush mymq b (integer) 2 localhost:6379> lpush mymq c (integer) 3 localhost:6379> lrange mymq 0 -1 1) "c" 2) "b" 3) "a" // 通过阻塞的方式获取元素,如果没有元素则进行等待,直到有元素为止 // 5为没有元素时等待的时间 localhost:6379> brpop mymq 5 1) "mymq" 2) "a" localhost:6379> brpop mymq 5 1) "mymq" 2) "b" localhost:6379> brpop mymq 5 1) "mymq" 2) "c" localhost:6379> brpop mymq 5 (nil) (5.33s) // 等待时间5. Set类型的使用场景
集合元素无序且唯一。因为是无序的,所以不会按照元素插入的先后顺序进行存储。集合类型和列表类型的区别如下:
1)列表可以存储重复元素,集合默认去重。
2)列表顺序存储,而集合是无序存储。
3)列表和集合都支持增、删、改、查,同时集合还支持取多个集合的交集、并集、差集。
1. Set数据类型的基本操作1、添加元素到集合
SADD key member [member …]
将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略。假如 key 不存在,则创建一个只包含 member 元素作成员的集合。返回被添加到集合中的新元素的数量,不包括被忽略的元素。
2、返回集合 key 中的所有成员
返回集合 key 中的所有成员,不存在的 key 被视为空集合。
SMEMBERS key
3、返回集合中的一个随机元素
SRANDMEMBER key [count]
如果命令执行时,只提供了 key 参数,那么返回集合中的一个随机元素。
从 Redis 2.6 版本开始, SRANDMEMBER命令接受可选的 count 参数:
如果 count 为正数,且小于集合基数,那么命令返回一个包含 count 个元素的数组,数组中的元素各不相同。如果 count 大于等于集合基数,那么返回整个集合。如果 count 为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为 count 的绝对值。
该操作和 SPOP相似,但 SPOP将随机元素从集合中移除并返回,而SRANDMEMBER则仅仅返回随机元素,而不对集合进行任何改动。
// 添加单个元素到集合 localhost:6379> sadd member a (integer) 1 localhost:6379> sadd member b (integer) 1 localhost:6379> sadd member c (integer) 1 // 添加多个元素到集合 localhost:6379> sadd member d e f (integer) 3 localhost:6379> sadd member f (integer) 0 // 返回集合 key 中的所有成员 localhost:6379> smembers member 1) "c" 2) "b" 3) "d" 4) "f" 5) "e" 6) "a" // 返回集合中的一个随机元素 localhost:6379> srandmember member "d" localhost:6379> srandmember member "f"
4、移除集合 key 中的一个或多个 member 元素
SREM key member [member …]
移除集合 key 中的一个或多个 member 元素,不存在的 member 元素会被忽略。
localhost:6379[15]> sadd user user1 user2 user3 user4 user5 (integer) 5 // 移除集合中的单个元素 localhost:6379[15]> srem user user1 (integer) 1 // 移除集合中的多个元素 localhost:6379[15]> srem user user2 user3 (integer) 2 // 获取集合中的所有元素 localhost:6379[15]> smembers user 1) "user4" 2) "user5"1. 微博瘫痪
当出现xxx明星和xxx事件的八卦时,可能会造成服务器压力过大,导致服务器宕机。宕机的大多数原因是很多人同时去关注和查看一条热点新闻或者八卦,而对于热点的过多点赞或者评论会导致并发问题。如果将这些点赞和评论利用缓存来解决,然后不定时地把缓存数据刷到数据库中,那么将能提高性能。
2. 抽奖逻辑对于一个用户可以抽多次奖品的抽奖活动,则可以执行如下命令:
// 向集合中存入所有抽奖人员的信息 localhost:6379> sadd luckuser user1 (integer) 1 localhost:6379> sadd luckuser user2 (integer) 1 localhost:6379> sadd luckuser user3 user4 user5 (integer) 3 // 获取集合中所有人员 localhost:6379> smembers luckuser 1) "user4" 2) "user2" 3) "user1" 4) "user3" 5) "user5" // 随机抽取一个人,抽完不删除人员信息 localhost:6379> srandmember luckuser "user1" localhost:6379> srandmember luckuser "user4" // 验证抽完后人员信息是否删除 localhost:6379> smembers luckuser 1) "user3" 2) "user4" 3) "user1" 4) "user2" 5) "user5"
对于一个用户只可以抽一次奖品的抽奖活动,则可以执行如下命令:
localhost:6379[15]> sadd luckuser user1 user2 user3 user4 user5 (integer) 5 // 随机抽取一个人,抽完删除人员信息,不允许重复抽奖 localhost:6379[15]> spop luckuser "user4" localhost:6379[15]> spop luckuser "user3" // 验证抽完后人员信息是否删除 localhost:6379[15]> smembers luckuser 1) "user1" 2) "user2" 3) "user5"3. 文章点赞或者投票
在朋友圈经常收到亲戚朋友发来的链接,请求帮忙点赞和投票。就投票而言,如果根据用户ID或者IP地址来进行限制,那么一个IP地址或者一个用户ID只能针对一个信息投票一次。集合数据类型能够体现出它的特色,即自动去重。对于点赞应用也是一样的。
点赞或者投票操作可执行如下命令:
// 用户点赞 localhost:6379[15]> sadd like:id1 ip1 (integer) 1 localhost:6379[15]> sadd like:id1 ip2 (integer) 1 localhost:6379[15]> sadd like:id1 ip3 (integer) 1 // 重复点赞会失败并返回0 localhost:6379[15]> sadd like:id1 ip3 (integer) 0 // 取消点赞 localhost:6379[15]> srem like:id1 ip1 (integer) 1 // 查看所有的点赞数量 localhost:6379[15]> smembers like:id1 1) "ip2" 2) "ip3"4. 共同好友统计
localhost:6379[15]> flushdb OK // 给zhangsan添加好友friend1、friend2、friend3、friend4 localhost:6379[15]> sadd zhangsan friend1 (integer) 1 localhost:6379[15]> sadd zhangsan friend2 (integer) 1 localhost:6379[15]> sadd zhangsan friend3 (integer) 1 localhost:6379[15]> sadd zhangsan friend4 (integer) 1 // 给lisi添加好友friend1、friend2、friend5、friend6 localhost:6379[15]> sadd lisi friend1 (integer) 1 localhost:6379[15]> sadd lisi friend2 (integer) 1 localhost:6379[15]> sadd lisi friend5 (integer) 1 localhost:6379[15]> sadd lisi friend6 (integer) 1 // zhangsan和lisi的共同好友 localhost:6379[15]> sinter zhangsan lisi 1) "friend2" 2) "friend1" // zhangsan可能认实lisi的好友 localhost:6379[15]> sdiff lisi zhangsan 1) "friend6" 2) "friend5" // lisi可能认识zhangsan的好友 localhost:6379[15]> sdiff zhangsan lisi 1) "friend3" 2) "friend4" // zhangsan和lisi的所有好友 localhost:6379[15]> sunion zhangsan lisi 1) "friend4" 2) "friend2" 3) "friend1" 4) "friend3" 5) "friend6" 6) "friend5"6. Zset类型的使用场景
有序集(Zset)类型是Redis中一个非常重要的数据类型,类似于集合类型和哈希类型之间的混合类型。像Set集合一样,Zset有序集由唯一、非重复的一组元素所组成,从某种意义上说也是一个集合。
虽然Zset内的元素没有排序,但是排序后集合中的所有元素都与一个称为得分的浮点值相关联。
1. Zset基本操作1、将元素及其 score 值加入到有序集 key 当中
ZADD key score member [[score member] [score member] …]
将一个或多个 member 元素及其 score 值加入到有序集 key 当中。如果某个 member 已经是有序集的成员,那么更新这个 member 的 score 值,并通过重新插入这个 member 元素,来保证该 member 在正确的位置上。
2、返回有序集 key 中,指定区间内的成员
ZRANGE key start stop [WITHSCORES]
返回有序集 key 中,指定区间内的成员。其中成员的位置按 score 值递增(从小到大)来排序。具有相同 score 值的成员按字典序来排列。如果你需要成员按 score 值递减(从大到小)来排列,请使用ZREVRANGE 命令。
下标参数 start 和 stop 都以 0 为底,也就是说,以 0 表示有序集第一个成员,以 1 表示有序集第二个成员,以此类推。也可以使用负数下标,以 -1 表示最后一个成员, -2 表示倒数第二个成员,以此类推。
// 添加单个元素 127.0.0.1:6379> zadd page_range 10 goole.com (integer) 1 // 添加多个元素 127.0.0.1:6379> zadd page_range 9 baidu.com 8 csdn.com (integer) 2 // 显示整个有序集成员 127.0.0.1:6379> zrange page_range 0 -1 withscores 1) "csdn.com" 2) "8" 3) "baidu.com" 4) "9" 5) "goole.com" 6) "10" // 显示有序集下标区间 1 至 2 的成员 127.0.0.1:6379> zrange page_range 1 2 withscores 1) "baidu.com" 2) "9" 3) "goole.com" 4) "10"2. 利用Zset实现限流
微服务日益流行,缓存、降级和限流是保护微服务系统运行稳定性的三大利器。缓存的目的是提升系统访问速度和增大系统处理的容量,而降级是当服务出现问题或者影响到核心流程的性能时需要暂时被屏蔽掉,待高峰或者问题解决后再打开。有些场景并不能用缓存和降级来解决,比如稀缺资源、数据库的写操作、频繁的复杂查询,因此需有一种手段来限制这些场景的请求量,这就是限流。Redis的Zset类型的结构可以实现限流功能。
3. 新闻排行榜百度热榜可能是我们经常看到的,针对这样数据量大且实时性高的排名,用传统的数据库来实现性能太差。下面我们使用Redis中的Zset数据类型来模拟一下。
127.0.0.1:6379> zadd news 10 newId1 (integer) 1 127.0.0.1:6379> zadd news 20 newId2 (integer) 1 127.0.0.1:6379> zadd news 30 newId3 (integer) 1 127.0.0.1:6379> zrange news 0 10 withscores 1) "newId1" 2) "10" 3) "newId2" 4) "20" 5) "newId3" 6) "30"4. 直播打赏排名
// 以粉丝打赏的金额作为元素分值存入集合中 127.0.0.1:6379> zadd anchor 10 fans1 (integer) 1 127.0.0.1:6379> zadd anchor 20 fans2 (integer) 1 127.0.0.1:6379> zadd anchor 30 fans3 (integer) 1 // 按照粉丝的打赏金额排名顺序输出粉丝信息 127.0.0.1:6379> zrange anchor 0 -1 1) "fans1" 2) "fans2" 3) "fans3" // 按照粉丝的打赏金额排名倒序输出粉丝信息 127.0.0.1:6379> zrevrange anchor 0 -1 1) "fans3" 2) "fans2" 3) "fans1" // 粉丝信息和金额全部输出 127.0.0.1:6379> zrange anchor 0 -1 withscores 1) "fans1" 2) "10" 3) "fans2" 4) "20" 5) "fans3" 6) "30"