redis 占用内存研究

计算机存储单位:

bit 位 1byte=8bit
b byte 一字节
kb 一千字节 1kb=1024byte

mb 一兆字节 1mb=1024kb

登录redis

redis-cli -h 127.0.0.1 -p 6379

auth 11111

使用info 命令可以查看redis 的各个情况。

info命令输出的数据可分为10个类别,分别是:

  • server
  • clients
  • memory
  • persistence
  • stats
  • replication
  • cpu
  • commandstats
  • cluster
  • keyspace

主要介绍比较重要的2部分性能指标memory和stats。

 
   

使用命令 info memory  可以查看redis 的内存使用情况。

返回结果 :(返回值默认单位为字节)

# Memory

used_memory:3225584

used_memory_human:3.08M

used_memory_rss:9515008

used_memory_rss_human:9.07M

used_memory_peak:3440040

used_memory_peak_human:3.28M

total_system_memory:8187613184

total_system_memory_human:7.63G

used_memory_lua:37888

used_memory_lua_human:37.00K

maxmemory:0

maxmemory_human:0B

maxmemory_policy:noeviction

mem_fragmentation_ratio:2.95

mem_allocator:jemalloc-3.6.0

used_memory:13490096 //数据占用了多少内存(字节)

 

used_memory_human:12.87M //数据占用了多少内存(带单位的,可读性好)

 

used_memory_rss:13490096  //redis占用了多少内存

 

used_memory_peak:15301192 //占用内存的峰值(字节)

 

used_memory_peak_human:14.59M //占用内存的峰值(带单位的,可读性好)

 

used_memory_lua:31744  //lua引擎所占用的内存大小(字节)

 

mem_fragmentation_ratio:1.00  //内存碎片率

 

mem_allocator:libc //redis内存分配器版本,在编译时指定的。有libc、jemalloc、tcmalloc这3种。

如何判断自己的项目适合多大内存呢?

第一种 根据官方提示:

### What's the Redis memory footprint? 
To give you a few examples (all obtained using 64-bit instances): 
* An empty instance uses ~ 1MB of memory. 
* 1 Million small Keys -> String Value pairs use ~ 100MB of memory. 
* 1 Million Keys -> Hash value, representing an object with 5 fields, use ~ 200 MB of memory. 

如果key是短字符串,value也是字符串,按照 100 字节一条计算就可以了。
如果key是字符串, value 是包含5个字段的值,按照200字节一条计算就可以了。

第二种 查看一个key 的value 的情况 :

debug object 1010tdrule_detail

返回值

Value at:0x7fb6abe969d0 refcount:1 encoding:raw serializedlength:290 lru:11479144 lru_seconds_idle:283689

然后 预估一共会有多少个key 。

使用 keys 111110* 可以查看以字符串111110开头的所所有key,可以知道一共有多少个。

如何查看服务器上占用内存最大key:


导出查看服务器的所有keys :

echo  "KEYS *\n" | redis-cli -h 192.168.80.206 -p 6379 -a 111111 > keys.redis


计算每个key占用的内存:

for key in `cat keys.redis` ; do info=`echo "debug object $key" | redis-cli -h 192.168.80.206 -p 6379 -a 111111 | grep -o "serializedlength\:[0-9]*" | cut -d ":" -f2 | tr -d '\n'`; echo $info": "$key  >> keysize.redis; done;



对生成结果进行排序,查看内存使用大的key(这边列出前面100个):
cat keysize.redis | sort -nr -k 1 | head -n 100



因内存交换引起的性能问题

内存使用率是Redis服务最关键的一部分。如果一个Redis实例的内存使用率超过可用最大内存 (used_memory > 可用最大内存),那么操作系统开始进行内存与swap空间交换,把内存中旧的或不再使用的内容写入硬盘上(硬盘上的这块空间叫Swap分区),以便腾出新的物理内存给新页或活动页(page)使用。 
在硬盘上进行读写操作要比在内存上进行读写操作,时间上慢了近5个数量级,内存是0.1μs单位、而硬盘是10ms。如果Redis进程上发生内存交换,那么Redis和依赖Redis上数据的应用会受到严重的性能影响。 通过查看used_memory指标可知道Redis正在使用的内存情况,如果used_memory>可用最大内存,那就说明Redis实例正在进行内存交换或者已经内存交换完毕。管理员根据这个情况,执行相对应的应急措施。

跟踪内存使用率

