基于redis的二级缓存


#软件架构与思考#


2014-11-28

编译redis

当前redis的稳定版本是2.8.17,可以在官网下载。下载后解压,进入解压后的目录,直接make,之后可以在src目录下找到编译好的二进制文件redis-serverredis-cli等。

配置

理论上来说,第1级缓存的访问速度要比第2级缓存快,第1级缓存的容量要比第2级缓存小,第1级缓存在价钱上要比第2级缓存高很多。另外,第1级缓存上的数据的访问频率也应该高于第2级缓存,第1级缓存上的数据的热门程度大于第2级缓存。

对于新闻网站,当天的新闻可以缓存到第1级缓存中,之前的新闻可以缓存到第2级缓存。

第1级缓存配置如下(6301.conf):

port 6301
maxmemory 2mb
maxmemory-policy allkeys-lru
maxmemory-samples 20
pidfile ./6301.pid  
timeout 300
logfile ./6301.log  

第1级缓存占用6301端口,使用2 metabytes内存。在内存使用达到2 metabytes时,若需要存储新的内容,使用LRU算法置换已经存储的内容,以腾出内存空间。进程id信息保存在文件6301.pid中,日志信息保存到6301.log文件中。

关于maxmemory-policymaxmemory-samples,可以参考Using Redis as an LRU cache

第2级缓存配置如下(6302.conf):

port 6302
maxmemory 100mb
maxmemory-policy allkeys-lru
maxmemory-samples 20
pidfile ./6302.pid  
timeout 300
logfile ./6302.log

第2级缓存占用6302端口,使用100 metabytes内存。

python2.7 安装相关的模块:

$ sudo pip install redis

缓存的数据格式

假设每条数据的key和value大小都是相同的。key是长度为7的字符串,value是长度为20000的字符串。

在本机上启动第1级缓存:

$ redis-server ./6301.conf

运行下面的python程序:

# coding: UTF-8
import redis

redis1 = redis.StrictRedis(host='localhost', port=6301, db=0)

for x in xrange(100):  #设的大一些
    key = 'url%4d'% x
    value = [str(x%10)] * 20 * 1000
    redis1.set(key, ' '.join(value))

print redis1.dbsize()  # 键值对数量
print redis1.flushall()  # 清空

输出:

36
True

可见第1级缓存可存储36个这样的键值对。

类似的,第2级缓存可以存储2540个这样的键值对。

在单机上测试

在本机上启动第1级缓存:

$ redis-server ./6301.conf

在本机上启动第2级缓存:

$ redis-server ./6302.conf

第2级缓存单独作为一个缓存系统,名为Cache1;第1级和第2级缓存共同作为一个缓存系统,名为Cache2。

在第2级缓存中添加数据:

# coding: UTF-8

import redis

redis2 = redis.StrictRedis(host='127.0.0.1', port=6302, db=0)

for x in xrange(2540):
    key = 'url%4d'% x
    value = [str(x%10)] * 20 * 1000
    redis2.set(key, ' '.join(value))

测试程序:

# coding: UTF-8

import redis
import time
import random


class Cache1:
    '''
    只有第2级缓存, 100mb
    '''
    def __init__(self):
        self.redis2 = redis.StrictRedis(host='127.0.0.1', port=6302, db=0)

    def get(self, key):
        return self.redis2.get(key)

    def set(self, key, value):
        return self.redis2.set(key, value)


class Cache2:
    '''
    第1级缓存:2mb
    第2级缓存:100mb
    '''
    def __init__(self):
        self.redis1 = redis.StrictRedis(host='127.0.0.1', port=6301, db=0)
        self.redis2 = redis.StrictRedis(host='127.0.0.1', port=6302, db=0)

    def get(self, key):

        value1 = self.redis1.get(key)
        if value1 is not None:
            return value1
        else:
            value2 = self.redis2.get(key)
            if value2 is not None:
                self.redis1.set(key, value2)
            return value2

    def set(self, key, value):

        return self.redis1.set(key, value) or self.redis2.set(key, value)


def test01():
    '''
    测试Cache1
    '''
    c1 = Cache1()
    start_time = time.time()
    for x in xrange(200):
        ids = range(36)
        ids.append(random.randint(100, 1800))
        keys = ['url%4d' % x for x in ids]
        random.shuffle(keys)
        for key in keys:
            if c1.get(key) is None:
                print 'do not have key: %s' % key

    print 'test01耗时:', time.time() - start_time


def test02():
    '''
    测试Cache2,第1级缓存命中率较高
    '''
    c2 = Cache2()
    start_time = time.time()
    for x in xrange(200):
        ids = range(36)
        ids.append(random.randint(100, 1800))
        keys = ['url%4d' % x for x in ids]
        random.shuffle(keys)
        for key in keys:
            if c2.get(key) is None:
                print 'do not have key: %s' % key

    print 'test02耗时:', time.time() - start_time
    c2.redis1.flushall()


def test03():
    '''
    测试Cache2,第1级缓存命中率较低
    '''
    c2 = Cache2()
    start_time = time.time()
    for x in xrange(200):
        ids = range(20)
        ids.extend([random.randint(100, 1800) for _ in xrange(17)])
        keys = ['url%4d' % x for x in ids]
        random.shuffle(keys)
        for key in keys:
            if c2.get(key) is None:
                print 'do not have key: %s' % key

    print 'test03耗时:', time.time() - start_time
    c2.redis1.flushall()


if __name__ == '__main__':
    test01()
    test02()
    test03()

三次运行的结果如下:

test01耗时: 1.01688408852
test02耗时: 2.49854183197
test03耗时: 4.28654408455

test01耗时: 1.41078400612
test02耗时: 2.63620305061
test03耗时: 3.54241800308

test01耗时: 1.5699839592
test02耗时: 2.48805403709
test03耗时: 4.36335802078

本以为test02耗时要低于test01,但事实却不是,可以认为redis本身的算法实现很漂亮。

在双机上测试

通常,网站的缓存系统和网站主程序并不在同一个主机上。将第2级缓存转移到连接同一个交换机的另外一台主机上,假设该主机ip为180.180.180.180,将上述测试程序中的

self.redis2 = redis.StrictRedis(host='127.0.0.1', port=6302, db=0)

修改为

self.redis2 = redis.StrictRedis(host='180.180.180.180', port=6302, db=0)

三次运行的结果如下:

test01耗时: 29.73127985
test02耗时: 8.53076004982
test03耗时: 24.4670870304

test01耗时: 29.7424390316
test02耗时: 8.52891612053
test03耗时: 24.3255221844

test01耗时: 29.9915928841
test02耗时: 8.52997803688
test03耗时: 24.701570034

可以认为,由于网络传输的延迟,第2级缓存的访问速度要低于第1级缓存,两级缓存系统满足需求。


( 本文完 )