Redis4.0新特性(一)-Memory Command

Redis4.0版本增加了很多诱人的新特性,在redis精细化运营管理中都非常有用(猜想和antirez加入redislabs有很大关系);此系列几篇水文主要介绍以下几个新特性的使用和效果。

  • Redis Memeory Command:详细分析内存使用情况,内存使用诊断,内存碎片回收;
  • PSYNC2:解决failover和从实例重启不能部分同步;PSYNC3已经路上了;
  • LazyFree: 再也不用怕big key的删除引起集群故障切换;
  • LFU: 支持近似的LFU内存淘汰算法;
  • Active Memory Defragmentation:内存碎片回收效果很好(实验阶段);
  • Modules: Redis成为更多的可能(觉得像mongo/mysql引入engine的阶段);
  • 因暂未有官方的详细文档,加之业余时间有限; 还请各位看官请轻拍。:)

那本文先介绍第一个特性memory指令。


Memory Command简介

redis4.0引入新的命令memory, memory命令共有5个子命令;
让我们能更深入要了解redis内部的内存使用情况。
通过memory help命令,可以查看除memory doctor的其他4个子命令;
5个指令简介如下:

  • MEMORY USAGE [SAMPLES] -“Estimate memory usage of key”
  • MEMORY STATS -“Show memory usage details”
  • MEMORY PURGE -“Ask the allocator to release memory”
  • MEMORY DOCTOR - “A better observability on the Redis memory usage.”
  • MEMORY MALLOC-STATS - “Show allocator internal stats”

本文简述memory每个子命令的用途和部分实现。


1 memory usage

在redis4.0之前,只能通过DEBUG OBJECT命令估算key的内存使用(字段serializedlength),但因为相差太大,没有太多参考价值。

注:可以通过rdb工具分析rdb文件,获得某个key的实际使用内存

如以下示例,k1的序列化值是7。

127.0.0.1:6379> set k1 value1
OK
127.0.0.1:6379> DEBUG OBJECT k1
xx refcount:1 encoding:embstr serializedlength:7 lru:7723189 lru_seconds_idle:160

memory usage的基本使用

usage子命令使用非常简单,直接按memory usage key名字;如果当前key存在,则返回key的value实际使用内存估算值;如果key不存在,则返回nil.
示例:

127.0.0.1:6379> set k1 value1
OK
127.0.0.1:6379> memory  usage k1    //这里k1 value占用57字节内存
(integer) 57
127.0.0.1:6379> memory  usage aaa  // aaa键不存在,返回nil.
(nil)

memory usage细节分析

memory usage不包含key串的内存占用

127.0.0.1:6379>set k1 a    // key长度为2字符
OK
127.0.0.1:6379> memory usage k1  
(integer) 52
127.0.0.1:6379> set k111111111111 a  //key长度为13字符
OK
127.0.0.1:6379> memory usage k111111111111  //两个value相同,但key长度不同的key, usage分析的内存占用相同
(integer) 52
  • memory usage不包含Key Expire的内存占用
127.0.0.1:6379> memory usage k1  
(integer) 52
127.0.0.1:6379> expire k1 10000   //对k1设置ttl
(integer) 1
127.0.0.1:6379> memory usage k1  //usage不包含ttl的内存占用
(integer) 52
  • 对于集合的数据类型(除string外), usage子命令采用类似LRU SAMPLES的抽样方式,默认抽样5个元素求平均 X 元数个数 得出实际内存占用(下一节会详细说明)。所以计算是近似值,当面可以指定抽样的SAMPLES个数。
    示例说明: 生成一个100w个字段的hash键:hkey, 每字段的value长度是从1~1024字节的随机值。
