0


Redis开发规范与性能优化(二)

开发规范与性能优化

3.客户端使用

1.【推荐】避免多个应用使用一个Redis示例

正例:不相干的业务拆分,公共数据库做服务化

2.【推荐】使用带有连接池的数据库,可以有效控制链接,同时提高效率,标准使用方式如代码所示

publicclassMain{publicstaticvoidmain(String[] args){JedisPoolConfig jedisPoolConfig =newJedisPoolConfig();
  jedisPoolConfig.setMaxTotal(5);
  jedisPoolConfig.setMaxIdle(2);
  jedisPoolConfig.setTestOnBorrow(true);JedisPool jedisPool =newJedisPool(jedisPoolConfig,"192.168.0.60",6379,3000,null);Jedis jedis =null;try{
   jedis = jedisPool.getResource();// 具体的指令
   jedis.executeCommand();}catch(Exception e){
   log.error("op key {} error:{}", key, e);}finally{//注意这里不是关闭链接,在JedisPool模式下,Jedis会被归还给资源池if(jedis !=null){
    jedis.close()}}}}
连接池参数含义:

在这里插入图片描述

优化建议:
  • 1.maxTotal:最大连接数,早期的版本叫maxActive实际上这个是一个很难回答的问题, 考虑的因素比较多: 1.1 业务希望Redis并发量 1.2 客户端执行命令时间 1.3 Redis资源:例如nodes(例如应用个数) * maxTotal是不能超过redis的最大连接数maxclients 1.4 资源开销:例如虽然希望控制空闲连接(连接池此可可马上使用的连接),但是不希望因为连接池 的频繁释放创建连接造成不必要开销

举个例子:假设

  • 1.一次命令时间(borrow|return resource + Jedis执行命令(含网络))的平均耗时约为1ms,一个链接的QPS大约是1000
  • 2.业务期望的QPS是50000那么理论上需要的资源池大小是50000 / 1000 = 50 个。但事实上这是个理论值,还要考虑到要比理论值预留一些资源,通常来讲maxTotal可以比理论值大一些。但这个值不是越大越好,一方面连接太多占用客户端和服务端资源,另一方面对于Redis这种高QPS的服务器,一个大命令的阻塞即时设置再大资源池仍然会无济于事。
优化建议:
  • 2.maxIdle和minIdle maxIdle实际上才是业务需要的最大连接数,maxTotal是为了给出雨量,所以maxIdle不要设置过小,否则会有new Jedis(新连接)开销 连接池的最佳性能是maxTotal = maxIdle,这样就避免连接池伸缩带来的性能干扰,到那时如果并发量不大或者maxTotal设置过高,会导致不必要的连接资源浪费。一般推荐maxIdle可以设置为上面的业务期望QPS计算出来的理论连接数,maxTotal可以再放大一倍。minIdle()最小空闲连接数,与其说是最小空闲连接数,不如说是"至少需要保持的空闲连接数",在使用连接的过程中如果连接数超过了minIdle那么继续建立连接,如果超过了maxIdle,当超过的连接执行完业务后会慢慢被移除连接池释放掉,如果系统启动完马上就会有很多的请求过来,那么可以给redis连接池 做预热,比如快速的创建一些redis连接,执行简单命令,类似ping(),快速地将连接池里地空闲连接提升到minIdle地数量
publicclassMain{publicstaticvoidmain(String[] args){List<Jedis> minIdleJedisList =newArrayList<Jedis>(jedisPoolConfig.getMinIdle());for(int i =0; i < jedisPoolConfig.getMinIdle(); i++){Jedis jedis =null;try{
    jedis = pool.getResource();
    minIdleJedisList.add(jedis);
    jedis.ping();}catch(Exception e){
    log.error(e.getMessage(), e);}finally{// 注意,这里不能马上close将连接还回连接池,// 否则最后连接池里只会建立1个连接// jedis.close();}}// 同一将预热的连接还回连接池for(int i =0; i < jedisPoolConfig.getMinDile(); i++){Jedis jedis =null;try{
    jedis = minIdleJedisList.get(i);// 将连接该归还回连接池
    jedis.close();}catch(Exception e){
    log.error(e.getMessage(), e);}finally{}}}}

3.【建议】高并发下建议客户端添加熔断功能(例如sentinel、hystrix)

4.【推荐】设置合理的密码,如有必要可以使用SSL加密访问

5.【建议】Redis对于过期键有三种清除策略:
  • 5.1 被动删除:当读/写一个已经过期的key时,会触发惰性删除策略,直接删除掉这个过期key
  • 5.2 主动删除:由于惰性删除策略无法保证冷数据被即时删除,所以Redis会定期主动淘汰一批已过期的key
  • 5.3 当前医用内存超过maxmemory限定时,触发主动清理策略

