- 浏览: 2654611 次
- 来自: 杭州
文章分类
- 全部博客 (1188)
- webwork (4)
- 网摘 (18)
- java (103)
- hibernate (1)
- Linux (85)
- 职业发展 (1)
- activeMQ (2)
- netty (14)
- svn (1)
- webx3 (12)
- mysql (81)
- css (1)
- HTML (6)
- apache (3)
- 测试 (2)
- javascript (1)
- 储存 (1)
- jvm (5)
- code (13)
- 多线程 (12)
- Spring (18)
- webxs (2)
- python (119)
- duitang (0)
- mongo (3)
- nosql (4)
- tomcat (4)
- memcached (20)
- 算法 (28)
- django (28)
- shell (1)
- 工作总结 (5)
- solr (42)
- beansdb (6)
- nginx (3)
- 性能 (30)
- 数据推荐 (1)
- maven (8)
- tonado (1)
- uwsgi (5)
- hessian (4)
- ibatis (3)
- Security (2)
- HTPP (1)
- gevent (6)
- 读书笔记 (1)
- Maxent (2)
- mogo (0)
- thread (3)
- 架构 (5)
- NIO (5)
- 正则 (1)
- lucene (5)
- feed (4)
- redis (17)
- TCP (6)
- test (0)
- python,code (1)
- PIL (3)
- guava (2)
- jython (4)
- httpclient (2)
- cache (3)
- signal (1)
- dubbo (7)
- HTTP (4)
- json (3)
- java socket (1)
- io (2)
- socket (22)
- hash (2)
- Cassandra (1)
- 分布式文件系统 (5)
- Dynamo (2)
- gc (8)
- scp (1)
- rsync (1)
- mecached (0)
- mongoDB (29)
- Thrift (1)
- scribe (2)
- 服务化 (3)
- 问题 (83)
- mat (1)
- classloader (2)
- javaBean (1)
- 文档集合 (27)
- 消息队列 (3)
- nginx,文档集合 (1)
- dboss (12)
- libevent (1)
- 读书 (0)
- 数学 (3)
- 流程 (0)
- HBase (34)
- 自动化测试 (1)
- ubuntu (2)
- 并发 (1)
- sping (1)
- 图形 (1)
- freemarker (1)
- jdbc (3)
- dbcp (0)
- sharding (1)
- 性能测试 (1)
- 设计模式 (2)
- unicode (1)
- OceanBase (3)
- jmagick (1)
- gunicorn (1)
- url (1)
- form (1)
- 安全 (2)
- nlp (8)
- libmemcached (1)
- 规则引擎 (1)
- awk (2)
- 服务器 (1)
- snmpd (1)
- btrace (1)
- 代码 (1)
- cygwin (1)
- mahout (3)
- 电子书 (1)
- 机器学习 (5)
- 数据挖掘 (1)
- nltk (6)
- pool (1)
- log4j (2)
- 总结 (11)
- c++ (1)
- java源代码 (1)
- ocr (1)
- 基础算法 (3)
- SA (1)
- 笔记 (1)
- ml (4)
- zokeeper (0)
- jms (1)
- zookeeper (5)
- zkclient (1)
- hadoop (13)
- mq (2)
- git (9)
- 问题,io (1)
- storm (11)
- zk (1)
- 性能优化 (2)
- example (1)
- tmux (1)
- 环境 (2)
- kyro (1)
- 日志系统 (3)
- hdfs (2)
- python_socket (2)
- date (2)
- elasticsearch (1)
- jetty (1)
- 树 (1)
- 汽车 (1)
- mdrill (1)
- 车 (1)
- 日志 (1)
- web (1)
- 编译原理 (1)
- 信息检索 (1)
- 性能,linux (1)
- spam (1)
- 序列化 (1)
- fabric (2)
- guice (1)
- disruptor (1)
- executor (1)
- logback (2)
- 开源 (1)
- 设计 (1)
- 监控 (3)
- english (1)
- 问题记录 (1)
- Bitmap (1)
- 云计算 (1)
- 问题排查 (1)
- highchat (1)
- mac (3)
- docker (1)
- jdk (1)
- 表达式 (1)
- 网络 (1)
- 时间管理 (1)
- 时间序列 (1)
- OLAP (1)
- Big Table (0)
- sql (1)
- kafka (1)
- md5 (1)
- springboot (1)
- spring security (1)
- Spring Boot (3)
- mybatis (1)
- java8 (1)
- 分布式事务 (1)
- 限流 (1)
- Shadowsocks (0)
- 2018 (1)
- 服务治理 (1)
- 设计原则 (1)
- log (0)
- perftools (1)
最新评论
-
siphlina:
课程——基于Python数据分析与机器学习案例实战教程分享网盘 ...
Python机器学习库 -
san_yun:
leibnitz 写道hi,我想知道,无论在92还是94版本, ...
hbase的行锁与多版本并发控制(MVCC) -
leibnitz:
hi,我想知道,无论在92还是94版本,更新时(如Puts)都 ...
hbase的行锁与多版本并发控制(MVCC) -
107x:
不错,谢谢!
Latent Semantic Analysis(LSA/ LSI)算法简介 -
107x:
不错,谢谢!
Python机器学习库
最近发现线上的session 服务器每隔一段时间内存占用就达到24G,通过redis info查看发现expires key没有被删除:
db1:keys=101177370,expires=101165505
研究了一下才发现,只有在配置文件中设置了最大内存时候才会调用这个函数,而设置这个参数的意义是,你把当做一个内存而不是数据库。
redis如何删除过期数据?
用一个可以 "find reference" 的 IDE, 沿着 setex( Set the value and expiration of a key ) 命令一窥究竟:
void setexCommand(redisClient *c) { c->argv[3] = tryObjectEncoding(c->argv[3]); setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2]); }
setGenericCommand 是一个实现 set,setnx,setex 的通用函数,参数设置不同而已。
void setCommand(redisClient *c) { c->argv[2] = tryObjectEncoding(c->argv[2]); setGenericCommand(c,0,c->argv[1],c->argv[2],NULL); } void setnxCommand(redisClient *c) { c->argv[2] = tryObjectEncoding(c->argv[2]); setGenericCommand(c,1,c->argv[1],c->argv[2],NULL); } void setexCommand(redisClient *c) { c->argv[3] = tryObjectEncoding(c->argv[3]); setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2]); }
再看 setGenericCommand :
void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire) { long seconds = 0; /* initialized to avoid an harmness warning */ if (expire) { if (getLongFromObjectOrReply(c, expire, &seconds, NULL) != REDIS_OK) return; if (seconds <= 0) { addReplyError(c,"invalid expire time in SETEX"); return; } } if (lookupKeyWrite(c->db,key) != NULL && nx) { addReply(c,shared.czero); return; } setKey(c->db,key,val); server.dirty++; if (expire) setExpire(c->db,key,time(NULL)+seconds); addReply(c, nx ? shared.cone : shared.ok); }
13 行处理 "Set the value of a key, only if the key does not exist" 的场景, 17 行插入这个 key , 19 行设置它的超时,注意时间戳已经被设置成了到期时间。这里要看一下 redisDb ( 即 c ->db ) 的定义:
typedef struct redisDb { dict *dict; /* The keyspace for this DB */ dict *expires; /* Timeout of keys with a timeout set */ dict *blocking_keys; /* Keys with clients waiting for data (BLPOP) */ dict *io_keys; /* Keys with clients waiting for VM I/O */ dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */ int id; } redisDb;
仅关注 dict 和 expires ,分别来存 key-value 和它的超时,也就是说如果一个 key-value 是有超时的,那么它会存在 dict 里,同时也存到 expires 里,类似这样的形式: dict[key]:value,expires[key]:timeout.
当然 key-value 没有超时, expires 里就不存在这个 key 。 剩下 setKey 和 setExpire 两个函数无非是插数据到两个字典里,这里不再详述。
那么 redis 是如何删除过期 key 的呢。
通过查看 dbDelete 的调用者, 首先注意到这一个函数,是用来删除过期 key 的。
int expireIfNeeded(redisDb *db, robj *key) { time_t when = getExpire(db,key); if (when < 0) return 0; /* No expire for this key */ /* Don't expire anything while loading. It will be done later. */ if (server.loading) return 0; /* If we are running in the context of a slave, return ASAP: * the slave key expiration is controlled by the master that will * send us synthesized DEL operations for expired keys. * * Still we try to return the right information to the caller, * that is, 0 if we think the key should be still valid, 1 if * we think the key is expired at this time. */ if (server.masterhost != NULL) { return time(NULL) > when; } /* Return when this key has not expired */ if (time(NULL) <= when) return 0; /* Delete the key */ server.stat_expiredkeys++; propagateExpire(db,key); return dbDelete(db,key); }
ifNeed 表示能删则删,所以 4 行没有设置超时不删, 7 行在 "loading" 时不删, 16 行非主库不删, 21 行未到期不删。 25 行同步从库和文件。
再看看哪些函数调用了 expireIfNeeded ,有 lookupKeyRead , lookupKeyWrite , dbRandomKey , existsCommand , keysCommand 。通过这些函数命名可以看出,只要访问了某一个 key ,顺带做的事情就是尝试查看过期并删除,这就保证了用户不可能访问到过期的 key 。但是如果有大量的 key 过期,并且没有被访问到,那么就浪费了许多内存。 Redis 是如何处理这个问题的呢。
dbDelete 的调用者里还发现这样一个函数:
1 /* Try to expire a few timed out keys. The algorithm used is adaptive and 2 * will use few CPU cycles if there are few expiring keys, otherwise 3 * it will get more aggressive to avoid that too much memory is used by 4 * keys that can be removed from the keyspace. */ 5 void activeExpireCycle(void) { 6 int j; 7 8 for (j = 0; j < server.dbnum; j++) { 9 int expired; 10 redisDb *db = server.db+j; 11 12 /* Continue to expire if at the end of the cycle more than 25% 13 * of the keys were expired. */ 14 do { 15 long num = dictSize(db->expires); 16 time_t now = time(NULL); 17 18 expired = 0; 19 if (num > REDIS_EXPIRELOOKUPS_PER_CRON) 20 num = REDIS_EXPIRELOOKUPS_PER_CRON; 21 while (num--) { 22 dictEntry *de; 23 time_t t; 24 25 if ((de = dictGetRandomKey(db->expires)) == NULL) break; 26 t = (time_t) dictGetEntryVal(de); 27 if (now > t) { 28 sds key = dictGetEntryKey(de); 29 robj *keyobj = createStringObject(key,sdslen(key)); 30 31 propagateExpire(db,keyobj); 32 dbDelete(db,keyobj); 33 decrRefCount(keyobj); 34 expired++; 35 server.stat_expiredkeys++; 36 } 37 } 38 } while (expired > REDIS_EXPIRELOOKUPS_PER_CRON/4); 39 } 40 } 41
这个函数的意图已经有说明: 删一点点过期 key ,如果过期 key 较少,那也只用一点点 cpu 。 25 行随机取一个 key , 38 行删 key 成功的概率较低就退出。这个函数被放在一个 cron 里,每毫秒被调用一次。这个算法保证每次会删除一定比例的 key ,但是如果 key 总量很大,而这个比例控制的太大,就需要更多次的循环,浪费 cpu ,控制的太小,过期的 key 就会变多,浪费内存——这就是时空权衡了。
最后在 dbDelete 的调用者里还发现这样一个函数:
/* This function gets called when 'maxmemory' is set on the config file to limit * the max memory used by the server, and we are out of memory. * This function will try to, in order: * * - Free objects from the free list * - Try to remove keys with an EXPIRE set * * It is not possible to free enough memory to reach used-memory < maxmemory * the server will start refusing commands that will enlarge even more the * memory usage. */ void freeMemoryIfNeeded(void)
这个函数太长就不再详述了,注释部分说明只有在配置文件中设置了最大内存时候才会调用这个函数,而设置这个参数的意义是,你把 redis 当做一个内存 cache 而不是 key-value 数据库。
以上 3 种删除过期 key 的途径,第二种定期删除一定比例的 key 是主要的删除途径,第一种“读时删除”保证过期 key 不会被访问到,第三种是一个当内存超出设定时的暴力手段。由此也能看出 redis 设计的巧妙之处,
参考:http://www.cppblog.com/richbirdandy/archive/2011/11/29/161184.html
发表评论
-
spring-cloud问题排查
2019-11-18 14:58 520### 问题1报错,应用无法启动 org.springfra ... -
log4j 和slf4j的 类冲突解决
2018-07-14 16:11 19811. 检查 log4j 和slf4j的冲突 pub ... -
HikariPool-1 - Connection is not available, request timed out after
2018-05-07 19:13 6680在开发中遇到 HikariPool-1 - Conn ... -
java.io.StreamCorruptedException: invalid stream header: EFBFBDEF
2018-05-03 16:54 2409Java代码 publi ... -
log4j问题总结-加载配置文件
2014-08-20 16:34 1063今天遇到log4j的log4j.properties没有生效 ... -
一个诡异的类冲突错误排查记录
2014-08-13 10:44 816今天遇到一个问题排查了很久,记录一下。 问题原因,日常环境 ... -
tomcat7.0.26的连接数控制bug的问题排查
2014-04-23 19:51 1710今儿线上一台机器,监控一直在告警, mtophost[CM ... -
tomcat,jboss,jetty访问出现404错误问题记录
2014-04-21 19:51 1694原因是WEB-INF下面缺少jboss-web.xml ... -
记录Hadoop native libraries无法load的问题
2014-04-10 19:45 2298今天遇到一个错误,在本地往Hadoop写文件的时候报了一个错 ... -
新浪微博 Redis 实战经验分享
2014-02-07 13:36 832Tape is Dead,Disk is Tape,Flas ... -
Exception性能问题
2014-01-24 22:25 13071.从Exception往上介绍相关结构、代码 ... -
spring加载xml去远程获取dtd验证xml的问题
2014-01-24 11:57 899最近遇到spring 加载xml去远程获取dtd,但外网不 ... -
hbase查询超时导致的错误
2013-12-25 19:53 2715今天上线跑一个hbase程序出现如下错误: org.ap ... -
hbase无法启动问题
2013-12-24 23:53 930今天遇到hbase无法启动,找了很久,才发现可能是data ... -
web.xml配置注意点
2013-12-19 20:29 6401. servlet启动初始化 servlet默认只有访 ... -
Linux服务器Cache占用过多内存导致系统内存不足问题的排查解决
2013-12-17 23:22 3913作者: 大圆那些事 | 文章可以转载,请以超链接形式标明文章 ... -
linger close用法
2013-12-13 14:18 965Linux下tcp连接断开的时候调用close()函数,有 ... -
log4j-over-slf4的log4j Loger加载问题
2013-12-11 00:19 5781log4j-over-slf4是通过定义和log4j同包名 ... -
log4j,slf4j,logback问题总结
2013-12-10 18:12 6530ls4j初始化过程参考:l ... -
maven 小结(打包和单元测试)
2013-12-04 11:53 6861. 如何把一个jar和他依赖的包打包成一个 使用mave ...
相关推荐
定时删除策略是在设置 key 的过期时间时,同时创建一个定时事件,当时间到达时,由事件处理器自动执行 key 的删除操作。惰性删除策略是在每次访问 key 时,检查该 key 是否已经过期,如果已经过期,则删除该 key。...
分布式锁是 Redis 在分布式系统中的重要应用场景,它可以解决多节点共享资源的问题,防止并发操作导致的数据不一致。Redis 提供了 SETNX (Set if Not eXists) 和 EXPIRE 命令组合实现简单版本的分布式锁。在 Java ...
Redis 是一个广泛使用的开源键值存储...了解这些内部机制对于优化 Redis 应用和排查问题至关重要。在实际应用中,还可以通过调整哈希表负载因子、合理设置数据库数量和监控 rehash 进程来提高 Redis 的性能和稳定性。
1. Redis 的内部数据结构包含两个重要的字典:`dict` 存储 key-value 映射,`expires` 存储 key 和其失效时间。当使用 `EXPIRE` 系列命令设置 key 的失效时间时,Redis 会在两个字典中分别更新对应条目。 2. Redis ...
Spring Boot 整合 Redis 是现代 Web 应用中常见的数据缓存策略,它极大地提高了应用程序的性能和响应速度。Spring Boot 提供了便捷的方式与 Redis 集成,利用其强大的缓存功能。以下是关于 Spring Boot 整合 Redis ...
**设置键的时效性**:Redis支持为键设置过期时间,使用`KeyExpire()`或`StringSet()`的`expires`参数: ```csharp // 使用KeyExpire db.KeyExpire("key", DateTimeOffset.UtcNow.AddSeconds(10)); // 或者在设置键...
Redis是一种分布式的、非关系型的(key-value)数据库系统,因其高效性和灵活性,在缓存、消息队列处理等方面有着广泛的应用。Redis支持多种数据结构如字符串(strings)、散列(hashes)、列表(lists)、集合(sets...
Redis的内存管理机制是通过redisDb结构体来实现的,该结构体包含了dict、expires两个成员变量,其中dict用于存储key-value数据,expires用于存储key的过期时间。 在Redis中,freeMemoryIfNeeded()函数用于释放不...
Redis 是一个基于内存的高性能 key-value 数据库,它的特点是支持保存多种数据结构,并且性能非常出色,每秒可以处理超过 10 万次读写操作。下面是对 Redis 的详细介绍: Redis 的特点 Redis 是一个基于内存的高...
4. **数据生命周期管理**:利用Redis提供的过期机制自动删除不再使用的数据。 5. **精细配置**:合理设置Redis的最大内存限制,并结合内存淘汰策略进行管理。 通过对Redis内存存储结构的深入理解以及采取适当的优化...
* 丰富的特性:可用于缓存,消息,按 key 设置过期时间,过期后将会自动删除。 知识点3:Redis 和 Memcached 的区别 Redis 和 Memcached 都是内存数据库,但是它们有以下区别: * 存储方式:Memecache 把数据全部...
自动创建和删除键. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 Redis 哈希/散列 (Hashes). . . . . . . . . . . . . . . . . . . . ....
@Cacheable(value = "cache", key = "key") // 使用默认的 RedisCacheManager @Cacheable(cacheNames = "guavaCacheManager", value = "guavaCache", key = "key") // 使用 GuavaCacheManager ``` #### 五、Redis ...
自动创建和删除键. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 Redis 哈希/散列 (Hashes). . . . . . . . . . . . . . . . . . . . ....
定期过期是指每隔一定的时间,会扫描一定数量的数据库的 expires 字典中一定数量的key,并清除其中已过期的 key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下...