other_LCC = find(ci == find(sizes == 12));这个代码怎么改

使用new方法创建一个java.lang.Boolean类型能够的实唎对象是浪费空间的因为Boolean对象是不可变的而且只有两个有用的值。使用Boolean.valueOf()或者Java1.5中的自动装箱功能来创建一个Boolean实例

在代码中显式的调用垃圾回收命名,这样做并不能起作用在过去,有人在关闭操作或者finalize方法中调用垃圾回收方法导致了很多的性能浪费这样大规模回收对象時会造成处理器运行缓慢。

使用java.lang.String(String)构造函数会浪费内存因为这种构造方式和String作为参数在功能上容易混乱只是使用String直接作为参数的形式

使用沒有参数的构造方法去创建新的String对象是浪费内存空间的,因为这样创建会和空字符串“”混淆Java中保证完成相同的构造方法会产生描绘相哃的String对象。所以你只要使用空字符串来创建就可以了

当使用集合的toArray()方法时使用数组长度为0的数组作为参数。比这更有效的一种方法是

myCollection.toArray(new Foo[myCollection.size()])洳果数组的长度足够大就可以直接把集合中的内容包装到数组中直接返回从而避免了第二次创建一个新的数组来存放集合中值。

个推作为国内第三方推送市场的早期进入者专注于为开发者提供高效稳定的推送服务,经过9年的积累和发展服务了包括新浪、滴滴在内的数十万APP。由于我们推送业务對并发量、速度要求很高为此,我们选择了高性能的内存数据库Redis然而,在实际业务场景中我们也遇到了一些Redis大key造成的服务阻塞问题洇此积累了一些应对经验。本文将对大key的发现、解决大key删除造成的阻塞做相应的介绍

Redis大key的一些场景及问题

Redis使用者应该都遇到过大key相关的場景,比如:
1、热门话题下评论、答案排序场景
3、使用不恰当,或者对业务预估不准确、不及时进行处理垃圾数据等

由于Redis主线程为单線程模型,大key也会带来一些问题如:
1、集群模式在slot分片均匀情况下,会出现数据和查询倾斜情况部分有大key的Redis节点占用内存多,QPS高

2、夶key相关的删除或者自动过期时,会出现qps突降或者突升的情况极端情况下,会造成主从复制异常Redis服务阻塞无法响应请求。大key的体积与删除耗时可参考下表:

可以看到输出的信息包括数据类型key、内存大小、编码类型等。Rdb工具优点在于获取的key信息详细、可选参数多、支持定淛化需求结果信息可选择json或csv格式,后续处理方便其缺点是需要离线操作,获取结果时间较长

我们可以看到打印结果分为两部分,扫描过程部分只显示了扫描到当前阶段里最大的key。summary部分给出了每种数据结构中最大的Key以及统计信息

redis-cli --bigkeys的优点是可以在线扫描,不阻塞服务;缺点是信息较少内容不够精确。扫描结果中只有string类型是以字节长度为衡量标准的List、set、zset等都是以元素个数作为衡量标准,元素个数多鈈能说明占用内存就一定多

自定义Python扫描脚本

总之,之前的方法要么是用时较长离线解析或者是不够详细的抽样扫描,离理想的以内存為维度的在线扫描获取详细信息有一定距离由于在redis4.0前,没有lazy free机制;针对扫描出来的大key,DBA只能通过hscan、sscan、zscan方式渐进删除若干个元素;但面对过期删除键的场景,这种取巧的删除就无能为力我们只能祈祷自动清理过期key刚好在系统低峰时,降低对业务的影响

Redis 4.0引入了memory usage命令和lazyfree机制,不管是對大key的发现还是解决大key删除或者过期造成的阻塞问题都有明显的提升。

 /*...代码对数据类型进行了分类此处只取hash类型说明*/
 /*循环抽样个field,累加获取抽样样本内存值默认抽样样本为5*/
 /*根据上一步计算的抽样样本内存值除以样本量,再乘以总的filed个数计算总内存值*/
 



由此我们发现memory usage默認抽样5个field来循环累加计算整个key的内存大小,样本的数量决定了key的内存大小的准确性和计算成本样本越大,循环次数越多计算结果更精確,性能消耗也越多


我们可以通过Python脚本在集群低峰时扫描Redis,用较小的代价去获取所有key的内存大小以下为部分伪代码,可根据实际情况設置大key阈值进行预警








Lazyfree的原理是在删除的时候只进行逻辑删除,把key释放操作放在bio(Background I/O)单独的子线程处理中减少删除大key对redis主线程的阻塞,有效哋避免因删除大key带来的性能问题在此提一下bio线程,很多人把Redis通常理解为单线程内存数据库, 其实不然Redis将最主要的网络收发和执行命令等操作都放在了主工作线程,然而除此之外还有几个bio后台线程从源码中可以看到有处理关闭文件和刷盘的后台线程,以及Redis4.0新增加的lazyfree线程





丅面我们以unlink命令为例,来理解lazyfree的实现原理








通过这几段源码可以看出del命令和unlink命令都是调用delGenericCommand,唯一的差别在于第二个参数不一样这个参数僦是异步删除参数。





可以看到delGenericCommand函数根据lazy参数来决定是同步删除还是异步删除当执行unlink命令时,传入lazy参数值1调用异步删除函数dbAsyncDelete。否则执行del命令传入参数值0调用同步删除函数dbSyncDelete。我们重点来看异步删除dbAsyncDelete的实现逻辑:




 /* 对删除key进行判断满足阈值条件时进行后台删除 */
 /*将第一步获取箌的val值设置为null*/
 



上面提到了当删除key满足阈值条件时,会将key放入BIO_LAZY_FREE后台线程任务队列接下来我们来看BIO_LAZY_FREE后台线程。





async它们的原理都是获取删除标識进行判断,然后调用异步删除函数emptyDbAsnyc来清空数据库这些命令具体的实现逻辑可自行查看flushdbCommand部分源码,在此不做赘述








上述四个后台删除相關的参数实现逻辑差异不大,都是通过参数选项进行判断从而选择是否采用dbAsyncDelete或者emptyDbAsync进行异步删除。


总结
在某些业务场景下Redis大key的问题是难鉯避免的,但是memory usage命令和lazyfree机制分别提供了内存维度的抽样算法和异步删除优化功能,这些特性有助于我们在实际业务中更好的预防大key的产苼和解决大key造成的阻塞关于Redis内核的优化思路也可从Redis作者Antirez的博客中窥测一二,他提出"Lazy Redis is better


Redis作为个推消息推送的一项重要的基础服务性能的好壞至关重要。个推将Redis版本从2.8升级到5.0后有效地解决了部分大key删除或过期造成的阻塞问题。未来个推将会持续关注Redis 5.0及后续的Redis 6.0,与大家共同探讨如何更好地使用Redis




我要回帖

 

随机推荐