Redis多实例-Proxy模式RPM包

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 引子: 公司一个项目需要使用Redis,单实例的Redis性能不够,但出于成本考虑,不打算使用集群模式,因而选择了Redis的Proxy模式,为了方便部署,干脆打成了RPM包。这里分享出来,以飨众人。 RPM包下载地址: 链接: https://pan.baidu.com/s/1dP7Ilo 密码: p456 说明: 构建简单的Redis Cluster 基于 Redis 版本为4.0.1 Redis Proxy 为唯品会开发的多线程版本的twemproxy –twemproxies(版本1.2.1) 项目地址: https://github.com/vipshop/twemproxies 安装&&启动&&停止 1.安装 rpm -ivh redis_tp_cluster-0.0.1-4.x86_64.rpm 安装完成以后, 1.1 配置文件在 /etc/redis中 ## Redis共10个实例 /etc/redis/7001.conf /etc/redis/7002.conf /etc/redis/7003.conf /etc/redis/7004.conf /etc/redis/7005.conf /etc/redis/7006.conf /etc/redis/7007.conf /etc/redis/7008.conf /etc/redis/7009.conf /etc/redis/7010.conf # Redis Proxy 配置文件 /etc/redis/nutcrackers.yml 1.2 bin文件在 /usr/local/bin/redis-server /usr/local/bin/redis-cli /usr/local/bin/nutcrackers 1.3 启动脚本 /etc/init.d/redis_tp_cluster.sh 2.启动 /etc/init.d/redis_tp_cluster.sh start 3.停止 /etc/init.d/redis_tp_cluster.sh stop 重要 Redis Proxy默认 服务地址 127.0.0.1:22121 密码 b8@40fc02d52 如果需要调整请修改 /etc/redis/nutcrackers.yml 参考资料: 1.Redis rpm包下载地址

January 11, 2018 · 1 min

利用redis实现带优先级的消息队列

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 前言 以前一直有使用celery的优先级机制(基于redis的任务队列),一直很好奇它的实现机制,在查阅了部分资料后,决定写这篇文章,作为总结。 1. 利用Sorted Set 实现 使用Sorted Set 做优先级队列最大的优点是直观明了。 ZADD key score member [[score member] [score member] ...] score 作为优先级,member 作为相应的任务 在Sorted Set 中,score 小的,位于优先级队列的头部,即优先级较高 由于score 就是menber的优先级,因此非常直观 可以使用 MULTI ZRANGE key 0 0 WITHSCORES ZREMRANGEBYRANK task_list 0 0 EXEC 来获取任务队列中优先级最高的元素 ZRANGE 用于获取任务,ZREMRANGEBYRANK 用于从消息队列中移除 注意:由于Sorted Set本身是一个set,因此消息队列中的消息不能重复,否则新加入的消息会覆盖以前加入的消息 注意:对于score 相同的消息,Sorted Set 会按照字典序进行排序 2. 利用List实现 应该一下就能想到,list 是作为消息队列的最理想的选择,但这里使用list 实现带优先级的消息队列也可以有好几种不同的实现方式。 2.1 准备 首先,如果我们假定消息队列中的消息,从消息队列的右侧推入(RPUSH),从左侧取出(LPOP) 那么单个list 很容易构造成一个FIFO 队列。但是如果优先级只有两级,高和低,那么我们可以把高优先级的消息,使用LPUSH 推入队列左侧,把低优先级的消息,使用RPUSH推入到队列右侧, 这样单个list就可以实现2级的带优先级的消息队列。 2.2 使用BLPOP redis 提供了列表的阻塞式(blocking)弹出原语。 ...

January 2, 2018 · 2 min

