redis08

redis的规范与运维

 

Key设计

1.可读性和可管理性

- 以业务名(或数据库名)为前缀(防止key冲突),用冒号分割分割,

例如 业务名:表名:id,如 ugc:video:1

 

数据对象名:数据对象id:对象属性

User:123:passpword

User:123:username

Sku:108:info

 

2.简洁性

保证语义的情况下,控制key的长度,当key较多时,内存占用也不容忽视
例如 user:{uid}:friends:messages:{mid} 可以简化为 u:{uid}:fri:mes:{mid}

3.不要包含特殊字符

反例:包含空格、换行、单双引号以及其他转义字符

 

 

Value设计

 

1.避免bigkey

string类型控制在10KB以内
hash、list、set、zset元素个数不要超过5000个
反例:一个包含几百万个元素的list、hash等,一个巨大的json字符串

 

2.bigkey的危害

网络阻塞
Redis阻塞
会形成慢查询阻塞其他命令
集群节点数据不均衡
频繁序列化:应用服务器CPU消耗
Redis客户端本身不负责序列化
应用频繁序列化和反序列化bigkey:本地缓存或Redis缓存

 

发现bigkey的方法

使用应用的监控异常对其发现
JedisConnectionException:Read timed out
Could not get a resource from the pool
在从节点上执行 redis-cli --bigkeys
scan + debug object key
主动报警:网络流量监控、客户端监控
内核热点key问题优化

 

bigkey的删除

直接通过del删除bigKey会非常慢,对redis发生阻塞
小心bigkey的隐性删除(不会存在在主节点的慢查询中,但是会发生在从节点中)
过期
rename等
可以使用redis4.0的lazy delete (unlink命令)
redis会将unlink后的数据进行后台删除,不会阻塞前台的命令线程
bigkey的预防
优化数据结构:例如二级拆分,例如按天/按小时存入
存在bigkey的节点进行物理隔离或者万兆网卡:不是治本方案
命令优化:例如hgetall -> hmget 、 hscan
报警和定期优化

 

 

选择合理的数据结构

 

例如存储实体类型时候

反例
set user:1:name tom;
set user:1:age 19;
set user:1:favor football;


正例
hmset user:1 name tom age 19 favor football;


缘由
避免出现数据松散
小hash会采用ziplist,得到节省内存的效果

 

需求 100万数据的(picId -> userId)

方案1:全string:set picId userId

方案2:一个hash:hset allPics picId userId

方案3:若干个小hash:hset picId/100 picId%100 userId

三个方案的比较


方案1 全string 编码简单 , 浪费内存全量获取比较复杂
方案2 全hash , 浪费内存bigKey |
方案3 分段hash 节省内存 编程复杂,hash无法设置ttl,超时问题,性能问题-ziplist

 

键值生命周期的管理

周期数据需要设置过期时间,object idletime可以找垃圾的key-value
过期时间不宜集中,容易出现缓存穿透和雪崩等问题

 

 

命令优化技巧

1.O(n)以上命令需要关注n的数量

例如:hgetall、lrange、smembers、zrange、sinter等并非不能使用,但是需要明确n的值。有遍历的需求可以使用hscan、sscan、zscan。

2.禁用命令

禁止线上使用keys、flushall、flushdb等,通过redis的config rename机制禁调命令,或者使用scan的方式渐进式处理。

3.合理使用select

redis的多数据库较弱,使用数字进行区分。
很多客户端支持较差,不清楚当前使用的是哪个库
同时多业务用多数据库实际还是单线程处理,会有干扰。

4.Redis事务功能较弱,不建议过多使用

Redis的事务功能较弱(不支持回滚)
在集群版本中要求一次事务操作的key必须在一个slot上(可以使用hashtag功能解决)

5.Redis集群版本在使用Lua上有特殊要求

所有的key,必须在一个slot之上,否则直接返回ERROR

6.必要情况下使用monitor命令时,要注意不要长时间使用

​任何客户端中,发起任何命令都会发生送monitor客户端。但是在大QPS环境下对性能造成影响

 

Java客户端优化

 

1.避免多个应用使用一个Redis实例

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

2.使用连接池,标准使用方式

 1 Jedis jedis = null;
 2 try {
 3     jedis = jedisPool.getResource();
 4     jedis.executeCommand();
 5 }catch(Exception e) {
 6     logger.error("op key {} error : {} " , key , e.getMessage() , e);
 7 }finally{
 8     if( null != jedis ) {
 9         jedis.close();
10     }
11 }

 

 

Java客户端优化

1.避免多个应用使用一个Redis实例

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

2.使用连接池,标准使用方式

 

 1 Jedis jedis = null;
 2 try {
 3     jedis = jedisPool.getResource();
 4     jedis.executeCommand();
 5 }catch(Exception e) {
 6     logger.error("op key {} error : {} " , key , e.getMessage() , e);
 7 }finally{
 8     if( null != jedis ) {
 9         jedis.close();
10     }
11 }

 

连接池参数优化

 

1.maxTotal:8

资源池中的最大连接数


2.maxIdle:8

资源池允许最大空闲的连接数


3.minIdle:0

资源池确保最少空闲的连接数


4.blockWhenExhausted:true

当资源池用尽时,调用者是否要等待,只有当设为true时,下面的maxWaitMillis才会生效
使用建议:建议使用默认值

maxWaitMillis:-1(永不超时)

当资源池连接用尽时,调用者的最大等待时间(单位:毫秒)
使用建议:不建议使用默认值

6.testOnBorrow:false

向资源借用连接时是否是否做连接有效性检测(ping),无效连接会被移除
使用建议:业务量很大的时候建议设置为false(多一次ping的开销)

7.testOnReturn:false

