Redis Essentials 读书笔记 - 第六章: Common Pitfalls (Avoiding Traps)
2016-05-05 20:53
471 查看
Chapter 6. Common Pitfalls (Avoiding Traps)
本章讲述使用Redis的一些误区,部分例子基于Yipit (www.yipit.com)和其它一些公司的经验教训。The wrong data type for the job
Yipit最初将要发送给用户的deal存于Set中,尽管可以运行,但开发者担心由于用户量太大,Set的内存消耗大,因此转向用Bitmap存储,结果是上线后不久,内存就满了,下来会解释原因。教训:在提交解决方案前必须做benchmark测试。
The Set approach
Set的实现非常直接,每一个用户一个Set,即userID作为key, DealID用Set存放,例如(1, 2, 3, 4, 5, …).The following code is a benchmark of this implementation, using 100,000 Sets with 12 deal IDs each, and each user will receive the same 12 deals.
Create a file called benchmark-set.js in the chapter 6 folder with the following code:
以下为使用Set的benchmark代码,模拟10万用户,每用户12个deal。
var redis = require("redis"); var client = redis.createClient(); var MAX_USERS = 100000; var MAX_DEALS = 12; var MAX_DEAL_ID = 10000; for (var i = 0 ; i < MAX_USERS ; i++) { var multi = client.multi(); for (var j = 0 ; j < MAX_DEALS ; j++) { multi.sadd("set:user:" + i, MAX_DEAL_ID - j, 1); } multi.exec(); } client.quit();
运行benchmark程序,得到内存使用情况:
[redis@tt12c chapter 6]$ redis-cli FLUSHALL && node benchmark-set.js && redis-cli INFO memory OK # Memory used_memory:14355424 used_memory_human:13.69M used_memory_rss:18239488 used_memory_peak:32488744 used_memory_peak_human:30.98M used_memory_lua:36864 mem_fragmentation_ratio:1.27 mem_allocator:jemalloc-3.6.0
The Bitmap approach
Bitmap的实现中,也是userID作为key,然后dealID存放于Bitmap中,需要发送的deal置为1。不过Bitmap的问题在于最大的dealID决定了其存储开销,如果最大的dealID是1,000,000,Bitmap就需要消耗1,000,001 bit。
以下为使用Bitmap的benchmark代码,模拟10万用户,每用户12个deal。
var redis = require("redis"); var client = redis.createClient(); var MAX_USERS = 100000; var MAX_DEALS = 12; var MAX_DEAL_ID = 10000; for (var i = 0 ; i < MAX_USERS ; i++) { var multi = client.multi(); for (var j = 0 ; j < MAX_DEALS ; j++) { multi.setbit("bitmap:user:" + i, MAX_DEAL_ID - j, 1); } multi.exec(); } client.quit();
运行benchmark程序,得到内存使用情况:
[redis@tt12c chapter 6]$ redis-cli FLUSHALL && node benchmark-bitmap.js && redis-cli INFO memory OK # Memory used_memory:265555560 used_memory_human:253.25M used_memory_rss:291766272 used_memory_peak:283525488 used_memory_peak_human:270.39M used_memory_lua:36864 mem_fragmentation_ratio:1.10 mem_allocator:jemalloc-3.6.0
Bitmap实现使用了 253MB内存,而Set实现仅使用了近14M。
为何有如此大的区别,理论上Bitmap应更省内存。仔细看这段代码,比Set的实现多了
MAX_DEAL_ID参数,使得Bitmap被”撑大了”,一个Bitmap需要100000个bit,约12500字节
multi.setbit("bitmap:user:" + i, MAX_DEAL_ID - j, 1);
Multiple Redis databases
Redis服务器支持多个数据库,类似于SQL数据库,如MySQL,只不过Redis用数字表示不同的数据库。但Redis不推荐使用多个数据库,而推荐在一个机器上启动多个Redis服务,因为Redis是单线程的,这样可以更好的利用CPU core。
而且多个数据库不好管理,无法判断是谁引起问题。
Keys without a namespace
使用namespace是最佳建议,目的是为了避免key name的冲突,并且可以很好的对key进行组织。在 SQL数据库中, namespace可以用数据库名或表名表示。而Redis并不支持namespace,因此必须模拟namespace,常用的方法是加前缀, 形式namespace:key_name。这种方法在前面例子中已经见到很多了,例如:
music-online:song:1 music-online:song:2 music-online:album:10001:metadata music-online:album:10001:songs music-online:author:123
Using Swap
不要用Swap,尽可能让Redis运行在内存中。因频繁的使用swap会阻塞客户端的访问。Linux有一个kernel参数swappiness控制如何使用swap,数越大,使用swap越频繁。将它设置为0。
sysctl -w vm.swappiness=0 同时 修改/etc/sysctl.conf
Not planning and configuring the memory properly
Redis服务器需要足够的内存来执行备份,在备份是,最极端的情况需要Redis内存的两倍。在创建RDB snapshot 和 AOF rewriting时, redis-server需要通过fork()复制自身。
如果在fork时,Redis非常忙,这时copy-on-write以及内存overcommitting已经不够,子进程可能需要和父进程同样大小的内存。
如果是Linux操作系统,在/etc/sysctl.conf中设置vm.overcommit_memory=1可加速后台的数据保存。
Redis有一配置参数maxmemory可限制Redis最多可使用的内存。
在备份开启后,Redis使用的内存不要超过可用内存的一半。
An inappropriate persistence strategy
在Yipit,有一个读密集的Redis实例变得缓慢,开始以为是代码有问题,后来发现是定期的备份(持久化)使其变慢。在Redis开始创建RDB snapshot和AOF rewriting时,需要用fork创建子进程,然后让新的子进程来处理。在fork执行时,Redis不能对外服务,这时用户会感觉到响应慢。
Yipit的问题就是因为fork时间过长,因为Redis运行在AWS上,机器使用的是较慢的PV而非HVM虚拟机。
通过以下的措施可以缓解此问题:
* 禁止transparent huge pages内核参数 (echo never > /sys/kernel/mm/transparent_hugepage/enabled)
* 使用HVN虚拟机
* 进行复制,复制目标端用且仅用于持久化
* 减少备份的频度
* 禁止自动持久化,手工来做
* 如果数据可以很快的重建,就不要做持久化
Redislab 发布了fork时间的benchmark:https://redislabs.com/blog/testing-fork-time-on-awsxen-infrastructure.
Summary
本章讲述了Redis使用的一些误区,比较重要的有:* 要使用namespace避免key命名冲突
* 才提交解决方案时需要先测试,做benchmark测试
* 不做或少做持久化
相关文章推荐
- 5.Python操作Redis:集合(Set)
- redis 多租户概念的个人理解
- Java中使用Jedis操作Redis
- Redis实际应用场景
- jedispool 连 redis
- redis sentinel集群配置
- redis 下载 安装 主从简单配置
- C# ServiceStack.Redis 操作对象List
- 利用redis完成自动补全搜索功能(二)
- redis线程模型
- mybatis和redis整合 log4j打印sql语句
- Redis3.0.5封装
- Redis Essentials 读书笔记 - 第四章: Commands (Where the Wild Things Are)
- Windows系统安装测试redis
- 美团在Redis上踩过的一些坑-5.redis cluster遇到的一些问题
- C++封装的Redis
- PHPredis长连接pconnect
- Redis主从复制和sentinel配置
- php操作redis出现不报错就退出
- Redis发布预订使用总结