在大规模分布式系统中,Redis已经成为了最流行的缓存和数据存储方案之一。然而,为了保证Redis系统的高可用性,运维人员需要采取一系列措施,其中最重要的就是对Redis键的过期时间进行管理。
为桐梓等地区用户提供了全套网页设计制作服务,及桐梓网站建设行业解决方案。主营业务为网站建设、成都网站制作、桐梓网站设计,以传统方式定制建设网站,并提供域名空间备案等一条龙服务,秉承以专业、用心的态度为用户提供真诚的服务。我们深信只要达到每一位用户的要求,就会得到认可,从而选择与我们长期合作。这样,我们也可以走得更远!
传统的Redis采用单线程方式对键的过期时间进行管理,这种方式虽然保证了数据一致性,但是在高并发场景下会造成性能瓶颈。为了解决这个问题,Redis 4.0引入了多线程方式实现过期管理,大大提升了Redis的性能。
多线程方式实现高效的过期管理
Redis的多线程方式实现过期管理的原理是将Redis键值对按照过期时间进行排序,将快要过期的数据优先淘汰。Redis将过期时间轮(Expire wheel)划分为多个时间槽,每个时间槽对应一个过期时间,Redis在每个时间槽中维护一个链表结构,存储在这个时间槽内过期的键值对。Redis通过多个线程定时移动时间槽,将过期时间较短的键值对优先淘汰,从而达到高效的过期管理。
下面是Redis多线程方式实现过期管理的代码:
#define REDIS_EXPIRELOOKUPS_PER_LOOP 20
void activeExpireCycle(int type) {
/* 定义时钟的处理周期 */
long long start = ustime(), timelimit_exit = 0, timelimit_us = 0;
unsigned int expired = 0;
if (server.masterhost && server.repl_slave_ro == 1) return;
/* 如果Redis是开启写保护的,直接返回 */
if (server.activerehashing) return;
if (type == ACTIVE_EXPIRE_CYCLE_FAST)
timelimit_us = 1000;
else
timelimit_us = server.maxmemory_samples ? 0 : 100000;
/* 如果Redis有设置最大循环时间,则使用它 */
if (server.maxmemory_samples != 0) {
timelimit_exit = start + (1000000/server.hz)*server.maxmemory_samples;
}
/* 停止keys迭代器 */
signalFlushedDb(-1);
while(1) {
long long now;
int j;
/* 如果达到了我们的定义的过期处理扫描次数,需要退出循环。 */
if (type == ACTIVE_EXPIRE_CYCLE_FAST)
timelimit_exit = start + 1000;
if (server.maxmemory_samples != 0) {
if (ustime() > timelimit_exit) break;
}
/* 记录当前的时间戳 */
now = mstime();
/* 减少计数器的值 */
if (server.lazyfree_lazy_expire) lazyfreeTryExpire(now);
/* 需要分配候选键数组,该数组用来记录到期的键 */
if (expired_entries_pool_size
populateExpiredArray();
/* 遍历所有哈希表,查找到期键 */
for (j = 0; j
int expired_this_loop = 0;
/* 获得当前数据库 */
redisDb *db = server.db+j;
/* 因为执行过期操作时,需要修改键而且Redis是事件驱动的,
如果在过期操作时有其它操作正在进行,可能会产生冲突
所以,该库必须被标记 */
if (dictSize(db->expires)) {
dictEntry *de;
de = dictFind(db->expires,dictGetSomeKeys(db->expires));
if (de) {
long long t = dictGetSignedIntegerVal(de)-now;
/* 如果该键已经过期,将其淘汰 */
if (t
flushdbAsync(j);
expired+=dictSize(db->expires);
continue;
}
/* 计算出该时间槽所属的槽位 */
int i = (t/1000)
(t/1000) : (REDIS_EXPIRELOOKUPS_PER_LOOP-1);
/* 将该键存储到该时间槽所属的链表中 */
expired_candidates[i][expired_count[i]] = de;
expired_count[i]++;
}
}
}
/* 遍历所有的时隙,逐个处理过期键值 */
for (j = 0; j
dictEntry *de;
if (expired_count[j] == 0) continue;
/* 遍历当前时间槽的所有键值对 */
while(expired_count[j]--) {
de = expired_candidates[j][expired_count[j]];
dictDelete(server.db[de->v.val].dict,dictGetKey(de));
dictDelete(server.db[de->v.val].expires,dictGetKey(de));
notifyKeyspaceEvent(REDIS_NOTIFY_EXPIRED,
"expired",dictGetKey(de),
dictGetKeyLength(de));
decrRefCount(dictGetKey(de));
server.dirty++;
if (++expired >= REDIS_EXPIREDLOOKUPS_PER_CRON) break;
}
/* 如果达到最大过期键扫描次数,需要退出循环 */
if (expired >= REDIS_EXPIREDLOOKUPS_PER_CRON) {
goto end;
}
}
}
end:
/* 重置过期键候选数组 */
expired_entries_pool_size = 0;
memset(expired_candidates,0,sizeof(expired_candidates));
memset(expired_count,0,sizeof(expired_count));
/* 重置keys迭代器 */
signalFlushedDb(-1);
}
如上面的代码所示,Redis在以上循环中处理过期键值对。具体而言,Redis将键值对存储在过期时间槽所属的链表中,每次清理的是快要过期的键,避免因为并发性能瓶颈而使Redis出现性能问题。
结语
采用多线程方式实现高效的过期管理方式是实现Redis高可用性的关键之一。通过优化Redis的过期管理方式,我们可以更好地保证Redis的性能和稳定性,为用户带来更好的使用体验。
香港服务器选创新互联,2H2G首月10元开通。
创新互联(www.cdcxhl.com)互联网服务提供商,拥有超过10年的服务器租用、服务器托管、云服务器、虚拟主机、网站系统开发经验。专业提供云主机、虚拟主机、域名注册、VPS主机、云服务器、香港云服务器、免备案服务器等。
文章名称:强可用的Redis多线程实现高效的过期管理(redis过期多线程)
标题网址:http://www.shufengxianlan.com/qtweb/news22/264122.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联