127.0.0.1:6379> hlen hkey    // hkey有100w了字段,每个字段的value长度介入1~1024个字节
(integer) 1000000
127.0.0.1:6379> MEMORY usage hkey   //默认SAMPLES为5,分析hkey键内存占用521588753字节
(integer) 521588753
127.0.0.1:6379> MEMORY usage hkey SAMPLES  1000 //指定SAMPLES为1000,分析hkey键内存占用617977753字节
(integer) 617977753
127.0.0.1:6379> MEMORY usage hkey SAMPLES  10000 //指定SAMPLES为10000,分析hkey键内存占用624950853字节
(integer) 624950853

这是使用抽样求平均的算法,要想获取key较精确的内存值,就指定更大SAMPLES个数。但并不越大越好,因为越大,memory usage占用cpu时间分片就大。

  • memory usage时间复杂度,和指定的SAMPLES数有点
    见以下示例,SAMPLES为1000耗时0.176ms, 为100000耗时14.65ms
127.0.0.1:6379> SLOWLOG get
1) 1) (integer) 3
   3) (integer) 14651
   4) 1) "MEMORY"
      2) "usage"
      3) "hkey"
      4) "SAMPLES"
      5) "100000"
2) 1) (integer) 1
   3) (integer) 176
   4) 1) "MEMORY"
      2) "usage"
      3) "hkey"
      4) "SAMPLES"
      5) "1000"

注:全实例的Expire内存占用,详见下文memory stats子命令的overhead.hashtable.expires)

memory usage源码实现

  • memory命令的入口函数为memoryCommand(object.c文件中)
/* The memory command will eventually be a complete interface for the
 * memory introspection capabilities of Redis.
 *
 * Usage: MEMORY usage <key> */
void memoryCommand(client *c) {
对于memory usage的计算核心函数objectComputeSize(object.c文件中)

因为文章篇幅,这里只贴少部分代码:

//函数注释已说明,value计算和取样计算的处理
/* Returns the size in bytes consumed by the key’s value in RAM.
 * Note that the returned value is just an approximation, especially in the
 * case of aggregated data types where only “sample_size” elements
 * are checked and averaged to estimate the total size. */




#define OBJ_COMPUTE_SIZE_DEF_SAMPLES 5 /* Default sample size. */ //对于集合类型结构,默认取样个数为5 size_t objectComputeSize(robj *o, size_t sample_size) { if (o->type == OBJ_STRING) { //String类型 — 省略——— } else if (o->type == OBJ_LIST) { //List类型 if (o->encoding == OBJ_ENCODING_QUICKLIST) { quicklist *ql = o->ptr; quicklistNode *node = ql->head; asize = sizeof(*o)+sizeof(quicklist); //获取List的头sample_size个元数,计算长度之和 do { elesize += sizeof(quicklistNode)+ziplistBlobLen(node->zl); samples++; } while ((node = node->next) && samples < sample_size); 个元素 asize += (double)elesize/samples*listTypeLength(o); //求平均 X List元素个数,计算出内存之和 } —省略—-


2 memory stats

在redis 4.0之前,我们只能通过info memory查看redis实例的内存大体使用状况;而内存的使用细节,比如expire的消耗,client output buffer, query buffer等是很难直观显示的。 memory stats命令就是为展现redis内部内存使用细节。

memory stats的基本使用

memory stats命令直接运行,返回当前实例内存使用细节;命令的系统开销小(可用于监控采集)。示例如下: 运行时返回33行数据,16个子项目; 下节详细分析,每个子项目的具体含义。

127.0.0.1:6379> memory stats
 1) "peak.allocated"
 2) (integer) 3211205544
 3) "total.allocated"
 4) (integer) 875852320
 5) "startup.allocated"
 6) (integer) 765608
 7) "replication.backlog"
 8) (integer) 117440512
 9) "clients.slaves"
10) (integer) 16858
11) "clients.normal"
12) (integer) 49630
13) "aof.buffer"
14) (integer) 0
15) "db.0"
16) 1) "overhead.hashtable.main"
    2) (integer) 48388888
    3) "overhead.hashtable.expires"
    4) (integer) 104