主动清理策略在Redis4.0之前以供实现了6中内存淘汰策略,在4.0之后又加了2种策略,总共8种:

  • a.针对设置了过期时间的key做处理 a.1 volatile-ttl 在筛选时,会针对设置了过期时间的键值对,根据过期时间的先后进行删除,越早 过期的越先被删除 a.2 volatile-random 就像它的名称一样,在设置了过期时间的键值对种,进行随机删除 a.3 volatile-lru 会使用LRU算法筛选设置了过期时间的键值对删除 a.4 volatile-lfu 会使用LFU算法筛选设置了过期时间的键值对删除
  • b.针对所有的key做处理 b.1 allkeys-random:从所有键值对中随机选择并删除数据 b.2 allkeys-lru 使用LRU算法在所有数据中进行筛选删除 b.3 allkeys-lfu 使用LFU算法在所有数据中进行筛选删除
  • c.不处理 c.1 noeviction:不会剔除任何数据,拒绝所有写入操作并返回客户端错误信息error OOM command not allowed when used memory,此时Redis只相应读操作

当存在热点数据时,LRU的效率很好,但偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重。这时使用LFU可能更好点.

根据自身业务类型,配置好maxmemory-policy(默认时noeviction)推荐使用volatile-lru.如果不设置最大内存,当Redis内存超出物理内存限制时,内存的数据会开始和磁盘产生频繁的交换(swap)会让redis的性能急剧下降。当Redis运行在主从模式时,只有主节点才会执行过期删除策略,然后把删除操作"del key"同步到从节点删除数据

LRU算法(least Recently Used,最近最少使用)
淘汰很久没有被访问过的数据,以最近一次访问时间作为参考

LFU算法(Least Frequently Used,最不经常使用)
淘汰最近一段时间被访问次数最少的数据,以次数作为参考

4.系统内核参数优化

vm.swapiness

swap对于操作系统来说比较重要,当物理内存不足时可以将一部分内存页进行swap到硬盘上,以解燃眉之急。但世界上没有免费午餐,swap空间由硬盘提供,对于需要高并发、高吞吐的应用来说,磁盘IO通常会称为系统瓶颈。在Linux中,并不是等到所有物理内存都使用完才会使用到swap,
系统参数swappniess会决定操作系统使用swap的倾向程度。swappiness的取只范围是0~100,swappiness的值越大,说明操作系统可能使用swap的概率越高,swappiness值越低,表示操作系统更加倾向于使用物理内存。swappiness的取值越大,说明操作系统可能使用swap的概率越高,越低则越倾向于使用物理内存。如果linux内核版本小于3.5 那么wappiness设置为0,这样系统宁愿swap不会oom killer(杀掉进程)
如果linux内核版本>=3.5,那么swappiness设置为1,这样系统宁愿swap也不会oom killer一般需要保证redis不会被kill掉

cat /proc/version # 查看linux内核版本
echo 1>/proc/sys/vm/swappiness
echo vm.swappiness =1>>/etc/sysctl.cnf

PS:OOM killer机制是指Linux操作系统发现可用内存不足时,强制杀死一些用户进程(非内核进程),来保证系统有足够的可用内存进行分配

vm.overcommit_memory(默认0)

  • 0:表示内核将检查是否有足够的可用物理内存(实际不一定用满)供应用进程使用;如果有足够的可用物理内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程
  • 1:表示内核允许分配所有的物理内存,而不管当前的内存状态如何,如果是0的化,可能导致类似fork等操作执行失败,申请不倒足够的内存空间 Redis建议把这个值设置为1,就是为了让fork操作能够在低内存下也能执行成功
cat /proc/sys/vm/overcommit_memory
echo "vm.overcoimmit_memory=1">>/etc/sysctl.conf
sysctl vm.overcommit_memory =1

合理设置文件句柄数。

操作系统进程试图打开一个文件(或者叫句柄),但是现在进程打开的句柄数已经达到了上限,继续打开会报错"Too many open files"

ulimit -a # 查看系统文件句柄数,看open files选项
ulimt -n 65535 # 设置系统文件句柄数

慢查询日志:slowlog

Redis满日志命令说明

  • config get slow* #查看有关满日志的配置信息
  • config set slowlog-log-slower-than 20000 #设置满日志使用时间阈值,此处为20毫秒,即超过20毫秒的操作都会记录下来,生产环境建议设置1000,也就是1ms,这样理论上redis并发至少达到1000,如果要求 单机并发达到1万以上,这个值可能设置为100
  • config set slowlog-max-len 1024 # 设置慢日志记录保存数量,如果保存数量已满,会删除最早的记录,最新的日志追加进来,记录慢查询日志时Redis会对长命令做截断操作,并不会占用大量内存,建议设置稍大些,防止丢失日志
  • config rewrite #将服务器当前所使用的配置保存到redis.conf
  • slowlog len #获取慢查询日志列表的当前长度
  • slowlog get 5 # 获取最新的5条慢查询日志,慢查询日志由四个属性组成:标识ID,发生时间戳,命令耗时,执行命令和参数
  • slowlog reset #重置慢查询

本文转载自: https://blog.csdn.net/Cover_sky/article/details/136725013
版权归原作者 coffee_babe 所有, 如有侵权,请联系我们删除。

“Redis开发规范与性能优化(二)”的评论:

还没有评论