redis的数据添加过程是怎样的

文章详细介绍了Redis集群中数据的分配方式,通过哈希函数keyHashSlot将键映射到槽位,实现数据的分布。集群状态类ClusterState包含了节点信息,set命令的执行过程涉及了对象编码、数据库操作及槽位管理。文章还讨论了Redis单线程模型的优缺点以及多线程可能带来的问题。
摘要由CSDN通过智能技术生成

redis集群实现(四) 数据的和槽位的分配

class Node{

private long time; // 创建时间

private String name; // 名称

private int flag; // 标示主从,或者在线状态

private String ip;

private int port;

private ClusterState clusterState; // 集群信息

}

class ClusterState{

private Node myself;

private int state;

private int size; // 知道包含一个槽的节点数量

private Dictionary nodes; // 集群节点名单

}

127.0.0.1:7000> set key value

-> Redirected to slot [12539] located at 192.168.39.153:7002

OK

192.168.39.153:7002> get key

"value"

192.168.39.153:7002>struct redisCommand redisCommandTable[] = {

————————————————————————

{"set",setCommand,-3,"wm",0,NULL,1,1,1,0,0},

————————————————————————

可以看出,set命令会执行setCommand函数进行解析,继续进入setCommand函数查看

void setCommand(redisClient *c) {

int j;

robj *expire = NULL;

int unit = UNIT_SECONDS;

int flags = REDIS_SET_NO_FLAGS;

————————————————————————

// value编码

c->argv[2] = tryObjectEncoding(c->argv[2]);

//真正执行set命令的地方

setGenericCommand(c,flags,c->argv[1],c->argv[2],expire,unit,NULL,NULL);

}

继续进入setGenericCommand函数

void setGenericCommand(redisClient *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply) {

//参数检查和过期时间的检查

————————————————————————

//在数据库里设置key value

setKey(c->db,key,val);

//设置完成以后的时间通知

————————————————————————

}

接着看数据库的setKey函数

void setKey(redisDb *db, robj *key, robj *val) {

// 添加或覆写数据库中的键值对

if (lookupKeyWrite(db,key) == NULL) {

dbAdd(db,key,val);

} else {

dbOverwrite(db,key,val);

}

------------------------------------------------------------------------------------------

当没有在数据库中发现key的时候,我们需要执行dbAdd函数把key-value添加到数据库里。

void dbAdd(redisDb *db, robj *key, robj *val) {

// 赋值key的名字

sds copy = sdsdup(key->ptr);

// 添加键值对到字典中

int retval = dictAdd(db->dict, copy, val);

// 如果键已经存在,那么停止

redisAssertWithInfo(NULL,key,retval == REDIS_OK);

// 如果开启了集群模式,就把键保存到槽里面

if (server.cluster_enabled) slotToKeyAdd(key);

}

继续进入slotToKeyAdd函数

//把键key添加到槽里边

void slotToKeyAdd(robj *key) {

// 通过字符串key计算出键对应的槽

unsigned int hashslot = keyHashSlot(key->ptr,sdslen(key->ptr));

// 将槽 slot 作为分数,键作为成员,添加到 slots_to_keys 跳跃表里面

zslInsert(server.cluster->slots_to_keys,hashslot,key);

incrRefCount(key);

}

keyHashSlot是一个哈希函数,通过key映射到一个0-16384的整数,我们来看一下实现

unsigned int keyHashSlot(char *key, int keylen) {

//start end

int s, e;计算key字符串对应的映射值,redis采用了crc16函数然后与0x3FFF取低16位的方法。crc16以及md5都是比较常用的根据key均匀的分配的函数,就这样,用户传入的一个key

们就映射到一个槽上,然后经过gossip协议,周期性的和集群中的其他节点交换信息,最终整个集群都会知道key在哪一个槽上。

(1) 绝大部分请求是纯粹的内存操作(非常快速)

(2) 采用单线程,避免了不必要的上下文切换和竞争条件

(3) 非阻塞IO - IO多路复用

1)单线程的问题

无法发挥多核CPU性能,单进程单线程只能跑满一个CPU

可以通过在单机开多个Redis实例来完善

可以通过数据分片来增加吞吐量,问题(不支持批量操作、扩缩容复杂等)

2)多线程的问题

多线程处理可能涉及到锁 

多线程处理会涉及到线程切换而消耗CPU

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

入伍击寇

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

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

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

打赏作者

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

抵扣说明:

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

余额充值