Redis常见的使用场景

什么是Redis?

Redis是一个非常快速的开源非关系、Key-Value数据库,通常称为数据结构服务器;它存储了五种不同类型值的键映射。用作数据库,缓存和消息代理。

Redis和其他键值数据库之间的主要区别之一是Redis存储和操作高级数据类型的能力。这些数据类型是大多数开发人员熟悉的基本数据结构(列表,映射,集合和排序集)。Redis的卓越性能,简单性和数据结构的原子操作有助于解决使用传统关系数据库实现时难以实现或执行不佳的问题。

Redis的特性

一个产品的使用场景肯定是需要根据产品的特性,先列举一下Redis的特点:

  • 读写性能优异
  • 持久化
  • 数据类型丰富
  • 单线程
  • 数据自动过期
  • 发布订阅
  • 分布式

这里我们通过几个场景,不同维度说下Redis的应用。

常见的应用场景

1、缓存

缓存是Redis最常见的应用场景,之所有这么使用,主要是因为Redis读写性能优异。而且逐渐有取代memcached,成为首选服务端缓存的组件。而且,Redis内部是支持事务的,在使用时候能有效保证数据的一致性。

不过也有需要注意的地方:

  • 必须保证不同对象的key不可以重复,并且key尽量短,一般使用类名加主键拼接而成。
  • 选择一个有序的序列化方式也很重要,目的是提高序列化效率和减少内存占用。
  • 缓存期间的数据一致性。一般有两种做法:
    1. 只在数据库查询后将对象放入缓存,如果对象发生了删除或者修改操作,直接清除对应缓存(或者设置为过期)
    2. 在数据库新增和查询后将对象放入缓存,修改后更新缓存,删除后清除对应缓存,或者设置为过期。

2、限时业务的运用

redis中可以使用expire命令设置一个键的生存时间,到时间后redis会删除它。利用这一特性可以运用在限时的优惠活动信息、手机验证码等业务场景。

3、计数器

计数功能应该是最适合redis的使用场景之一,高频率读写特性完全可以发挥redis作为内存数据库的高效。在Redis的数据结构中,string, hash, 和sorted set都提供了incr方法用于原子性自增操作,下面举例说明它们各自的应用场景:

  • 如果应用需要显示每天注册用户数,便可以使用String作为计数器,设定一个名为REGISTERED_COUNT_TODAY的 key,并在初始化时给它设置一个到凌晨 0 点的过期时间,每当用户注册成功后便使用incr命令使该 key 增长 1,同时当每天凌晨 0 点后,这个计数器都会因为 key 过期使值清零。
  • 每条微博都有点赞数,评论数,转发数和浏览数四条属性,这是用hash进行计数会更好,该计数器的key设为为weibo:weibo_idhash的 field 为like_numbercomment_numberforward_numberview_number,在对应操作后通过hincrby使hash 中的 field 自增。
  • 如果应用有一个发帖排行榜的功能,便选择sorted set吧,将集合的 key 设为POST_RANK。当用户发帖后,使用zincrby将该用户 id 的 score 增长 1。sorted set会重新进行排序,用户所在排行榜的位置也就会得到实时的更新。

4、排行榜

使用sortedset可以轻松打造一个热度排行榜,zrevrangebyscore可以得到以分数倒序排列的序列,zrank可以得到成员在该排行榜中的作用。

5、分布式锁

在Redis2.6.12版本开始,string的set命令增加了三个参数:

  • Ex:设置键的过期时间(s)

  • Px: 设置键的过期时间(ms)

  • NX|XX:当设置为NX时,仅当key存在才进行操作,设置为xx时,仅当key不存在才会进行操作,这个操作是原子性的,可以简单实现一个分布式锁,例如:

    set key "lock" Ex 1 xx
    

    如果操作返回false,说明key的添加不成功,即当前有人占用这把锁,而如果返回true,说明得到了锁,可以继续进行操作,操作后通过del释放掉锁,并且即使程序因为某些原因没有释放锁,设置了过期时间,所以该锁也会在1秒后自动释放。

  • 伪代码:

    //产生锁
    while lock!=1
        //过期时间是为了避免死锁
        now = int(time.time())
        lock_timeout = now + LOCK_TIMEOUT + 1
        lock = redis_client.setnx(lock_key, lock_timeout)
    
    //真正要处理的业务
    doing() 
    
    //释放锁
    now = int(time.time())
    if now < lock_timeout:
        redis_client.delete(lock_key)
    

6、延时操作