17) "overhead.total"
18) (integer) 166661600
19) "keys.count"
20) (integer) 1000007
21) "keys.bytes-per-key"
22) (integer) 875
23) "dataset.bytes"
24) (integer) 709190720
25) "dataset.percentage"
26) "81.042335510253906"
27) "peak.percentage"
28) "27.274873733520508"
29) "fragmentation"
30) "0.90553224086761475"

memory stats细节分析

  • peak.allocated: redis从启动来,allocator分配的内存峰值;同于info memory的used_memory_peak
  • total.allocated: allocator分配当前内存字节数;同于info memory的used_memory
  • startup.allocated: redis启动完成使用的内存字节数;- initial_memory_usage; / Bytes used after initialization. /
  • replication.backlog: redis复制积压缓冲区(replication backlog)内存使用字节数; 通过repl-backlog-size参数设置,默认1M,上例中redis设置是100MB。(每个实例只有一个backlog)

注意:1. redis启用主从同步,不管backlog是否被填充,replication.backlog都等于repl-backlog-size的值。
笔者觉得此值应设置为repl_backlog_histlen更合适,没太明白大神的用意。

  1. slave也会启用backlog;用于slave被提升为master后,
    仍能使用PSYNC(这也是redis4.0 PSYNC 2.0实现的基础

clients.slaves: 在master侧,所有slave clients消耗的内存字节数(非常重要的指标)。
每个slave连接master有且只有一个client, 标识为Sclient list命令中flag为S. 这里消耗的内存指每个slave client的query buffer, client output buffer和client本身结构体占用。
有此指标,就能有效监控和分析slave client消耗的output buffer, 更优化地设置”client-output-buffer-limit”。
下面示例:当slave client limit设置很大时,可见client的output占用内存非常大,clients.slaves已达3GB. 以前只能通过client list的omem字段分析。

127.0.0.1:6379> memory stats
 1) "peak.allocated"
 2) (integer) 38697041192
 9) "clients.slaves"     //因slave client出现大量的client output buffer内存占用
10) (integer) 3312505550
11) "clients.normal"
12) (integer) 2531130
  • clients.normal:Redis所有常规客户端消耗内存节字数(非常重要)
    即所有flag为N的客户端内存使用: query buffer + client output buffer + client的结构体内存占用。

计算方式和clients.slave类似。 这个子项对于我们监测异常的数据写入或读取的client非常有用。

127.0.0.1:6379> memory stats   //省略其他信息
 9) "clients.slaves"  // slave client主要占用是client output buffer,见下面id=10520连接
10) (integer) 10256918
11) "clients.normal"   //普通clients占用的内存,一般是query buffer和client output buffer.
12) (integer) 102618310
127.0.0.1:6379> client list  //只显示几个测试clients, 查看omem和qbuf
id=10520 addr=xx:60592 fd=8  flags=S  qbuf=0 qbuf-free=0 obl=0 oll=2 omem=10240060 events=rw cmd=replconf
id=10591 addr=xx:56055 fd=10 flags=N  qbuf=5799889 qbuf-free=4440113 obl=0 oll=0 omem=0 events=r cmd=set
id=10592 addr=xx:56056 fd=11 flags=N  qbuf=10121401 qbuf-free=118601 obl=0 oll=0 omem=0 events=r cmd=set
id=10593 addr=xx:56057 fd=12 flags=N  qbuf=0 qbuf-free=10240002 obl=0 oll=0 omem=0 events=r cmd=set
  • aof.buffer: AOF BUFFER使用内存字节数; 一般较小,只有出现AOF rewrite时buffer会变得较大。开启AOF或定期执行BGREWRITEAOF命令的业务,可能使用内存较大,需关注此项目。
  • overhead.hashtable.main
  • overhead.hashtable.expires
  • overhead.total:redis额外的总开销内存字节数; 即分配器分配的总内存total.allocated,减去数据实际存储使用内存。overhead.total由7部分组成,公式如下:
计算公式:
overhead.total=startup.allocated + replication.backlog + clients.slaves 
           + clients.normal + aof.buffer + overhead.hashtable.main 
           + overhead.hashtable.expires

实例分析:(见上文 <memory stats的基本使用>节中的实例),通过计算验证正确。
765608 + 117440512 + 16858 + 49630 + 0 + 48388888 + 104 = 166661600
且示例中:17) "overhead.total"=166661600
理论应尽量减少额外的内存开销- overhead.total,现在有详细监控,就可以很好入手分析
  • keys.count: 整个实例key的个数; 相同于dbsize返回值
  • keys.bytes-per-key:每个key平均占用字节数;把overhead也均摊到每个key上。不能以此值来表示业务实际的key平均长度。
计算公式:
  keys.bytes-per-key = (total.allocated-startup.allocated)/keys.count   
实例分析:
   (875852320-765608)/1000007 = 875.08  //和上文中的875对应
  • dataset.bytes:表示redis数据占用的内存容量,即分配的内存总量,减去总的额外开销内存量。
计算公式:
    dataset.bytes = total.allocated - overhead.total
实例分析:
    875852320 - 166661600 = 709190720 // 有上文示例中"dataset.bytes"值相同
  • dataset.percentage:表示redis数据占用内存占总内存分配的百分比(重要);
计算公式:
    dataset.percentage = dataset.bytes/(total.allocated-startup.allocated) * 100%
实例分析:
    709190720/(875852320-765608) * 100% = 81.042336750% //有上文示例中的dataset.percentage相同

可表示业务的redis数据存储的内存效率

  • peak.percentage:当前内存使用量与峰值时的占比
计算公式:
    peak.percentage = total.allocated/peak.allocated * 100%
实例分析:
    875852320/3211205544 * 100% = 27.274875681393% //有上文示例中的peak.percentage相同
  • fragmentation: 表示Redis的内存碎片率(非常重要);前文的项目中都没包含redis内存碎片属性
    / Fragmentation = RSS / allocated-bytes /, 同于info memory中的mem_fragmentation_ratio


3 memory doctor

memory doctor命令分析redis使用内存的状态,根据一系列简单判断,给出一定的诊断建议,有一定参考价值。

memory doctor的基本使用

在redis-cli中运行memory doctor命令,如果内存使用有明显不合里的情况,会给出不合理的状态,同时给出处理的建议。
示例如下:

127.0.0.1:6379> memory doctor
"Sam, I detected a few issues in this Redis instance memory implants:\n\n
Peak memory: In the past this instance used more than 150% the memory that is currently using. 
The allocator is normally not able to release memory after a peak, 
so you can expect to see a big fragmentation ratio,
however this is actually harmless and is only due to the memory peak, and if the Redis instance Resident Set Size (RSS) is currently bigger than expected,
the memory will be used as soon as you fill the Redis instance with more data.
 If the memory peak was only occasional and you want to try to reclaim memory, 
 please try the MEMORY PURGE command, otherwise the only other option is to 
 shutdown and restart the instance.\n\n
 I'm here to keep you safe, Sam. I want to help you.\n"

memory doctor细节分析

memory doctor主要列举条件判断,满足条件的给出检查结果和建议。
主要包含以下几点,满足其中一点,就给出诊断结果和建议:

  • used_memory小于5M,doctor认为内存使用量过小,不做进一步诊断
  • peak分配内存大于当前total_allocated的1.5倍,可能说明RSS远大于used_memory
  • 内存碎片率大于1.4
  • 每个Normal Client平均使用内存大于200KB
  • 每个Slave Client平均使用内存大于10MB

memory doctor的源码实现

dockor命令的实现函数getMemoryDoctorReport(void)在object.c源文件
核心代码块如下:

sds getMemoryDoctorReport(void) {
    int empty = 0;          /* Instance is empty or almost empty. */
    int big_peak = 0;       /* Memory peak is much larger than used mem. */
    int high_frag = 0;      /* High fragmentation. */
    int big_slave_buf = 0;  /* Slave buffers are too big. */
    int big_client_buf = 0; /* Client buffers are too big. */
    int num_reports = 0;
    struct redisMemOverhead *mh = getMemoryOverheadData();  //获取各个内存指标,用于后面进行条件判断

    if (mh->total_allocated < (1024*1024*5)) {  // 如果使用内存小于5MB,则判断几乎是空实例,不进行其他诊断
        empty = 1;
        num_reports++;
    } else {
        ---- 省略---
        /* Fragmentation is higher than 1.4? */
        if (mh->fragmentation > 1.4) {
            high_frag = 1;
            num_reports++;
        }

        /* Slaves using more than 10 MB each? */
        if (numslaves > 0 && mh->clients_slaves / numslaves > (1024*1024*10)) {
            big_slave_buf = 1;
            num_reports++;
        }
    }

    sds s;
    if (num_reports == 0) {
        s = sdsnew(
        "Hi Sam, I can't find any memory issue in your instance. "
        "I can only account for what occurs on this base.\n");
    } else if (empty == 1) {
        s = sdsnew(
        "Hi Sam, this instance is empty or is using very little memory, "
        "my issues detector can't be used in these conditions. "
        "Please, leave for your mission on Earth and fill it with some data. "
        "The new Sam and I will be back to our programming as soon as I "
        "finished rebooting.\n");
    } else {
        if (high_frag) {
            s = sdscatprintf(s," * High fragmentation: This instance has a memory fragmentation greater than 1.4 (this means that the Resident Set Size of the Redis process is much larger than the sum of the logical allocations Redis performed). This problem is usually due either to a large peak memory (check if there is a peak memory entry above in the report) or may result from a workload that causes the allocator to fragment memory a lot. If the problem is a large peak memory, then there is no issue. Otherwise, make sure you are using the Jemalloc allocator and not the default libc malloc. Note: The currently used allocator is \"%s\".\n\n", ZMALLOC_LIB);
        }
        if (big_slave_buf) {
            s = sdscat(s," * Big slave buffers: The slave output buffers in this instance are greater than 10MB for each slave (on average). This likely means that there is some slave instance that is struggling receiving data, either because it is too slow or because of networking issues. As a result, data piles on the master output buffers. Please try to identify what slave is not receiving data correctly and why. You can use the INFO output in order to check the slaves delays and the CLIENT LIST command to check the output buffers of each slave.\n\n");
        }
        if (big_client_buf) {

}


4 memory purge

memory purge命令通过调用jemalloc内部命令,进行内存释放,尽量把redis进程占用但未有效使用内存,即常说的内存碎片释放给操作系统。
memory purge功能只适用于使用jemalloc作为allocator的实例。

redis的内存碎片率,是DBA比较头疼的事; 如某个业务下线删除了大量的key,redis不会把“清理”的内存及时归还给操作系统;但这部分内存可以被redis再次利用。

redis4.0提供两种机制解决内存碎片问题,一是memory purge命令; 二是Active memory defragmentation,目前还处于实验阶段,回收效率相当高; 本节只介绍memory purge.

memory purge的基本使用

memory purge使用简单,对性能没明显影响;通过测试验证来看,内存碎片回收的效率不高,当mem_fragmentation_ratio为2时,执行purge基本没有回收;

下面例子中:内存碎片率mem_fragmentation_ratio为8.2,执行memory purge, 碎片率下降为7.31,回收内存0.28GB。 从4.0版本来看,回收的效率不太理想。

127.0.0.1:6379> info memory




Memory

used_memory:344944360
used_memory_human:328.96M
used_memory_rss:2828042240
used_memory_rss_human:2.63G
mem_fragmentation_ratio:8.20
mem_allocator:jemalloc-4.0.3
active_defrag_running:0
lazyfree_pending_objects:0
127.0.0.1:6379> memory purge
OK
127.0.0.1:6379> info memory

Memory

used_memory:344942912
used_memory_human:328.96M
used_memory_rss:2522521600
used_memory_rss_human:2.35G
used_memory_dataset_perc:86.02%
mem_fragmentation_ratio:7.31
mem_allocator:jemalloc-4.0.3
active_defrag_running:0
lazyfree_pending_objects:0
memory purge细节分析

memory purge命令只在jemalloc分配器中有效。
因真正释放内存操作,是通过jemalloc的底层实现,笔者没太看明白;
感兴趣的看官,阅读object.c源文件中的memoryCommand()函数逻辑代码如下:

else if (!strcasecmp(c->argv[1]->ptr,"purge") && c->argc == 2) {




#if defined(USE_JEMALLOC) //判断当前redis使用提malloc是否为jemalloc char tmp[32]; unsigned narenas = 0; size_t sz = sizeof(unsigned); if (!je_mallctl("arenas.narenas", &narenas, &sz, NULL, 0)) { //调用jemalloc的处理 sprintf(tmp, "arena.%d.purge", narenas); if (!je_mallctl(tmp, NULL, 0, NULL, 0)) { addReply(c, shared.ok); return; } } addReplyError(c, "Error purging dirty pages"); #else addReply(c, shared.ok); /* Nothing to do for other allocators. */ #endif 5 memory malloc-stats

此命令用于打印allocator内部的状态,目前只支持jemalloc。对于源码开发同学,应该比较有用;简单示例如下:

127.0.0.1:6379> memory malloc-stats
___ Begin jemalloc statistics ___
Version: 4.0.3-0-ge9192eacf8935e29fc62fddc2701f7942b1cc02c
Assertions disabled
Run-time option settings:
  opt.abort: false
  opt.lg_chunk: 21
  opt.dss: "secondary"
  opt.narenas: 96
  opt.lg_dirty_mult: 3 (arenas.lg_dirty_mult: 3)
  opt.stats_print: false
  opt.junk: "false"
  opt.quarantine: 0
  opt.redzone: false
  opt.zero: false
  opt.tcache: true
  opt.lg_tcache_max: 15
CPUs: 24
Arenas: 96
Pointer size: 8
Quantum size: 8
Page size: 4096
Min active:dirty page ratio per arena: 8:1
Maximum thread-cached size class: 32768
Chunk size: 2097152 (2^21)
Allocated: 345935320, active: 350318592, metadata: 65191296, resident: 455610368, mapped: 2501902336
Current active ceiling: 352321536

arenas[0]:
assigned threads: 1
dss allocation precedence: secondary
min active:dirty page ratio: 8:1
dirty pages: 85527:10020 active:dirty, 82084 sweeps, 112369 madvises, 665894 purged
                            allocated      nmalloc      ndalloc    nrequests
small:                      311397848     12825077     11674091     32248603
large:                         983040         1850         1842         1854
huge:                        33554432            8            6            8
total:                      345935320     12826935     11675939     32250465
--省略------

总结

memory命令使用我们能直观地查看redis内存分布,对我们掌握内存使用情况,有针对性地做业务的内存使用优化。尤其是purge, stats, usage三个子命令。 相信在新的版本中,memory命令的功能会更加强:)

原文发布时间为:2017-11-6
本文作者:RogerZhuo
本文来自云栖社区合作伙伴“老叶茶馆”,了解相关信息可以关注“老叶茶馆”微信公众号

    <!-- 登录查看 begin -->
            <!-- 登录查看 end -->
</div>
<think>好的,用户现在让我“细说Redis”。首先,我需要回顾之前的对话历史。用户之前询问了如何优化前后端数据查询速度,我提到了使用Redis作为缓存机制。现在用户可能想深入了解Redis,包括它的功能、使用场景以及如何实际应用。 用户可能是开发者,或者是正在学习后端技术的人。他们可能已经对数据库有基本了解,现在想进步学习Redis来解决性能问题。深层需求可能是想全面掌握Redis,不仅了解基本概念,还想知道如何在实际项目中应用,以及最佳实践。 接下来,我需要确定回答的结构。应该从Redis的基本定义开始,然后介绍特点、数据结构、应用场景、高可用方案、性能优化、安全运维以及使用建议。还要结合之前的优化建议,比如缓存机制,说明Redis在其中的作用。 要确保内容详细但不过于冗杂。可能需要分点列出,每个部分都有子项,比如数据结构部分要提到String、Hash等,并给出使用场景的例子。应用场景部分需要联系之前的数据库优化,比如缓存、会话管理等。 另外,用户可能对Redis的持久化和集群方案感兴趣,所以需要解释RDB和AOF,以及主从复制、哨兵和Cluster的区别。性能优化方面,可以提到管道技术和内存管理,避免常见误区,比如不要滥用keys命令。 还需要注意提醒用户安全方面的设置,比如密码认证和网络隔离,避免安全漏洞。最后,给出使用建议,比如数据量预估和监控的重要性,帮助用户合理规划。 要确保语言通俗易懂,避免过多专业术语,必要时举例说明。例如,在讲缓存雪崩时,可以举个缓存同时失效的例子,说明如何通过随机过期时间来避免。 最后,检查内容是否符合之前系统级指令的要求,比如数学公式是否正确使用,结构是否清晰,分点是否明确。确保没有使用Markdown,但用符号来区分不同部分,保持整洁。</think>以下是关于 Redis 的详细介绍,涵盖核心特性、使用场景、技术实现和最佳实践: --- ### **Redis 基础概念** **Redis**(Remote Dictionary Server)是个开源的**内存型键值数据库**,支持多种数据结构,以高性能、低延迟著称。 - **定位**:缓存、消息队列、实时数据处理等场景。 - **核心优势**:内存操作 + 单线程模型(避免锁竞争) + 异步持久化。 - **协议**:基于 RESP(REdis Serialization Protocol)通信。 --- ### **二、Redis 核心特性** 1. **数据结构丰富** | 数据结构 | 典型场景 | 示例命令 | |-------------|----------------------------------|-------------------------| | **String** | 缓存、计数器、分布式锁 | `SET`, `INCR`, `GET` | | **Hash** | 存储对象属性(如用户信息) | `HSET`, `HGETALL` | | **List** | 消息队列、最新消息列表 | `LPUSH`, `BRPOP` | | **Set** | 标签系统、共同好友 | `SADD`, `SINTER` | | **ZSet** | 排行榜、延迟队列 | `ZADD`, `ZRANGEBYSCORE` | | **BitMap** | 签到统计、用户在线状态 | `SETBIT`, `BITCOUNT` | | **HyperLogLog** | 基数统计(UV 去重) | `PFADD`, `PFCOUNT` | | **Stream** | 消息队列(支持消费者组) | `XADD`, `XREADGROUP` | 2. **持久化机制** - **RDB(快照)**:定时生成数据快照(二进制文件),恢复速度快,但可能丢失最后次快照后的数据。 ```bash save 900 1 # 900秒内至少1次修改触发保存 ``` - **AOF(追加日志)**:记录所有写操作命令,数据完整性高,但文件较大,恢复速度慢。 ```bash appendonly yes appendfsync everysec # 每秒同步次 ``` 3. **高可用方案** - **主从复制**:主节点(Master)异步复制数据到从节点(Slave),实现读写分离。 - **哨兵(Sentinel)**:监控主节点状态,自动故障转移(选举新 Master)。 - **Cluster 集群**:数据分片(16384 个槽),支持横向扩展,每个节点存储部分数据--- ### **三、Redis 典型应用场景** 1. **缓存加速** - **缓存穿透**:查询不存在的数据(如恶意攻击)→ 解决方案:布隆过滤器(Bloom Filter)拦截无效请求。 - **缓存击穿**:热点 Key 过期后高并发请求直达数据库 → 解决方案:互斥锁(`SETNX`)或永不过期 + 异步更新。 - **缓存雪崩**:大量 Key 同时过期 → 解决方案:随机过期时间(如基础过期时间 + 随机偏移量)。 2. **分布式锁** 通过 `SET key value NX EX` 实现: ```lua -- 原子性操作(加锁 + 设置过期时间) SET lock:order_123 token NX EX 30 -- 释放锁时验证 token(避免误删其他客户端的锁) if redis.call("GET", KEYS[1]) == ARGV[1] then return redis.call("DEL", KEYS[1]) else return 0 end ``` 3. **实时排行榜** 使用 **ZSet** 实现: ```bash ZADD leaderboard 100 "user:A" # 添加分数 ZREVRANGE leaderboard 0 9 WITHSCORES # 获取 Top 10 ``` 4. **消息队列** - **List 实现简单队列**:`LPUSH` + `BRPOP`(阻塞式消费)。 - **Stream 实现高级队列**:支持多消费者组、消息确认(ACK)和消息回溯。 --- ### **四、Redis 性能优化** 1. **内存管理** - **淘汰策略**:根据场景选择 `volatile-lru`(淘汰过期 Key 中的最近最少使用)或 `allkeys-lfu`。 - **碎片整理**:启用 `activedefrag yes`(需 Redis 4.0+)。 - **大 Key 拆分**:避免单个 Key 过大(如 Hash 拆分为多个小 Hash)。 2. **网络优化** - **管道(Pipeline)**:合并多个命令次性发送,减少 RTT(Round-Trip Time)。 - **连接池**:复用连接,避免频繁创建/销毁。 3. **避免阻塞操作** - **禁用长耗命令**:如 `KEYS *` → 改用 `SCAN` 分批次遍历。 - **监控慢查询**:通过 `slowlog get` 分析性能瓶颈。 --- ### **五、Redis 运维与安全** 1. **监控指标** - **内存使用率**:`used_memory` / `maxmemory`。 - **命中率**:`keyspace_hits / (keyspace_hits + keyspace_misses)`。 - **持久化状态**:`rdb_last_save_time`(最后次 RDB 保存时间)。 2. **安全配置** - **密码认证**:设置 `requirepass` 防止未授权访问。 - **网络隔离**:绑定内网 IP(`bind 127.0.0.1`),禁用危险命令(`rename-command FLUSHDB ""`)。 --- ### **六、Redis 使用建议** 1. **适用场景** - **适合**:高频读、低延迟、数据结构复杂、数据量可控(内存成本高)。 - **不适合**:海量冷数据存储、复杂事务(推荐关系型数据库)。 2. **容量规划** - 预估数据量,预留 20%~30% 内存冗余。 - 使用 `INFO memory` 监控碎片率(`mem_fragmentation_ratio`)。 --- ### **七、Redis vs 其他缓存方案** | 对比项 | Redis | Memcached | |----------------|---------------------------|-------------------------| | **数据结构** | 支持复杂数据结构 | 仅 Key-Value | | **持久化** | 支持 RDB/AOF | 不支持 | | **线程模型** | 单线程(避免锁竞争) | 多线程(CPU 密集型更优) | | **适用场景** | 缓存、消息队列、实时计算 | 纯缓存场景 | --- ### **总结** Redis 通过内存操作和高效数据结构设计,成为解决高并发、低延迟场景的利器。使用时需注意: 1. 合理选择数据结构和持久化策略; 2. 避免大 Key 和慢查询; 3. 结合业务场景设计高可用架构(如 Cluster 或 Sentinel)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值