redis 启动警告及处理

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 起因: 生产环境的一台redis机器 Can't save in background: fork: Cannot allocate memory 导致redis服务停止,但是当时机器的内存是64G,redis使用到的内存只有40多G 我们都知道,redis 如果开启了持久化,RDB模式的bgsave 以及 AOF模式下,重写appendonly.aof 都会导致redis fork 出一个子进程。但是难道操作系统的进程fork难道不应该是copy-on-write 的吗? 这件事让我重新关注起redis启动时的日志来。 首先来看看redis启动时所报的日志 1610:M 12 Sep 07:46:20.524 # Server started, Redis version 3.0.1 1610:M 12 Sep 07:46:20.524 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect. 1610:M 12 Sep 07:46:20.524 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled. 1610:M 12 Sep 07:46:20.525 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. 1610:M 12 Sep 07:46:20.525 * The server is now ready to accept connections on port 6379 1610:M 12 Sep 07:57:21.819 * Background saving started by pid 1615 1615:C 12 Sep 07:57:21.827 * DB saved on disk 1615:C 12 Sep 07:57:21.827 * RDB: 4 MB of memory used by copy-on-write 1610:M 12 Sep 07:57:21.925 * Background saving terminated with success 可以看到警告有3个 ...

January 1, 2018 · 3 min

