其实同ElasticSearch的延迟删除【在段合并的时候才真正删除数据】【ElasticSearch从入门到放弃系列 九】Elasticsearch原理机制探索一样,Redis也不是马上删除数据,而是先进行标记,让其在内存中再多呆一会儿,等到满足一定条件的时候再进行统一删除。
什么是过期数据
当我们执行del删除redis数据以及expire过期的 ,过期数据:Redis是一种内存级数据库,所有数据均存放在内存中,内存中的数据可以通过TTL指令获取其状态:
127.0.0.1:6379> clear 127.0.0.1:6379> set name tml OK 127.0.0.1:6379> set age 23 ex 30 OK 127.0.0.1:6379> set color red OK 127.0.0.1:6379> del color (integer) 1 127.0.0.1:6379> ttl name -1:永久有效的数据 (integer) -1 127.0.0.1:6379> ttl age XX:具有时效性的数据,返回剩余时间 (integer) 5 127.0.0.1:6379> ttl color -2:已经过期的数据、被删除的数据、未定义的数据 (integer) -2 127.0.0.1:6379> 127.0.0.1:6379> ttl age (integer) -2 127.0.0.1:6379> set second (error) ERR wrong number of arguments for 'set' command 127.0.0.1:6379> ttl second (integer) -2 127.0.0.1:6379>
所以过期数据就是ttl返回为-2的三种状态的数据,要删除的数据。为了防止CPU压力过大,采取不同的删除策略。
过期数据的删除策略
和ElasticSearch的定期段合并策略相同,Redis处理过期数据也有一套。时效性数据的内存结构如下,有个espires的hash结构来存储数据的内存地址和时间,到时删除
删除策略的目标是在内存占用和CPU性能之间寻找一种平衡,既不能让过期数据过多,又不能让CPU太忙。目前有三种处理策略:
定时删除
创建一个定时器,当key设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作。
- 优点:节约内存,到时就删除,快速释放掉不必要的内存占用
- 缺点:CPU压力很大,无论CPU此时负载量多高,均占用CPU,会影响redis服务器响应时间和指令吞吐量
总而言之一句话:牺牲时间换取空间
惰性删除
定期删除可能会导致很多过期key到了时间并没有被删除掉。所以就有了惰性删除。过期key,依然停留在内存里,除非访问一下过期key,才会被redis给删除掉。这就是所谓的惰性删除。expireIfNeeded(),检查数据是否过期,执行get的时候调用
- 优点:节约CPU性能,发现必须删除的时候才删除
- 缺点:内存压力很大,出现长期占用内存的数据
总而言之一句话:牺牲空间换时间
定期删除
当然同AOF的几种策略相比,删除策略也不会走极端,于是就有了定期删除的策略:
这几个参数在配置中可以指定:
hz 10
周期性轮询redis库中的时效性数据,采用随机抽取的策略,利用过期数据占比的方式控制删除频度.
- 优点: CPU性能占用设置有峰值,检测频度可自定义设置,内存压力不是很大,长期占用内存的冷数据会被持续清理
总而言之一句话:空间和时间具有平衡性
删除策略对比
三种删除策略及对比如下:
删除策略 | 特点 | 执行特点 | 总结 |
定时删除 | 节约内存,消耗CPU | 不分时段执行,内存占用低,CPU损耗高 | 牺牲时间换空间 |
惰性删除 | 内存占用验证,CPU消耗低 | 延迟执行,内存占用高,CPU损耗低 | 牺牲空间换时间 |
定期删除 | 内存定期随机清理 | 每秒花费固定CPU资源维护内存,处理时间久的冷数据 | 定时抽查,重点抽查 |
一般会组合惰性删除和定期删除进行使用。
Redis的逐出算法
当新数据进入redis时,如果内存不足怎么办?Redis使用内存存储数据,在执行每一个命令前,会调用freeMemoryIfNeeded()检测内存是否充足。如果内存不满足新加入数据的最低存储要求,redis要临时删除一些数据为当前指令清理存储空间。清理数据的策略称为逐出算法。
逐出算法
首先需要注意几个参数,这几个参数决定了逐出算法的后续逻辑。
- 最大可使用内存maxmemory:占用物理内存的比例,默认值为0,表示不限制。通常设置在50%以上
- 每次选取待删除数据的个数maxmemory-samples: 选取数据时并不会全库扫描,导致严重的性能损耗,降低读写性能。因此采用随机获取数据的方式作为待检测删除数据
- 删除策略maxmemory-policy :达到最大内存后,对被选出来的数据进行删除的策略
逐出算法有三大类:
检测易失性数据(可能会过期的数据集server.db[i].expires)
检测易失数据有四种算法:
- volatile-lru --> 从已设置过期时间的数据集中挑选最近最少使用的数据淘汰
- volatile-lfu–>从已设置过期时间的数据集中挑选最不经常使用的数据淘汰
- volatile-ttl–>从已设置过期时间的数据集中挑选将要过期的数据淘汰
- volatile-random -->从已设置过期时间的数据集中任意选择数据淘汰
检测全库数据(所有数据集server.db[i].dict)
检测易失数据有三种算法:
- allkeys-lru --> 当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(最常用)
- allkeys-random–>从数据集中任意选择数据淘汰
- allkeys-lfu–>当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的key
因为不需要关心数据是否过期,所以没有volatile-ttl
放弃数据驱逐
no-eviction–>禁止驱逐数据(redis4.0默认策略),也就是说当内存不足以容纳新写入数据时,新写入操作或报错,回引发OOM(Out of memory)
综合以上,我们推荐使用的策略是:maxmemory-policy volatile-lru
无法逐出的情况
逐出数据的过程不是100%能够清理出足够的可使用的内存空间,如果不成功则反复执行。当对所有数据尝试完毕后,如果不能达到内存清理的要求【均无过期数据,且占用内存已满】,将出现错误信息。
(err)OOM command not allowed when used memory > 'maxmemory'
以上所有的信息均可以通过info来进行查看:
127.0.0.1:6379> info # Server redis_version:6.0.8 redis_git_sha1:00000000 redis_git_dirty:0 redis_build_id:4c35dfe260ddf15c redis_mode:standalone os:Linux 3.10.0-1127.el7.x86_64 x86_64 arch_bits:64 multiplexing_api:epoll atomicvar_api:atomic-builtin gcc_version:9.3.1 process_id:44507 run_id:14ab8c5119da4c338a94724975bb50054d3e9ce0 tcp_port:6379 uptime_in_seconds:70012 uptime_in_days:0 hz:10 configured_hz:10 lru_clock:9778284 executable:/root/redis-6.0.8/redis-server config_file:/root/redis-6.0.8/config/redis-6379.conf io_threads_active:0 # Clients connected_clients:2 client_recent_max_input_buffer:2 client_recent_max_output_buffer:0 blocked_clients:0 tracking_clients:0 clients_in_timeout_table:0 # Memory used_memory:946288 used_memory_human:924.11K used_memory_rss:2076672 used_memory_rss_human:1.98M used_memory_peak:946288 used_memory_peak_human:924.11K used_memory_peak_perc:100.14% used_memory_overhead:910474 used_memory_startup:876184 used_memory_dataset:35814 used_memory_dataset_perc:51.09% allocator_allocated:912048 allocator_active:2038784 allocator_resident:2038784 total_system_memory:1019609088 total_system_memory_human:972.38M used_memory_lua:37888 used_memory_lua_human:37.00K used_memory_scripts:0 used_memory_scripts_human:0B number_of_cached_scripts:0 maxmemory:0 maxmemory_human:0B maxmemory_policy:noeviction allocator_frag_ratio:2.24 allocator_frag_bytes:1126736 allocator_rss_ratio:1.00 allocator_rss_bytes:0 rss_overhead_ratio:1.02 rss_overhead_bytes:37888 mem_fragmentation_ratio:2.28 mem_fragmentation_bytes:1164624 mem_not_counted_for_evict:174 mem_replication_backlog:0 mem_clients_slaves:0 mem_clients_normal:33972 mem_aof_buffer:174 mem_allocator:libc active_defrag_running:0 lazyfree_pending_objects:0 # Persistence loading:0 rdb_changes_since_last_save:0 rdb_bgsave_in_progress:0 rdb_last_save_time:1603597171 rdb_last_bgsave_status:ok rdb_last_bgsave_time_sec:0 rdb_current_bgsave_time_sec:-1 rdb_last_cow_size:98304 aof_enabled:1 aof_rewrite_in_progress:0 aof_rewrite_scheduled:0 aof_last_rewrite_time_sec:1 aof_current_rewrite_time_sec:-1 aof_last_bgrewrite_status:ok aof_last_write_status:ok aof_last_cow_size:139264 module_fork_in_progress:0 module_fork_last_cow_size:0 aof_current_size:1339 aof_base_size:105 aof_pending_rewrite:0 aof_buffer_length:0 aof_rewrite_buffer_length:0 aof_pending_bio_fsync:0 aof_delayed_fsync:0 # Stats total_connections_received:7 total_commands_processed:97 instantaneous_ops_per_sec:0 total_net_input_bytes:2632 total_net_output_bytes:131444 instantaneous_input_kbps:0.01 instantaneous_output_kbps:11.36 rejected_connections:0 sync_full:0 sync_partial_ok:0 sync_partial_err:0 expired_keys:2 expired_stale_perc:0.00 expired_time_cap_reached_count:0 expire_cycle_cpu_milliseconds:334 evicted_keys:0 keyspace_hits:12 keyspace_misses:6 pubsub_channels:0 pubsub_patterns:0 latest_fork_usec:165 migrate_cached_sockets:0 slave_expires_tracked_keys:0 active_defrag_hits:0 active_defrag_misses:0 active_defrag_key_hits:0 active_defrag_key_misses:0 tracking_total_keys:0 tracking_total_items:0 tracking_total_prefixes:0 unexpected_error_replies:0 total_reads_processed:117 total_writes_processed:111 io_threaded_reads_processed:0 io_threaded_writes_processed:0 # Replication role:master connected_slaves:0 master_replid:d71bf05a8550a15cb81e049e7baef5451dc8fd3f master_replid2:0000000000000000000000000000000000000000 master_repl_offset:0 second_repl_offset:-1 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0 # CPU used_cpu_sys:17.306819 used_cpu_user:10.844898 used_cpu_sys_children:0.014531 used_cpu_user_children:0.014085 # Modules # Cluster cluster_enabled:0 # Keyspace db0:keys=2,expires=0,avg_ttl=0