hiredis查询失败时出现key丢失问题定位

问题描述

hiredis查询key失败后,出现key丢失的问题。

REDIS版本及源码:6.0.10

hiredis版本及源码:v1.0.2

案例描述:

REDIS中预先写入1个字符串键"hello",客户端代码基于hiredis,创建100个读线程和100个写线程,每个线程里发起一次短连接读写key,代码参考:

#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <hiredis/hiredis.h>
#include <pthread.h>

#define SERV_IP "127.0.0.1"
#define SERV_PORT 6379
#define NUM_READER 100
#define NUM_WRITER 100

static const int OK = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int DBGetString(const char *key, char *value) {
    struct timeval tv;
    tv.tv_sec = 5; tv.tv_usec = 0;
    redisContext *c = redisConnectWithTimeout(SERV_IP, SERV_PORT, tv);
    assert(c != NULL && !c->err);

    char cmd[256];
    sprintf(cmd, "GET %s", key);
    redisReply *reply = (redisReply *)redisCommand(c, cmd);
    assert(reply->str != NULL);
    strcpy(value, reply->str);
    freeReplyObject(reply);
    redisFree(c);
    return OK;
}

int DBSetString(const char *key, const char *value) {
    struct timeval tv;
    tv.tv_sec = 5; tv.tv_usec = 0;
    redisContext *c = redisConnectWithTimeout(SERV_IP, SERV_PORT, tv);
    assert(c != NULL && !c->err);

    char cmd[256];
    sprintf(cmd, "SET %s %s", key, value);
    redisReply *reply = (redisReply *)redisCommand(c, cmd);
    freeReplyObject(reply);
    redisFree(c);
    return OK;
}

void *Reader(void *args) {
    char buf[256];
    DBGetString("hello", buf);
    printf("%s\n", buf);
    return NULL;
}

void *Writer(void *args) {
    DBSetString("hello", "world");
    return NULL;
}

void testcase() {
    time_t t1 = time(NULL);
    pthread_t wid[NUM_WRITER];
    pthread_t rid[NUM_READER];

    for (int i = 0; i < NUM_WRITER; ++i) {
        pthread_create(&wid[i], NULL, Writer, NULL);
    }
    for (int i = 0; i < NUM_READER; ++i) {
        pthread_create(&rid[i], NULL, Reader, NULL);
    }

    for (int i = 0; i < NUM_WRITER; ++i) {
        pthread_join(wid[i], NULL);
    }
    for (int i = 0; i < NUM_READER; ++i) {
        pthread_join(rid[i], NULL);
    }
    time_t t2 = time(NULL);
    printf("%ld s\n", t2 - t1);
}

int main() {
    testcase();
    return 0;
}

定位思路

Redis key丢失一般有如下原因:

  • key被客户端删除。
  • key是过期键,到期自动删除。
  • 因为内存不足导致key被逐出。

本案例中的客户端只有get,set,没有删除操作,未设置键的过期时间,且机器内存也足够(4G以上)。初步分析不出原因,只能结合REDIS源码正向定位,于是有了第二种思路:

REDIS是基于内存的数据库,字符串键肯定在REDIS-SERVER进程中的某个内存地址处,key丢失时这个地址的内容肯定被改写,这时只需要GDB打个数据断点,看下调用栈即可分析原因。

以下给出详细的定位过程。

定位过程

1、使用GCC的-g -O0编译选项,重新编译redis-server, hiredis源代码。(这一步非必要,目的是更方便调试)

2、GDB打数据断点
在这里插入图片描述

3、断点触发,观察调用栈
在这里插入图片描述

观察第5帧,freeMemoryIfNeeded,说明触发了REDIS的内存淘汰机制,GDB打印出的REDIS配置项maxmemory仅为1048576字节(表示内存大小为1M),且内存淘汰机制为allkeys-lru,这个机制会导致key被回收。

解决方法

修改redis配置项maxmemory,内存给多点。(这里给1073741824,表示1G内存)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pcj_888

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值