Redis是完全开源的,极高性能的NoSql的数据库;读的速度能达到次/s,写的速度能达到次/s。
数据类型
为什么是单线程的
Redis是基于内存的操作,CPU不是Redis的瓶颈
省去了很多上下文切换线程的时间,不用去考虑各种锁的问题
多路I/O复用:使用了单线程来轮询描述符,减少了线程切换时上下文的切换和竞争
能带来更好的可维护性,方便开发和调试
数据淘汰策略
volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集中任意选择数据淘汰
allkeys-lru:从所有数据集中挑选最近最少使用的数据淘汰
allkeys-random:从所有数据集中任意选择数据进行淘汰
noeviction:禁止驱逐数据需要先设置最大内存maxmemory,然后如果内存不足,会触发我们选择的过期淘汰策略
持久化
1.RDB
按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb;如果系统发生故障,将会丢失最后一次创建快照之后的数据。
默认开启
#savesecondschangessavesavesave
保存流程(BGSAVE)
redis-rdb-flow.png
需要注意的是:
RDB写入,每次都是全量,在数据量特别大时,服务器负载会比较高
RDB会在服务器宕机时,丢失几分钟的数据,主要是根据save策略来的
2.AOF
如何开启
appendonlyyesappendfsynceverysecauto-aof-rewrite-percentageauto-aof-rewrite-min-size64mb
重写流程
redis-aof-flow.png
需要注意的是:
重写是直接把当前内存的数据生成对应命令,不需要分析老的AOF文件;
恢复数据时,会先判断有没有AOF,没有的话,在加载RDB,因为AOF文件相对完整;
3.雪崩、穿透、击穿
雪崩
现象:缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩(由于原有缓存失效,新缓存未到期间);
解决方案:考虑用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上;还有一个解决方案,原有的失效时间基础上增加一个随机值;
穿透
现象:查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义;
解决方案:如果一个查询返回的数据为空,我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟;
击穿(热点Key)
现象:缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮;
解决方案:对缓存查询加锁,如果KEY不存在,就加锁,然后查DB入缓存,然后解锁;其他进程如果发现有锁就等待,然后等解锁后返回数据或者进入DB查询;
4.分布式锁
publicbooleanlock(intdbIndex,Stringkey,StringrequestId,intcacheSeconds){longbeginTime=System.currentTimeMillis();booleanisBroken=false;Jedisjedis=null;booleanresult;try{jedis=getJedis();jedis.select(dbIndex);//NX:SETIFNOTEXIST,没有SET,有的话不操作//PX:EXPX,过期时间设置Stringvalue=jedis.set(key,requestId,"NX","PX",cacheSeconds);result=LOCK_SUCCESS.equals(value);}catch(Exceptione){isBroken=true;thrownewJedisException(e);}finally{release(jedis,isBroken);LoggerBuilder.writeTimeLog("jedis-lock",beginTime);}returnresult;}publicbooleanreleaseLock(intdbIndex,StringlockKey,StringrequestId){longbeginTime=System.currentTimeMillis();booleanisBroken=false;Jedisjedis=null;booleanresult;try{jedis=getJedis();jedis.select(dbIndex);Stringscript="ifredis.call(get,KEYS[1])==ARGV[1]thenreturnredis.call(del,KEYS[1])elsereturn0end";Objectvalue=jedis.eval(script,Collections.singletonList(lockKey),Collections.singletonList(requestId));result=RELEASE_SUCCESS.equals(value);}catch(Exceptione){isBroken=true;thrownewJedisException(e);}finally{release(jedis,isBroken);LoggerBuilder.writeTimeLog("jedis-releaseLock",beginTime);}returnresult;}
5.Redis常见性能问题和解决方案
Master最好不要做任何持久化工作;
某个Slave开启AOF备份数据,策略设置为每秒同步一次;
几个可能导致Redis阻塞的原因1.API或数据结构使用不合理,发现慢查询:slowlogget ,发现大对象:redis-cli-h{ip}-p{port}bigkeys2.CPU饱和,redis-cli-h{ip}-p{port}–stat3.持久化阻塞,fork阻塞,使用infostats命令获取lastest_fork_usec指标,表示redis最近一次fork操作耗时;统计中的aof_delayed_fsync指标4.内存交换,cat/proc/{process_id}/smaps
grepSwap,如果交换量都是0KB或者个别4KB,是正常现象5.网络
提高缓存命中率1.keyspace_hits/(keyspace_hits+keyspace_misses)=命中率2.缓存时间越长,命中率会越高。时效性要求越低,就越适合缓存3.缓存的容量有限,则容易引起缓存失效和被淘汰4.缓存的粒度越小,命中率会越高
6.部署方式
6.1主从
主从复制原理:
从服务器连接主服务器,发送SYNC命令;
主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;
主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;
从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;
主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令;
从服务器完成对快照的载入,开始接受命令请求,并执行来自主服务器缓冲区的写命令;(从服务器初始化完成)
主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令(从服务器初始化完成后的操作)
一般主从配置可以缓解请求压力,做读写分离,写服务器不开启持久化,从服务器开启,从服务器还负责读取的操作,而且从服务器可以是多个,可以有效缓解主服务器的压力;但是坏处在于,如果主服务器宕机,无法自动切换恢复;
6.2哨兵
主要作用
监控主服务器和从服务器是否正常运行
主服务器出现故障时自动将从服务器转换为主服务器
也具备了主从的全部好处
工作方式
每个Sentinel(哨兵)进程以每秒钟一次的频率向整个集群中的Master主服务器,Slave从服务器以及其他Sentinel(哨兵)进程发送一个PING命令。
如果一个实例(instance)距离最后一次有效回复PING命令的时间超过down-after-milliseconds选项所指定的值,则这个实例会被Sentinel(哨兵)进程标记为主观下线(SDOWN)
如果一个Master主服务器被标记为主观下线(SDOWN),则正在监视这个Master主服务器的所有Sentinel(哨兵)进程要以每秒一次的频率确认Master主服务器的确进入了主观下线状态
当有足够数量的Sentinel(哨兵)进程(大于等于配置文件指定的值)在指定的时间范围内确认Master主服务器进入了主观下线状态(SDOWN),则Master主服务器会被标记为客观下线(ODOWN)
在一般情况下,每个Sentinel(哨兵)进程会以每10秒一次的频率向集群中的所有Master主服务器、Slave从服务器发送INFO命令。
当Master主服务器被Sentinel(哨兵)进程标记为客观下线(ODOWN)时,Sentinel(哨兵)进程向下线的Master主服务器的所有Slave从服务器发送INFO命令的频率会从10秒一次改为每秒一次。
若没有足够数量的Sentinel(哨兵)进程同意Master主服务器下线,Master主服务器的客观下线状态就会被移除。若Master主服务器重新向Sentinel(哨兵)进程发送PING命令返回有效回复,Master主服务器的主观下线状态就会被移除。
哨兵其实是对主从配置的补充,可以自动选举出新的主,提供服务,但是,如果单机redis数据量过大,这个部署就无法处理了,需要cluster模式进行分片
6.3集群
多个redis节点网络互联,数据共享
所有的节点都是一主一从(也可以是一主多从),其中从不提供服务,仅作为备用
不支持同时处理多个key(如MSET/MGET)
支持在线增加、删除节点
Redis实战(异步图书出品)启画堂Redis入门京东¥购买已下架