若是在使用Redis期间没有开启rdb快照或aof持久化策略,那么缓存数据在Redis崩溃时就有丢失的危险。因为当Redis内存使用率超过可用内存的95%时,部分数据开始在内存与swap空间来回交换,这时就可能有丢失数据的危险。
当开启并触发快照功能时,Redis会fork一个子进程把当前内存中的数据完全复制一份写入到硬盘上。因此若是当前使用内存超过可用内存的45%时触发快照功能,那么此时进行的内存交换会变的非常危险(可能会丢失数据)。 倘若在这个时候实例上有大量频繁的更新操作,问题会变得更加严重。

通过减少Redis的内存占用率,来避免这样的问题,或者使用下面的技巧来避免内存交换发生:

  1. 假如缓存数据小于4GB,就使用32位的Redis实例。因为32位实例上的指针大小只有64位的一半,它的内存空间占用空间会更少些。 这有一个坏处就是,假设物理内存超过4GB,那么32位实例能使用的内存仍然会被限制在4GB以下。 要是实例同时也共享给其他一些应用使用的话,那可能需要更高效的64位Redis实例,这种情况下切换到32位是不可取的。 不管使用哪种方式,Redis的dump文件在32位和64位之间是互相兼容的, 因此倘若有减少占用内存空间的需求,可以尝试先使用32位,后面再切换到64位上。

  2. 尽可能的使用Hash数据结构。因为Redis在储存小于100个字段的Hash结构上,其存储效率是非常高的。所以在不需要集合(set)操作或list的push/pop操作的时候,尽可能的使用Hash结构。比如,在一个web应用程序中,需要存储一个对象表示用户信息,使用单个key表示一个用户,其每个属性存储在Hash的字段里,这样要比给每个属性单独设置一个key-value要高效的多。 通常情况下倘若有数据使用string结构,用多个key存储时,那么应该转换成单key多字段的Hash结构。 如上述例子中介绍的Hash结构应包含,单个对象的属性或者单个用户各种各样的资料。Hash结构的操作命令是HSET(key, fields, value)和HGET(key, field),使用它可以存储或从Hash中取出指定的字段。

  3. 设置key的过期时间。一个减少内存使用率的简单方法就是,每当存储对象时确保设置key的过期时间。倘若key在明确的时间周期内使用或者旧key不大可能被使用时,就可以用Redis过期时间命令(expire,expireat, pexpire, pexpireat)去设置过期时间,这样Redis会在key过期时自动删除key。 假如你知道每秒钟有多少个新key-value被创建,那可以调整key的存活时间,并指定阀值去限制Redis使用的最大内存。

  4. 回收key。在Redis配置文件中(一般叫Redis.conf),通过设置“maxmemory”属性的值可以限制Redis最大使用的内存,修改后重启实例生效。 也可以使用客户端命令config set maxmemory 去修改值,这个命令是立即生效的,但会在重启后会失效,需要使用config rewrite命令去刷新配置文件。 若是启用了Redis快照功能,应该设置“maxmemory”值为系统可使用内存的45%,因为快照时需要一倍的内存来复制整个数据集,也就是说如果当前已使用45%,在快照期间会变成95%(45%+45%+5%),其中5%是预留给其他的开销。 如果没开启快照功能,maxmemory最高能设置为系统可用内存的95%。

当内存使用达到设置的最大阀值时,需要选择一种key的回收策略,可在Redis.conf配置文件中修改“maxmemory-policy”属性值。 若是Redis数据集中的key都设置了过期时间,那么“volatile-ttl”策略是比较好的选择。但如果key在达到最大内存限制时没能够迅速过期,或者根本没有设置过期时间。那么设置为“allkeys-lru”值比较合适,它允许Redis从整个数据集中挑选最近最少使用的key进行删除(LRU淘汰算法)。Redis还提供了一些其他淘汰策略,如下:

  • volatile-lru:使用LRU算法从已设置过期时间的数据集合中淘汰数据。
  • volatile-ttl:从已设置过期时间的数据集合中挑选即将过期的数据淘汰。
  • volatile-random:从已设置过期时间的数据集合中随机挑选数据淘汰。
  • allkeys-lru:使用LRU算法从所有数据集合中淘汰数据。
  • allkeys-random:从数据集合中任意选择数据淘汰
  • no-enviction:禁止淘汰数据。

通过设置maxmemory为系统可用内存的45%或95%(取决于持久化策略)和设置“maxmemory-policy”为“volatile-ttl”或“allkeys-lru”(取决于过期设置),可以比较准确的限制Redis最大内存使用率,在绝大多数场景下使用这2种方式可确保Redis不会进行内存交换。倘若你担心由于限制了内存使用率导致丢失数据的话,可以设置noneviction值禁止淘汰数据。




你可能感兴趣的:(缓存)