内容简介:redis是基于内存的数据库,单次查询的速度很快。但要查询的数据分布在不同的key里,且查询字段较多时,速度依然会被拖慢。简单来说,性能分为当数据量比较小,更新不频繁,而且查询逻辑复杂时,可以把数据读到内存中,定时从redis更新。这种方法把网络耗时降到0,查询耗时又不会比redis差。如果数据量比较大,读进内存这一步就很耗时,只能在redis中查询。可以使用pipiline或者lua脚本来减少网络耗时,尽量在一次网络交互就拿到所有查询结果。
redis是基于内存的数据库,单次查询的速度很快。但要查询的数据分布在不同的key里,且查询字段较多时,速度依然会被拖慢。简单来说,性能分为 网络耗时
和 查询耗时
。
当数据量比较小,更新不频繁,而且查询逻辑复杂时,可以把数据读到内存中,定时从 redis 更新。这种方法把网络耗时降到0,查询耗时又不会比redis差。
如果数据量比较大,读进内存这一步就很耗时,只能在redis中查询。可以使用pipiline或者 lua 脚本来减少网络耗时,尽量在一次网络交互就拿到所有查询结果。
内存缓存
将redis中的数据,读到内存中。使用时,直接对自带的内存做计算,并且可以转换成更方便的数据结构进行查询。适用的场景是 数据量小,更新不频繁 。
关键的问题,在于更新内存中的缓存。什么时候更新,如何更新?
带有效期的字典: expiredict
expiringdict 实现了一个带有效期(和长度限制)的字典,可以设置key在多久之后过期。简单的测试:
import time from expiringdict import ExpiringDict cache = ExpiringDict(max_age_seconds=3, max_len=10) cache['test'] = 123 time.sleep(3) 'test' in cache # False
接下来一个简单的函数,就能保证(1)总能拿到有效的数据 (2)过期后自动获取数据,并返回更新后的数据。
def get_cache_data(key): global cache if key in cache: cacahe[key] = 'run function to get data from redis' return cacahe[key]
值得注意的是, run function to get data from redis
这一步也可以做适当的优化。
如果在redis存贮的数据,是hash或者set等复杂的数据结构,应该把数据序列化成字符,存贮成字符串对象,读取后再反序列化。因为redis对hash结构 HGETALL
的操作,比 GET
操作要耗时的多( GET
操作是最快的)。做个简单的测试就可以体会到了:
import time import json cli = get_redis_cli() def read_hash(): t1 = time.time() res = cli.hgetall('hash_data') print('read_hash time cost: {0:.2f}ms'.format(1000 * (time.time() - t1))) return res def read_kv(): t1 = time.time() res = json.loads(cli.hget('kv_data')) print('read_kv time cost: {0:.2f}ms'.format(1000 * (time.time() - t1))) return res if __name__ == '__main__': data = {i: i + 1 for i in rang(10000)} cli.hmset('hash_data', data) cli.set('kv_data', json.dumps(data)) read_kv(); read_hash()
当 data
比较小时可能差异不明显,测试中长度达到1w,区别就很显著了。
基于共享内存的更新方法
不论怎样,更新内存时 run function to get data from redis
这一步总会有多余的耗时。有没有可能把这部分也优化掉?
随手查到 mmap ,这家伙把内存数据映射到一个文件,其他进程可以对文件进行读写操作,进而共享控制同一块内存。
可行的操作是,线下定时更新文件对应的内存数据,线上的N个进程从文件读取更新的数据。这就把内存更新的耗时完全移到线下了,而且多进程共享还能节省内存。唯一的缺点就是,需要再线下维护一个定时任务。
降低网络开销
数据量比较大或者更新频繁时,只能从redis做查询,得到结果。能够优化的,仅仅是减少服务器与redis的通信次数,降低频繁的数据传输的耗时。
两种方案,一是redis内置的pipeline,一次发送多个查询,执行完毕后返回多个查询的结果;二是使用lua脚本,将脚本发送到redis服务器执行。相比pipeline,lua脚本的灵活性更高一些。如果多个查询之间有逻辑依赖(比如如果’test’在某个set里,就直接返回结果,否则再继续查询’test’是否在另一个set里),就是最适合使用lua 的场景了。
pipeline
pipeline使用起来很简单,编写指令,最后execute会把所有指令的结果一起返回。适合 多个查询相互独立 的场景。
cli = get_strict_redis() pipe = cli.pipeline() pipe.sismember('set1', '我在这里吗') pipe.sismember('set2', '你在哪里啊') res = pipe.execute() # [False, False] # 你们都不在这里
lua脚本
lua 是门编程语言,灵活性自然是有保障的。redis提供了对lua环境的支持,可以把lua代码发给redis执行。整体也不算太难,看看语法比葫芦画瓢就行了。
实现一个稍微复杂一点的例子,如果 '我在这里吗' in 'set1'
为True,就继续看 '你在哪里啊' in 'set2'
是否为True;否则第二个查询就不需要做了,直接返回1。
script = '''local ret = {} local exist = redis.call('sismember', 'set1', KEYS[1]) table.insert(ret, exist) if exist1 == 1 then table.insert(ret, redis.call('sismember', 'set2', KEYS[2])) else table.insert(ret, 1) end return ret ''' res = rcli.eval(script, 2, '我在这里吗', '你在哪里啊') # [0, 1]
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Glide - 内存缓存与磁盘缓存
- 缓存过期策略 + Redis 内存淘汰机制
- 设计实现高性能本地内存缓存
- 理解高性能内存缓存对象缓存Memcached原理 (Memcached核心概念,部署)
- Android 内存缓存框架 LruCache 的源码分析
- 内存缓存之HashMap、EHCache、Guava Cache对比
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
疯狂又脆弱 坚定又柔软
朱墨 / 湖南文艺出版社 / 2018-3 / 39.80元
《疯狂又脆弱 坚定又柔软》是朱墨的一部作品集,介绍了作者考研到北京,工作在华谊,以及留学去英国的经历,在这短短几年中她一路升职加薪,25岁升任华谊宣传总监,27岁赚到人生的第一笔100万,30岁却毅然离职去英国留学,在表面的光鲜亮丽之下,她也曾付出过外人所不知道的心血和努力。她的人生告诉我们,每一个身居高位或者肆意潇洒的人,都曾为梦想疯狂地倾尽全力,而那些心怀梦想的人也总是怀揣一颗坚定又柔软的内心......一起来看看 《疯狂又脆弱 坚定又柔软》 这本书的介绍吧!