我在数据库方面踩过的"坑"

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 前言:前段时间在公司内部做了一个分享总结了部分我在使用各种数据库方面的遇到的问题。也在这里分享给大家。强调一下,这里的坑,我是打了引号的,有些坑,不过是某种数据库的特点,或者因为我们错误的事情而引出了问题,并不一定完全就是这种数据库有问题。 1. 业务篇 1)业务场景 不合理的业务设计,永远是对程序员最大的伤痛 在我维护的系统中有这样一种场景,用户要一次性下载全年或者半年的舆情数据,数据量会很大,单个任务就会达到数百万条数据。任何一个系统要在短时间内吞吐数据数百万条记录,也不是件很轻松的事情,尤其当这样的任务很多的时候。 目前这个时间跨度已经被调整成了3个月。说到这里不经让我想到12306错开时间发售火车票。 任何时候从业务角度的优化,总能带来立竿见影的效果 2) 字段设计 在我维护的某个系统中,同一种指标,在不同的表中,被存成了不同的字段名,这给我们带来了巨大的痛苦。所以建议对于同一种指标,或者事物使用同样的字段名(名称)进行表达、存储,否则后期光转换都要人命 3)表结构的反范式设计 大数据场景下,不要受到关系数据库范式设计的太多影响 数据机构能够立体的,尽量立体,不要扁平化 以新浪微博的一条转发举例 一条转发会包含有 这条微博的作者 这条微博的内容 text 原创微博retweeted_status 原创微博的内容 retweeted_status.text 原创微博的作者 retweeted_status.user … 一条记录就包含了这条转发,以及与这条转发相关的大部分内容,在实际使用时,无需连表查询可以方便的用NoSQL 数据库进行存储 { "created_at": "Tue May 31 17:46:55 +0800 2011", "id": 11488058246, "text": "求关注。", "source": "<a href="http://weibo.com" rel="nofollow">新浪微博</a>", "favorited": false, "truncated": false, "in_reply_to_status_id": "", "in_reply_to_user_id": "", "in_reply_to_screen_name": "", "geo": null, "mid": "5612814510546515491", "reposts_count": 8, "comments_count": 9, "annotations": [], "user": { "id": 1404376560, "screen_name": "zaku", "name": "zaku", "province": "11", "city": "5", "location": "北京 朝阳区", "description": "人生五十年,乃如梦如幻;有生斯有死,壮士复何憾。", "url": "http://blog.sina.com.cn/zaku", "profile_image_url": "http://tp1.sinaimg.cn/1404376560/50/0/1", "domain": "zaku", "gender": "m", "followers_count": 1204, "friends_count": 447, "statuses_count": 2908, "favourites_count": 0, "created_at": "Fri Aug 28 00:00:00 +0800 2009", "following": false, "allow_all_act_msg": false, "remark": "", "geo_enabled": true, "verified": false, "allow_all_comment": true, "avatar_large": "http://tp1.sinaimg.cn/1404376560/180/0/1", "verified_reason": "", "follow_me": false, "online_status": 0, "bi_followers_count": 215 }, "retweeted_status": { "created_at": "Tue May 24 18:04:53 +0800 2011", "id": 11142488790, "text": "我的相机到了。", "source": "<a href="http://weibo.com" rel="nofollow">新浪微博</a>", "favorited": false, "truncated": false, "in_reply_to_status_id": "", "in_reply_to_user_id": "", "in_reply_to_screen_name": "", "geo": null, "mid": "5610221544300749636", "annotations": [], "reposts_count": 5, "comments_count": 8, "user": { "id": 1073880650, "screen_name": "檀木幻想", "name": "檀木幻想", "province": "11", "city": "5", "location": "北京 朝阳区", "description": "请访问微博分析家。", "url": "http://www.weibo007.com/", "profile_image_url": "http://tp3.sinaimg.cn/1073880650/50/1285051202/1", "domain": "woodfantasy", "gender": "m", "followers_count": 723, "friends_count": 415, "statuses_count": 587, "favourites_count": 107, "created_at": "Sat Nov 14 00:00:00 +0800 2009", "following": true, "allow_all_act_msg": true, "remark": "", "geo_enabled": true, "verified": false, "allow_all_comment": true, "avatar_large": "http://tp3.sinaimg.cn/1073880650/180/1285051202/1", "verified_reason": "", "follow_me": true, "online_status": 0, "bi_followers_count": 199 } } } 2. hbase 篇 1)无法建立索引 hbase 最大的问题是无法建立索引 两个变象建立索引的办法 ...

January 1, 2018 · 3 min

利用redis实现分布式环境下的限频

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc redis 本身有计数器,并且可以做原子的增1操作,特别适合用来做分布式环境下的限频 # coding:utf-8 import time import threading from redis import StrictRedis class Counter(object): def __init__(self, redis_url): self.redis_client = StrictRedis.from_url(redis_url) def increment(self, key): t = int(time.time()) sign = t / 60 redis_key = key + ':' + str(sign) counter = self.redis_client.incr(redis_key) # 注:设置key的失效时间没有必要和原子增1操作包含在一个事务中。 self.redis_client.expire(redis_key, 300) # 设置key的失效时间300 seconds return counter if __name__ == '__main__': redis_url = 'redis://127.0.0.1:6379/0' c = Counter(redis_url) for i in range(100): time.sleep(0.2) x = c.increment('hello') if x > 50: print "over limit" print x 这里限频有个前提条件,就是分布式环境中时钟,必须尽量对齐。 在上面的例子中频率限制就是50次/分钟

January 1, 2018 · 1 min

Redis 关于大量1级key的测试

版权声明 本站原创文章 由 萌叔 发表 转载请注明 萌叔 | http://vearne.cc 起因:有文章提到Redis的1级key数量不应该超过100w 我十分怀疑这个结论是怎么得到的。我们知道在Redis中一个DB中的所有key维护在一个HashMap中,较多的key当然会导致,key的移动更加困难,当然由于HashMap的原因,在rehash时,可能消耗更多的时间;另外内存可能有少部分浪费。可是key的数量增大到100w以上,是否真的会带来其它问题吗 1. 测试验证 1.1 测试方法 使用string 类型,不断的插入新key,为了保证key仅可能不重复,且长度一致 使用自增变量 i 的md5值 每写入10w个key记录一下当前的内存值,已经插入这10w个key所消耗的时间 import json import redis import random import time import hashlib r = redis.Redis(host='localhost',port=6379,db=5) SIZE = 100000 fp = open('result.txt', 'w') counter = 0 for i in xrange(0, 100): t1 = time.time() for j in xrange(0, SIZE): counter += 1 print 'counter', counter m = hashlib.md5() m.update(str(i * SIZE + j)) key = m.hexdigest() r.set(key, 1) t2 = time.time() margin = t2 - t1 info = r.info() #print info ll = [] ll.append( str((i + 1) * SIZE) ) ll.append(str(margin)) ll.append(str(info['used_memory'])) fp.write(','.join(ll) + '\n') fp.flush() print (i + 1) * SIZE, margin, info['used_memory'] fp.close() 1.2 测试数据 测试总计写入1000w个key,耗时大概在半小时 图1 ...

January 1, 2018 · 1 min