向资源池归还连接时是否做连接有效性检测(ping),无效连接会被移除
使用建议:业务量很大的时候建议设置为false(多一次ping的开销)

8.jmxEnabled:true

是否开启jmx监控,可用于监控
使用建议:建议开启,但应用本身也要开启

9.testWileIdle:false

是否开启空闲资源检测
使用建议:true

10.timeBetweenEvictionRunsMIllis:-1(不检测)

空闲资源的检测周期(单位为毫秒)
使用建议:建议设置,周期自行选择,可以默认也可以使用接下来JedisPoolConfig的配置

11.minEvictableIdleTimeMillis:1000\*60\*30=30分钟

资源池中资源最小空闲时间(单位为毫秒)达到此值后空闲资源将被移除
使用建议:可根据自身业务决定,大部分默认值即可,也可以考虑使用如下JedisPoolConfig中的配置

12.numTestsPerEvictionRun:3

做空闲资源监测时,每次的采样数
使用建议:可根据自身应用连接数进行微调,如果设置为-1,就是对所有连接座空闲检测

13.使用建议

maxTotal的配置方式

maxIdle接近maxTotal即可

考虑因素

业务希望Redis并发量,并发量越大maxTotal越大
客户端执行命令时间,命令时间越小maxTotal越小
Redis资源:例如 应用个数 * maxTotal 不能超过Redis最大连接数
资源开销:虽然希望控制空闲连接,但是不希望因为连接池的频繁释放创建连接造成不必的开销

案例计算

一次命令时间(borrow|return resource + Jedis执行命令(含网络时间))的平均耗时是1ms,一个连接的QPS大约是1000。如果业务期望的QPS是50000。那么理论的maxTotal=50,可适当伸缩。

 

 

Linux优化

 

1.vm.overcommit_memory

1 ##配置位置
2 vi /etc/sysctl
3 ##Redis推荐将该值设置为1,避免fork的时候,因为内存不够而失败
4 echo "vm.overcommit_memory=1" >> /etc/sysctl.conf
5 sysctl vm.overcommit_memory=1

 

最佳实践

Redis设置合理的maxmemory,保证机器有20%~30%的闲置内存
集中化管理AOF重写和RDB的bgsave。
避免一台机器上有多个master
设置vm.overcommit_memory=1.防止极端情况下fork失败

 

 2.swappiness

###立即生效
echo ${bestValue} > /proc/sys/vm/swappiness
###永久生效
echo vm.swappiness=${bestValue} >> /etc/sysctl.conf


最佳实践

如果Linux >= 3.5 , vm.swappiness = 1 , 否则vm.swappiness = 0
物理内存充足时,使Redis足够快
物理内存不足时,避免Redis死掉(如果当前Redis为高可用,死掉比阻塞更好)

 

 

3.THP(Transparent huge page)

作用:加速fork
建议:将其禁用,因为可能会产生更大(512倍)的内存开销

echo never > /sys/kernel/mm/transparent_huagepage/enabled

 

4.OOM killer

作用:内存使用超出,操作系统将按照规则kill掉某些进程
配置方法:/proc/${process_id}/oom_adj越小,被杀掉的概率越小
运维经验:
不要过度依赖此特性,应该合理管理内存
通常在高可用的情况下,被杀掉比僵死更好,因此不要过多依赖oom_adj配置。

 

5.NTP(Net Time Protocol)

避免有多台机器,每台机器的时间都不一致,那么查日志找问题的时候会产生很多困扰。
建议实现本地的NTP服务或配置统一的NTP服务
Redis Cluster或Redis Sentinel依赖选举的时间为本地时间,如果有不一致不会出错

6.ulimit

redis默认的maxclient=10000,则redis至少需要10032个文件句柄。
但是Linux默认只有4096个文件句柄,即Redis默认实际的maxclient=4064
可以通过ulimit -n 提升Linux文件句柄数

7.TCP backlog

Redis默认的tcp-backlog值为511,可以修改配置tcp-backlog进行调整,如果Linux的tcp-backlog小于Redis设置的tcp-backlog,那么在Redis启动时会提示Redis的tcp-backlog被迫降至Linux的tcp-backlog

 

 

安全的Redis

一、全球crackit攻击

被攻击的机器特征
Redis所在的机器有外网IP
Redis以默认端口6379为启动端口,并且对外网开放
Redis是以root用户启动的
Redis没有设置密码
Redis的网卡bind为0.0.0.0或者没有绑定

 

攻击步骤


访问Redis
flushall
set crackit id_rsa.pub
config set dir
config set dbfilename
save
ssh root

 

 

二、安全七则

 

1.设置密码
服务器配置:requirepass和masterauth
客户端连接:auth命令和-a参数
注意事项
密码要足够复杂,防止暴力破解
masterauth不要忘记
auth还是通过明文传输,可以通过抓包破解

 

2.伪装安全命令

服务端配置:rename-command为空或随机字符
客户端连接:不可用或指定随机字符
注意事项
不支持config set动态设置
RDB和AOF如果包含rename-command之前的命令,将无法使用
config命令本身是在Redis内核会使用到,不建议设置

 

3.网卡绑定bind

服务端配置:bind限制的是网卡,并不是客户端IP
注意事项:
bind不支持config set动态配置
bind 127.0.0.1需要谨慎
如果存在外网网卡

 

4.开启Linux防火墙

5.定期备份数据

6.不使用默认端口,防止被弱攻击杀掉

7.使用非root用户启动

 

热点Key的发现

 

1.客户端

2.代理端

3.服务端

4.机器收集

 

方案对比

 

 

 

 

 

posted @ 2019-08-19 09:48  曲阳阳  阅读(210)  评论(0编辑  收藏  举报