Redis应用场景

本文主要介绍Redis的常用场景,这都是由Redis的特性决定的。并尽量从源码角度说明Redis为什么适合于这种场景。

I、积分排行榜

1、积分排行榜是Redis的经典应用,这是得益于Redis提供了zset的有序集合数据结构。

2、针对zset,Redis提供了一系列的命令:

ZADD: 添加新元素;
ZRANGE: 按分数从低到高返回给定排名区间的元素;
ZREVRANGE: 按分数从高到底返回给定区间的元素;
ZRANK: 返回某个元素的排名。

3、实现:下面的例子为用python完成的一个leaderboard

# -*- coding: utf-8 -*-
#!/usr/bin/python
import redis
class Leaderboard:
    def __init__(self,host,port,key,db):
        self.host = host
        self.port = port
        self.key = key
        self.db = db
        self.r = redis.StrictRedis(host=self.host,port=self.port,db=self.db)
    def isRedisValid(self):
        return self.r is None
    def addMember(self,member,score):
        if self.isRedisValid():
            return None
        return self.r.zadd(self.key,score,member)
    def delMember(self,member):
        if self.isRedisValid():
            return None
        return self.r.zrem(self.key,member)
    def incrScore(self,member,increment):
        """increase score on specified member"""
        if self.isRedisValid():
            return None
        return self.r.zincrby(self.key,member,increment)
    def getRankByMember(self,member):
        """Get ranking by specified member."""
        if self.isRedisValid():
            return None
        return self.r.zrank(self.key,member)
    def getLeaderboard(self,start,stop,reverse,with_score):
        """Return the whole leaderboard."""
        if self.isRedisValid():
            return None
        return self.r.zrange(self.key,start,stop,reverse,with_score)
    def getLeaderboardByPage(self,item_per_page,page_num,reverse=False,with_score=False):
        """Return part of leaderboard configurably."""
        # fix parameters
        if item_per_page <= 0:
            item_per_page = 5
        if page_num <= 0:
            page_num = 1
        # note: it is possible that return value is empty list.
        return self.getLeaderboard((page_num-1)*item_per_page,
                                    page_num*item_per_page-1,
                                            reverse,with_score)
    def getWholeLeaderboard(self,reverse=False,with_score=False):
        """Return the whole leaderboard."""
        return self.getLeaderboard(0,-1,reverse,with_score)

II、分布式锁

1、我们通常所提到的锁,一般都是在本地环境下对多线程完成互斥操作,而如果共享资源存在于网络上,本地的锁就不起作用了。
互斥访问某个网络资源的时候,需要有一个在网络上的锁服务器,负责锁的申请与回收,Redis就可以作为这种分布式锁的服务器。

2、因为Redis是单进程单线程的工作模式,所以前来申请锁资源的请求都被排队处理,能保证锁资源的同步访问。

3、实现:可以在Redis服务器设置一个键值对,用以表示一把分布式互斥锁,当申请锁的时候,申请方SET这个键值对,当释放锁的时候,释放方DEL这个键值对:

lock = redis.get("mutex_lock");
if(!lock)
    error("apply the lock error.");
else
    -- 确定可以申请锁
    redis.set("mutex_lock","locking");
    do_something();

4、上述的申请方法,涉及到客户端与Redis服务器的多次交互,当客户端确定可以加锁的时候,可能这时候锁已经被其他客户端申请了,最终导致两个客户端同时持有锁。解决这个问题的方法就是将申请/释放锁的逻辑都放在服务器上。Redis Lua脚本可以完成这个问题:

-- apply for lock
local key = KEYS[1]
local res = redis.call('get', key)

-- 锁被占用,申请失败
if res == '0' then
return -1

-- 锁可以被申请
else
local setres = redis.call('set', key, 0)
if setres['ok'] == 'OK' then
return 0
end
end
return -1

get 命令不成功返回(nil).
实验命令:保存lua 脚本redis-cli script load ”$(cat mutex_lock.lua)”
-- releae lock
local key = KEYS[1]
local setres = redis.call('set', key, 1)
if setres['ok'] == 'OK' then
return 0
return -1

5、死锁问题

分布式锁由于客户端的崩溃很容易造成锁无法释放,从而出现死锁。所以需要使用Redis的TTL功能,设置超时释放:

-- apply for lock
local key = KEYS[1]
local timeout = KEYS[2]

local res = redis.call('get', key)

-- 锁被占用,申请失败
if res == '0' then
return -1
-- 锁可以被申请
else
local setres = redis.call('set', key, 0)
local exp_res = redis.call('pexpire', key, timeout)
if exp_res == 1 then
return 0
end
end
return -1

6、此外如出现服务器宕机,也会出现死锁问题,这就需要slave服务器。

III、消息中间件

1、消息中间件可以理解为分布式的消息队列
消息中间件负责接收生产者的消息,并存储转发给对应的消费者,生产者按照topic发布消息,消费者按topic订阅各种消息。生产者只需要向中间件中推送消息,不用等待消费者回应。

2、也就是说,这种分布式的消息中间件就是网络上的一个服务器,起到一个数据中转的功能。

IV、Web服务器存储session

1、以Redis作为session的存储,可以加速后台的处理速度,如购物车数据,就不必直接存储到磁盘数据库中,在这里Redis的作用就是一个缓存。

【参考】
[1] 《Redis源码日志》

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容

  • Redis在很多方面与其他数据库解决方案不同:它使用内存提供主存储支持,而仅使用硬盘做持久性的存储;它的数据模型非...
    全能程序猿阅读 24,191评论 0 8
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,637评论 18 139
  • string(字符串) 常用命令: set,get,decr,incr,mget 等。 使用SETBIT、GETB...
    陈小陌丿阅读 962评论 0 3
  • 1. 取最新N个数据的操作 比如典型的取你网站的最新文章,通过下面方式,我们可以将最新的5000条评论的ID放在R...
    六月星空2011阅读 500评论 0 1
  • 在英语的时态中有一个进行时还有一个完成时,其实想来应该还有一个正在适应时,当你进入一个全新的领域,必然需要一个适应...
    怡儿话书影阅读 293评论 0 0