redis
和数据相比除了他们的结构型颠覆以外!还有他们存储位置也是不相同。传统数据库将数据存储在硬盘上每次数据操作都需要IO
而Redis
是将数据存储在内存上的。这里稍微解释下IO是啥意思。IO就是输入流输出流方式将数据在硬盘和内存之间进行交互!而redis
直接在内存上就剩下了IO
操作。这也是redis
快的原因之一吧
redis
又将数据存储在内存上那么redis
肯定不能肆无忌惮的进行存储 。这就需要redis
和开发者们作出相应的优化redis
在配置文件(redis.conf
)中通过maxmemory
参数指定redis
设置整个对内存的支配大小!redis
中填值的时候根据自己的需求设置相应的key过期时间。这样不必要的数据就会被redis
过期驱逐策略清除。从而节省内存供别人使用本文凌驾于redis
基础之上,这里笔者默认大家都已经安装了redis
. 并实际使用过redis
maxmemory
指定大小。在redis.conf
配置文件中可以直接指定。他的单位时byte。redis
通过config
命令来设置redis.conf
中的配置。redis
本身角度出发设置了内存限制,这样不用担心他们吞噬系统内存!下面就需要我们开发者设计角度约束自己了。expire key time
ttl
能够观察到剩余时间在不断的减少!当减少到0的时候就被给驱逐策略驱逐!注意这里说的是驱逐策略驱逐并不是意味着立马被删除del key
直接将key删除了那么该key对应的过期自然也就不存在了!这种情况笔者也算作是更新过期时间set
getset
等命令重新设置key、value方式会覆盖过期时间 , 直接被覆盖成-1set
、 getset
包括del
严格意义是覆盖过期时间。真正做到更新过期时间的还是expire .在expire是以最新为准的!append
、incr
等命令是改变值这种命令是不会影响到原来的过期设置的redis
最大内存设置为1MB
, 设置大小随便最好能小点。然后我们通过Java
小程序不断向redis
中填充。最终当内存不够使用时就会报错redis
拒绝了!不仅仅是新增的被拒绝,就算此时我们想改变已经在redis
中的key的值也是不可用的public static void main(String[] args) {
Jedis jedis = new Jedis("39.102.60.114", 6379);
jedis.auth("Qq025025");
Integer index = 1;
while (true) {
String uuid = UUID.randomUUID().toString();
jedis.set(index.toString(), uuid);
System.out.println(index++);
}
}
OOM command not allowed when used memory > maxmemory
。代码中打印和redis
键个数一致;说明我们默认的淘汰策略是直接拒绝redis
内存被使用满了后,任何的写操作都会被拒绝!当内存告警时
redis
会将近期很少使用且设置了过期时间的key剔除出去,即使该key还没有到过期时间。如果没有符合的key也就是执行之后内存仍然不足时将会和默认淘汰策略noeviction
抛出一样的错误OOM command not allowed when used memory > maxmemory
redis.conf
中配置我们最近很少使用策略. maxmemory-policy volatile-lru
。 然后重启我们的redis
服务 。重启之后flushall
清空所有数据,我们在通过上面的Java
程序重新生成下数据!public static void main(String[] args) {
Jedis jedis = new Jedis("39.102.60.114", 6379);
jedis.auth("Qq025025");
Integer index = 1;
while (true) {
String uuid = UUID.randomUUID().toString();
if (index < 100) {
jedis.setex(index.toString(),360, uuid);
} else {
jedis.set(index.toString(), uuid);
}
System.out.println(index++);
}
}
redis
库中的key数量!就是因为我们为前100设置了过期时间。当内存不足时redis
就会将当前设置了过期时间的key中最近最少使用的key进行剔除!所以我们计数器会大于键数量。因为有部分键被清除了!我们获取前100的key都是null , 说明被删除了! 那么为什么本次计数器不是比上次多100 。 那是因为我们每次存储进来的是uuid, 所占长度都不是固定的。还有本身淘汰策略也是占用内存的noeviction:拒绝写请求,正常提供读请求,这样可以保证已有数据不会丢失(默认策略);
2\. volatile-lru:尝试淘汰设置了过期时间的key,虽少使用的key被淘汰,没有设置过期时间的key不会淘汰;
3\. volatile-ttl:跟volatile-lru几乎一样,但是他是使用的key的ttl值进行比较,最先淘汰ttl最小的key;
4\. volatile-random:其他同上,唯一就是他通过很随意的方式随机选择淘汰key集合中的key;
5\. allkeys-lru:区别于volatile-lru的地方就是淘汰目标是全部key,没设置过期时间的key也不能幸免;
6\. allkeys-random:这种方式同上,随机的淘汰所有的key。
redis
。 仅仅这两方面还没有完全高效使用内存!淘汰策略是濒临内存不足时触发。那么当设置了过期时间的键真正到了过期时间而此时内存尚够使用?这种场景是不是需要将过期键删除呢?redis
是单线程,那么在键过期的时候如何不影响对外服务的同时清除过期键呢?答案是【不行】。严格意义是无法解决的因为单线程同时只能做一件事!既然无法解决那么我们可以达到一种协调状态!如果同一时刻出现一个过期键那么清除键很快这时候阻塞外部服务的时间很短可能毫秒级设置纳秒级!redis
如何应对同一时间过多数据过期的场景,他的删除过期键的方法略有不同!key
需要设置一个定时器进行跟踪。redis
这里笔者猜测应该是启用另外线程来进行定时跟踪!这里有清除的还请帮忙解答下?key
很多的时候!我们的CPU就需要不断的执行这些定时器从而导致CPU资源紧张。最终会影响到redis
服务的性能redis
会每隔100ms
执行一次定时器,定时器的任务就是随机抽取20个设置过期的key
。 判断是否进行清除。上面图示中说明中写错了不是10S , 而是每隔100ms
。请原谅我的粗心!!!25ms
。 也就是说对于客户端来说服务端的延迟不会超过25ms
。redis
并不急着去清除这些数据,而是等到该key被再次请求时进行删除!这样在最终效果上是没有问题的。redis
中。redis
内部是使用惰性清除和定期清除两种方式结合使用,最终保证CPU和内存之间的一种平衡!redis
给我们开发者提供的功能。我们可以根据自己的业务需求合理的设置键的过期时间,从而保证内存的高可用redis.conf
中我们可以设置 hz 10
代表1S
中平均执行几次这也是我们上面所说的100MS
的由来。但是仅仅定期删除会产生遗漏数据所以我们还需要加上惰性清除,最终保证对客户端来说数据是准确实时清除的。redis
服务中还会堆积很多过期的无效key 。这个时候如果内存不够用了的话那又该怎么办呢?这时候我们需要设置淘汰策略比如果volatile-lru
. 就会将最近最少使用的设置过期key进行清除从而保证尽可能的接收更多的有效数据!