redis为什么只能支持redis单线程为什么快

redis是个单线程的程序,为什么会这么快呢?每秒10000?这个有点不解,具体是快在哪里呢?EPOLL?内存? - 知乎843被浏览123271分享邀请回答9118 条评论分享收藏感谢收起466 条评论分享收藏感谢收起查看更多回答2 个回答被折叠()现在的位置:
-& 什么是redis,redis能做什么,redis应用场景什么是redis,redis能做什么,redis应用场景1. 什么是RedisRedis是由意大利人Salvatore Sanfilippo(网名:antirez)开发的一款内存高速缓存数据库。Redis全称为:Remote Dictionary Server(远程数据服务),该软件使用C语言编写,Redis是一个key-value存储系统,它支持丰富的数据类型,如:string、list、set、zset(sorted set)、hash。2. Redis特点Redis以内存作为数据存储介质,所以读写数据的效率极高,远远超过数据库。以设置和获取一个256字节字符串为例,它的读取速度可高达110000次/s,写速度高达81000次/s。Redis跟memcache不同的是,储存在Redis中的数据是持久化的,断电或重启后,数据也不会丢失。因为Redis的存储分为内存存储、磁盘存储和log文件三部分,重启后,Redis可以从磁盘重新将数据加载到内存中,这些可以通过配置文件对其进行配置,正因为这样,Redis才能实现持久化。Redis支持主从模式,可以配置集群,这样更利于支撑起大型的项目,这也是Redis的一大亮点。3. Redis应用场景,它能做什么众多语言都支持Redis,因为Redis交换数据快,所以在服务器中常用来存储一些需要频繁调取的数据,这样可以大大节省系统直接读取磁盘来获得数据的I/O开销,更重要的是可以极大提升速度。拿大型网站来举个例子,比如a网站首页一天有100万人访问,其中有一个板块为推荐新闻。要是直接从数据库查询,那么一天就要多消耗100万次数据库请求。上面已经说过,Redis支持丰富的数据类型,所以这完全可以用Redis来完成,将这种热点数据存到Redis(内存)中,要用的时候,直接从内存取,极大的提高了速度和节约了服务器的开销。总之,Redis的应用是非常广泛的,而且极有价值,真是服务器中的一件利器,所以从现在开始,我们就来一步步学好它。&文章出自: 本站所有文章,除注明出处外皆为原创,转载请注明本文地址,版权所有。分享到:您可能还会对这些文章感兴趣!&最新文章最受欢迎热门排行最新评论Redis误区_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
阅读已结束,下载文档到电脑
想免费下载本文?
定制HR最喜欢的简历
下载文档到电脑,方便使用
还剩3页未读,继续阅读
定制HR最喜欢的简历
你可能喜欢下次自动登录
现在的位置:
& 综合 & 正文
从hiredis使用出core谈谈redis多线程的使用
从hiredis使用出core谈谈redis多线程的使用
发表于3个月前( 15:29)
阅读(607) | 评论() 11人收藏此文章,
摘要 hireedis多线程出core原因
在实际工作中,我需要使用redis的客户端去连接redis,于是选择了hiredis客户端(公司强推)。
hiRedis 是 Redis 官方指定的 C 语言客户端开发包,支持 Redis 完整的命令集、管线以及事件驱动编程。
1、情景描述
1.1 使用场景
一个epool模型的服务器不断接受外界请求,这个服务器框架给用户预留一个回调函数(多线程),回调函数为用户自己去实现的业务逻辑,其中redis的使用就需要在这个回调函数内部实现。
1.2 初步实现方案
在程序启动的时候,我就初始化redis的连接,获得hiredis句柄。然后把hiredis句柄传入到线程函数里面。让其做相应的业务逻辑。
很不幸,一次请求都没问题,做压力测试,同时开20个线程访问,程序立即出core。
线上出core如下:
at sds.c:97
at hiredis.c:1186
at hiredis.c:1206
at hiredis.c:1267
at hiredis.c:1276
res_lens=0x2bd5f54208,
res_buf=0x2bd5f54398 "")
at default_handler.cpp:659
当时经过多次尝试。把连接放入到了每个线程中。那么就不会出core了。
2、线下复现
因为不方便公开公司代码,所以我写一个类似的代码来复现这个case。
代码主要有testredis.cpp和Makefile(自己指定hiredis目录)。用法是 ./redis
-n [num] -h [host] -p [port], n为host数目,多个host用"-"进行分割。
testredis.cpp
#include&unistd.h&
&hiredis.h&
&stdlib.h&
&string.h&
&pthread.h&
uint32 unsigned int
MAX_REDIS_SERVER_CNT 10
MAX_REDIS_IPS 1024
typedef struct _redis_conf_t
char redis_ips[MAX_REDIS_IPS];
char redis_ip_array[MAX_REDIS_SERVER_CNT][MAX_REDIS_IPS];
typedef struct _redis_data_t
redisContext
*rc[MAX_REDIS_SERVER_CNT];
redis_conf
redis_data
void show_usage()
printf("usage:
./redis -n [num] -h [host] -p [port]\n");
int main_parse_option(int argc, char **argv)
= getopt(argc, argv, "h:p:n:"))
switch (c)
sprintf(g_cfg.redis_ips,
g_cfg.redis_port
= atoi(optarg);
g_cfg.redis_num
= atoi(optarg);
show_usage();
fflush(stdout);
return -1;
test_thread1(void*
redis_data*
redis_ins = (redis_data*)
redisReply
for(int i=0;
i&redis_ins-&redis_ i++)
= (redisReply *)redisCommand( redis_ins-&rc[i] ,"SET
%s %s", "foo", "hello
freeReplyObject(reply);
int init_data()
g_data.redis_num
struct timeval
timeout = { 1, 500000 };
char *ptok
char *part
= strtok_r(g_cfg.redis_ips, "-",
while(part)
strcpy(g_cfg.redis_ip_array[num++],
= strtok_r(NULL, "-",
!= g_cfg.redis_num || num & MAX_REDIS_SERVER_CNT)
printf("ip
num[%d] not equal redis_num[%d] or not vaild\n",
num, g_cfg.redis_num);
g_data.redis_num
= (num & MAX_REDIS_SERVER_CNT ) ? MAX_REDIS_SERVER_CNT :
i&g_data.redis_ i++)
g_data.rc[i]
= redisConnectWithTimeout( g_cfg.redis_ip_array[i], g_cfg.redis_port , timeout);
g_data.rc[i] == NULL || g_data.rc[i]-&err)
printf("content
to redis server[%s:%u], error[%s]\n",
g_cfg.redis_ip_array[i],
g_cfg.redis_port, g_data.rc[i]-&errstr
goto exit;
for(int j=0;
if(g_data.rc[j]
redisFree(g_data.rc[j]);
return -1;
int destory_data()
for(int j=0;
j&g_data.redis_ j++)
if(g_data.rc[j]
redisFree(g_data.rc[j]);
int main(int argc, char**
g_cfg.redis_ips[0]
g_cfg.redis_port
g_cfg.redis_num
0 != main_parse_option(argc, argv) )
show_usage();
return -1;
0 == g_cfg.redis_num || g_cfg.redis_num &
MAX_REDIS_SERVER_CNT )
printf("the
reids num[%u] is not vaild\n",
g_cfg.redis_num);
show_usage();
= init_data();
printf("init
num fail\n");
return -1;
for(int i=0;
i&100; i++)
pthread_create(&t[i],
NULL, test_thread1, &g_data);
for(int i=0;
i&100; i++)
pthread_join(t[i],
destory_data();
testredis.cpp
-g testredis.cpp -I./hiredis -L./hiredis -lhiredis -lpthread -o redis
2.2 编译执行
liujun05@cq01-rdqa-dev012.cq01:~/test/hiredis$
./redis -n2 -h10.48.46.26-10.46.175.102
glibc detected *** double free or
corruption (!prev): 0xaa80 ***
(core dumped)
可以看到出core了
0xaf2e2ed in raise () from /lib64/tls/libc.so.6
0xaf2fa3e in abort () from /lib64/tls/libc.so.6
0xaf62db1 in __libc_message () from /lib64/tls/libc.so.6
0xaf6888e in _int_free () from /lib64/tls/libc.so.6
0xaf68bd6 in free () from /lib64/tls/libc.so.6
0x3c75 in redisBufferWrite (c=0x50a010, done=0x571c008c) at hiredis.c:1162
0x3d3e in redisGetReply (c=0x50a010, reply=0x571c00b8) at hiredis.c:1195
0x3f62 in redisvCommand (c=0x50a010, format=Variable "format" is not available.
at hiredis.c:1296
0x4006 in redisCommand (c=Variable "c" is not available.
at hiredis.c:1313
0x13e7 in test_thread1 (data=0x509ba0) at testredis.cpp:88
0x610a in start_thread () from /lib64/tls/libpthread.so.0
0xafc6003 in clone () from /lib64/tls/libc.so.6
0x0000 in ?? ()
虽然出core位置不一致,但是经过查看代码,出core的原因应该是一致的。
2.3 原因分析
从堆栈5可以看到 hiredis.c的1162行出的core,打开hiredis.c
} else if (nwritten
if (nwritten
== (signed)sdslen(c-&obuf))
sdsfree(c-&obuf);
c-&obuf = sdsempty();
c-&obuf = sdsrange(c-&obuf,nwritten,-1);
可以看到的确在1152行对c-&obuf进行了一次free导致出core。
我们分析下调用关系,首先调用redisCommand.
1309 void *redisCommand(redisContext
*c, const char *format,
void *reply
va_start(ap,format);
reply = redisvCommand(c,format,ap);
va_end(ap);
然后调用redisvCommand
1303 void *redisvCommand(redisContext
*c, const char *format, va_list ap)
if (redisvAppendCommand(c,format,ap)
!= REDIS_OK)
return NULL;
return __redisBlockForReply(c);
接着调用redisvAppendCommand
&span&&/span&1233 int redisvAppendCommand(redisContext
*c, const char *format, va_list ap)
len = redisvFormatCommand(&cmd,format,ap);
__redisSetError(c,REDIS_ERR_OOM,"Out
of memory");
return REDIS_ERR;
if (__redisAppendCommand(c,cmd,len)
!= REDIS_OK) {
free(cmd);
return REDIS_ERR;
free(cmd);
return REDIS_OK;
这里,我们需要care调用__redisAppendCommand.
1220 int __redisAppendCommand(redisContext
*c, char *cmd, size_t len)
newbuf = sdscatlen(c-&obuf,cmd,len);
if (newbuf
== NULL) {
__redisSetError(c,REDIS_ERR_OOM,"Out
of memory");
return REDIS_ERR;
return REDIS_OK;
问题出现了。
对于任意一个多线程,他传入的redisContext* c都是一个,那么他们也公用同一个c-&obuf,这里很明显,线程数据是耦合的。
当一个线程调用sdsfree c-&obuf,其他任意一个线程使用c-&obuf都会导致出core. 这也是我所谓的hiredis对多线程支持的不好的地方。
3. 终极解决方案
那么,如果我一定要在多线程中通过hiredis客户端调用redis呢。有没有方案了,答案肯定是有,只不过性能稍差。
原先的做法是先获得hiredis连接句柄,然后把句柄传入到多线程中,让多线程使用。现在改成在线程里面连接获得hiredis句柄,然后再进行使用。当然,代价是对于每个请求,都需要去连接redis服务器,加大了网络开销的同时还加大了redis的请求。
redis是单线程异步模型,hiredis这个客户端看来也只支持单线程。希望后续有redis的相关程序猿来改进相应问题,在hiredis使用多线程需要慎重。
&&&&推荐文章:
【上篇】【下篇】}

我要回帖

更多关于 redis 单线程 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信