比如在订单生产后我们占用了库存,10分钟后去检验用户是够真正购买,如果没有购买将该单据设置无效,同时还原库存。 由于redis自2.8.0之后版本提供Keyspace Notifications功能,允许客户订阅Pub/Sub频道,以便以某种方式接收影响Redis数据集的事件。 所以我们对于上面的需求就可以用以下解决方案,我们在订单生产时,设置一个key,同时设置10分钟后过期, 我们在后台实现一个监听器,监听key的实效,监听到key失效时将后续逻辑加上。 当然我们也可以利用rabbitmq、activemq等消息中间件的延迟队列服务实现该需求。

7、分页、模糊搜索

redis的set集合中提供了一个zrangebylex方法,语法如下:

ZRANGEBYLEX key min max [LIMIT offset count]

通过ZRANGEBYLEX zset - + LIMIT 0 10 可以进行分页数据查询,其中- +表示获取全部数据

zrangebylex key min max 这个就可以返回字典区间的数据,利用这个特性可以进行模糊查询功能,这个也是目前我在redis中发现的唯一一个支持对存储内容进行模糊查询的特性。

8、点赞、好友等相互关系

一篇介绍微博Redis应用的PPT中,其中提到微博的Redis主要用在计数和好友关系两方面,

《Redis设计与实现》中作者最开始使用Redis中的set是因为传统数据库无法计算集合的交集。

对于一个用户A,将它的关注和粉丝的用户id都存放到两个set中:

  • A:follow:存放A所有关注的用户id

  • A:follower:存放A所有粉丝的用户id

    那么通过sinter命令便可以根据A:followA:follower的交集得到与 A 互相关注的用户。当 A 进入另一个用户 B 的主页后,A:followB:follow的交集便是 A 和 B 的共同专注,A:followB:follower的交集便是 A 关注的人也关注了 B。

9、队列

Redis中的list的数据结构实现的是双向链表,所以可以非常便捷的应用于消息队列(生产者/消费者模型)。消息的生产者只需要通过lpush将消息放入list,消费者可以通过rpop取出该消息,并且保证消息的有序性。

如果需要实现带有优先级的消息队列也可以选择**sorted list。而pub/sub**也可以作为发布者/订阅者模型的消息。由于Redis带有持久化功能,无需担心由于服务器故障导致消息丢失的情况发生。

10、时间轴

list作为双向链表,不光可以作为队列使用,如果将它用作栈便可以成为一个公用的时间轴。当用户发完微博后,都通过lpush将它存放在一个key为LATEST_WEIBO的list中。之后便可以通过lrange取出最新的微博。

11、倒排索引

倒排索引是构造搜索功能的最常见的方式,Redis中也可以通过set建立倒排索引,这里以简单的拼音+前缀搜索城市功能举例:

假设一个城市北京,通过拼音词库将北京转为beijing,再通过前缀分词将这两个词分为若干个前缀索引,有:北京bbebeijinbeijing。将这些索引分别作为set的 key(例如:index:北)并存储北京的 id,倒排索引便建立好了。接下来只需要在搜索时通过关键词取出对应的set并得到其中的 id 即可。

示例:秒杀和Redis的结合

秒杀是现在互联网系统中常见的营销模式,作为开发者,其实最不愿意这样的活动,因为非技术人员无法理解到其中的技术难度,导致在资源协调上总是有些偏差。秒杀其实经常会出现的问题包括:

  • 并发太高导致程序阻塞。
  • 库存无法有效控制,出现超卖的情况。

其实解决这些问题基本就两个方案:

  • 数据尽量缓存,阻断用户和数据库的直接交互。
  • 通过锁来控制避免超卖现象。

现在说明一下,如果现在做一个秒杀,那么,Redis应该如何结合进行使用?

  • 提前预热数据,放入Redis
  • 商品列表放入Redis List
  • 商品的详情数据 Redis hash保存,设置过期时间
  • 商品的库存数据Redis sorted set保存
  • 用户的地址信息Redis set保存
  • 订单产生扣库存通过Redis制造分布式锁,库存同步扣除
  • 订单产生后发货的数据,产生Redis list,通过消息队列处理
  • 秒杀结束后,再把Redis数据和数据库进行同步

以上是一个简略的秒杀系统和Redis结合的方案,当然实际可能还会引入http缓存,或者将消息对接用MQ代替等方案,也会出现业务遗漏的情况,这个只是希望能抛砖引玉。

发布了109 篇原创文章 · 获赞 101 · 访问量 36万+

猜你喜欢

转载自blog.csdn.net/Alen_xiaoxin/article/details/105147833
今日推荐