【Redis深度专题】「核心技术提升」探究Redis服务启动的过程机制的技术原理和流程分析的指南(基础功能分析)

news2024/10/6 14:25:51

探究Redis服务启动的过程机制的技术原理和流程分析的指南

  • Redis基本概念
    • Redis特点说明
  • Redis源码结构
  • Redis功能架构
  • Redis启动流程
    • 初始化全局服务器配置
      • 源码分析
      • 分析说明
        • initServerConfig方法
        • 初始化的内容
        • 保存机制的初始化策略
          • 优化的初始化策略
        • 指定配置文件
        • 加载配置文件
        • 默认的数据保存策略
          • 每15分钟进行1次更新
          • 每5分钟进行100次更新
          • 每1分钟进行10000次更新
        • 初始化服务器
        • 加载数据库
          • 读取文件的操作
          • AOF文件加载
            • loadAppendOnlyFile
            • appendonly.aof
            • 执行AOF命令
          • RDB文件加载
            • 从数据库文件中加载数据的基本步骤
    • 网络监听
  • Redis总结

Redis基本概念

Redis(REmote DIctionary Server)是由Salvatore Sanfilippo开发的高性能key-value存储系统,完全遵守BSD协议并且开源免费。

Redis特点说明

Redis具有以下几个特点,使其与其他key-value缓存产品(如memcache)相区别。

  1. 数据持久化: Redis支持将内存中的数据保存到磁盘中,以便在重新启动后再次加载使用。这使得Redis既能提供高速的读写性能,又能保证数据不会因为服务重启而丢失。

  2. 多样的数据结构: Redis不仅支持简单的key-value类型的数据,还提供了丰富的数据结构,包括list(列表)、set(集合)、zset(有序集合)和hash(哈希),扩展了Redis的应用场景。

  3. 数据备份: Redis支持主从模式的数据备份。在这种架构下,主节点负责写操作,而从节点负责数据的备份,确保数据的安全性和高可用性。

  4. 高性能: Redis以其出色的性能而闻名,能够处理高并发的读写请求。其内存存储和基于事件驱动的异步IO机制,使其能够在独立进程或线程中高效地执行操作,迅速响应请求。

  5. 原子性操作: Redis中的所有操作都是原子性的,保证了多个操作的执行不会被半途中断或干扰。此外,Redis还支持原子性地将一组操作合并为一个操作批量执行,提高了效率和性能。

  6. 丰富的扩展特性: Redis还提供了诸如发布/订阅、通知、键的过期等特性,使得Redis在构建实时应用、消息系统和计数器等场景中更加灵活和强大。

  7. 简洁的代码风格: Redis的代码风格极其精简,整个源码只有23000行,易于理解和阅读。这使得开发者可以通过阅读Redis源码来深入理解其实现原理,从而更好地使用和定制Redis。

Redis源码结构

Redis是一个开源的、高性能的键值存储系统。它的源代码目录结构如下:
在这里插入图片描述

  • src:该目录包含了Redis的核心源代码。
    • adlist.cadlist.h:双向链表的实现。
    • ae.cae.h:事件驱动库的实现,处理事件的多路复用。
    • anet.canet.h:网络库的实现,封装了底层网络操作。
    • config.h:配置文件,定义了Redis的各种编译选项和宏定义。
    • crc16.ccrc64.c:CRC校验的实现。
    • db.cdb.h:数据库的实现,包括键值对的存储和操作等。
    • debug.cdebug.h:调试相关的实现。
    • dict.cdict.h:字典的实现,用于存储键值对。
    • fmacros.h:一些常用的宏定义。
    • help.h:帮助信息的定义。
    • intset.cintset.h:整数集合的实现。
    • lzf_c.clzf_d.c:压缩算法LZF的实现。
    • multi.cmulti.h:事务的实现。
    • networking.c:网络模块的实现,包括请求的解析和响应的生成。
    • object.cobject.h:对象的实现,包括字符串、列表、哈希等各种类型。
    • pubsub.cpubsub.h:发布订阅的实现。
    • redis.c:Redis服务器的入口点,包含主循环和命令处理逻辑。
    • redis-check-aof.credis-check-dump.c:用于检查和修复AOF日志和RDB文件的工具。
    • redis-cli.c:Redis命令行客户端的实现。
    • redis-benchmark.c:性能测试工具的实现。
    • sha1.csha1.h:SHA1算法的实现。
    • sds.csds.h:简单动态字符串的实现。
    • server.cserver.h:服务器相关的实现,包括配置文件的解析、客户端的管理等。
    • syncio.csyncio.h:同步IO库的实现。
    • t_hash.ct_list.ct_set.ct_string.ct_zset.c:各种数据类型的实现。
    • util.cutil.h:工具函数的实现。

此外,还有一些其他的目录:

  • deps:该目录包含了Redis所依赖的第三方库,如Jemalloc、Lua等。
  • tests:该目录包含了Redis的测试代码。
  • utils:该目录包含了一些实用工具,如启动脚本、配置文件样例等。

以上是Redis源码的大致目录结构。不同版本的Redis可能会有一些差异,但总体结构类似。

Redis功能架构

在这里插入图片描述

Redis启动流程

本文深入介绍了Redis的启动加载过程,可以总结为以下几个键步骤:
在这里插入图片描述

  1. 全局服务器配置的初始化
    在服务器启动时,首先进行全局服务器配置的初始化。这包括初始化服务器的监听端口、最大连接数、日志级别等参数,以及其他与服务器运行相关的配置项。

  2. 配置文件的加载
    接下来,系统会加载配置文件。如果用户指定了配置文件路径,则使用指定的配置文件进行加载;否则,系统将使用默认的配置文件。配置文件中包含了一些常用的配置项,如数据库地址、密码、缓存大小等。

  3. 服务器的初始化
    服务器的初始化是指服务器程序的启动和必要的资源分配。在这一步中,服务器会创建并初始化网络监听器、数据库连接池、线程池等关键组件,为后续的业务逻辑提供支持。

  4. 数据库的加载
    在服务器初始化完成后,会加载数据库配置和连接信息。服务器会连接到指定的数据库,对相关的数据表进行初始化,确保数据库的完整性和可用性。

  5. 网络监听
    服务器会开启网络监听,开始接收来自客户端的请求。通过网络监听器,服务器可以接受和处理各种类型的请求,如查询、更新、删除等。同时,服务器会通过网络监听器将响应结果返回给客户端。

通过上述步骤,服务器完成了全局配置的初始化、配置文件的加载、服务器的初始化、数据库的加载和网络的监听。这些步骤确保了服务器能够正常运行,并能够接收和处理客户端的请求。

初始化全局服务器配置

源码分析

 umask(server.umask = umask(0777));

    uint8_t hashseed[16];
    getRandomBytes(hashseed,sizeof(hashseed));
    dictSetHashFunctionSeed(hashseed);

    char *exec_name = strrchr(argv[0], '/');
    if (exec_name == NULL) exec_name = argv[0];
    server.sentinel_mode = checkForSentinelMode(argc,argv, exec_name);
    initServerConfig();
    ACLInit(); /* The ACL subsystem must be initialized ASAP because the
                  basic networking code and client creation depends on it. */
    moduleInitModulesSystem();
    connTypeInitialize();

    /* Store the executable path and arguments in a safe place in order
     * to be able to restart the server later. */
    server.executable = getAbsolutePath(argv[0]);
    server.exec_argv = zmalloc(sizeof(char*)*(argc+1));
    server.exec_argv[argc] = NULL;
    for (j = 0; j < argc; j++) server.exec_argv[j] = zstrdup(argv[j]);

    /* We need to init sentinel right now as parsing the configuration file
     * in sentinel mode will have the effect of populating the sentinel
     * data structures with master nodes to monitor. */
    if (server.sentinel_mode) {
        initSentinelConfig();
        initSentinel();
    }

分析说明

通过将全局服务器配置的初始化过程封装到 initServerConfig() 函数中,可以提高代码的可维护性和可扩展性。在需要修改或扩展服务器配置时,只需要修改或扩展这个函数即可,而不需要修改其他相关的代码。

具体代码如下所示:

struct redisServer server;
initServerConfig(&server);

全局服务器配置通过 initServerConfig() 函数完成。这个函数的主要作用是对 server 变量进行初始化,server 是一个 redisServer 结构的变量。

initServerConfig方法

该函数的作用是确保服务器在启动时具有正确的初始配置,并为后续的加载和操作做准备。在 initServerConfig() 函数中,会对 server 变量的各个属性进行赋值。这些属性包括服务器的监听地址、监听端口、最大连接数、超时时间等。通过给这些属性赋予合适的初值,能够确保服务器在启动时具备正确的配置和参数设置。

void initServerConfig(void) {
    int j;
    char *default_bindaddr[CONFIG_DEFAULT_BINDADDR_COUNT] = CONFIG_DEFAULT_BINDADDR;

    initConfigValues();
    updateCachedTime(1);
    server.cmd_time_snapshot = server.mstime;
    getRandomHexChars(server.runid,CONFIG_RUN_ID_SIZE);
    server.runid[CONFIG_RUN_ID_SIZE] = '\0';
    changeReplicationId();
    clearReplicationId2();
    server.hz = CONFIG_DEFAULT_HZ; /* Initialize it ASAP, even if it may get
                                      updated later after loading the config.
                                      This value may be used before the server
                                      is initialized. */
    server.timezone = getTimeZone(); /* Initialized by tzset(). */
    server.configfile = NULL;
    server.executable = NULL;
    server.arch_bits = (sizeof(long) == 8) ? 64 : 32;
    server.bindaddr_count = CONFIG_DEFAULT_BINDADDR_COUNT;
    for (j = 0; j < CONFIG_DEFAULT_BINDADDR_COUNT; j++)
        server.bindaddr[j] = zstrdup(default_bindaddr[j]);
    memset(server.listeners, 0x00, sizeof(server.listeners));
    server.active_expire_enabled = 1;
    server.lazy_expire_disabled = 0;
    server.skip_checksum_validation = 0;
    server.loading = 0;
    server.async_loading = 0;
    server.loading_rdb_used_mem = 0;
    server.aof_state = AOF_OFF;
    server.aof_rewrite_base_size = 0;
    server.aof_rewrite_scheduled = 0;
    server.aof_flush_sleep = 0;
    server.aof_last_fsync = time(NULL);
    server.aof_cur_timestamp = 0;
    atomicSet(server.aof_bio_fsync_status,C_OK);
    server.aof_rewrite_time_last = -1;
    server.aof_rewrite_time_start = -1;
    server.aof_lastbgrewrite_status = C_OK;
    server.aof_delayed_fsync = 0;
    server.aof_fd = -1;
    server.aof_selected_db = -1; /* Make sure the first time will not match */
    server.aof_flush_postponed_start = 0;
    server.aof_last_incr_size = 0;
    server.aof_last_incr_fsync_offset = 0;
    server.active_defrag_running = 0;
    server.notify_keyspace_events = 0;
    server.blocked_clients = 0;
    memset(server.blocked_clients_by_type,0,
           sizeof(server.blocked_clients_by_type));
    server.shutdown_asap = 0;
    server.shutdown_flags = 0;
    server.shutdown_mstime = 0;
    server.cluster_module_flags = CLUSTER_MODULE_FLAG_NONE;
    server.migrate_cached_sockets = dictCreate(&migrateCacheDictType);
    server.next_client_id = 1; /* Client IDs, start from 1 .*/
    server.page_size = sysconf(_SC_PAGESIZE);
    server.pause_cron = 0;

    server.latency_tracking_info_percentiles_len = 3;
    server.latency_tracking_info_percentiles = zmalloc(sizeof(double)*(server.latency_tracking_info_percentiles_len));
    server.latency_tracking_info_percentiles[0] = 50.0;  /* p50 */
    server.latency_tracking_info_percentiles[1] = 99.0;  /* p99 */
    server.latency_tracking_info_percentiles[2] = 99.9;  /* p999 */

    server.lruclock = getLRUClock();
    resetServerSaveParams();

    appendServerSaveParams(60*60,1);  /* save after 1 hour and 1 change */
    appendServerSaveParams(300,100);  /* save after 5 minutes and 100 changes */
    appendServerSaveParams(60,10000); /* save after 1 minute and 10000 changes */

    /* Replication related */
    server.masterhost = NULL;
    server.masterport = 6379;
    server.master = NULL;
    server.cached_master = NULL;
    server.master_initial_offset = -1;
    server.repl_state = REPL_STATE_NONE;
    server.repl_transfer_tmpfile = NULL;
    server.repl_transfer_fd = -1;
    server.repl_transfer_s = NULL;
    server.repl_syncio_timeout = CONFIG_REPL_SYNCIO_TIMEOUT;
    server.repl_down_since = 0; /* Never connected, repl is down since EVER. */
    server.master_repl_offset = 0;
    server.fsynced_reploff_pending = 0;

    /* Replication partial resync backlog */
    server.repl_backlog = NULL;
    server.repl_no_slaves_since = time(NULL);

    /* Failover related */
    server.failover_end_time = 0;
    server.force_failover = 0;
    server.target_replica_host = NULL;
    server.target_replica_port = 0;
    server.failover_state = NO_FAILOVER;

    /* Client output buffer limits */
    for (j = 0; j < CLIENT_TYPE_OBUF_COUNT; j++)
        server.client_obuf_limits[j] = clientBufferLimitsDefaults[j];

    /* Linux OOM Score config */
    for (j = 0; j < CONFIG_OOM_COUNT; j++)
        server.oom_score_adj_values[j] = configOOMScoreAdjValuesDefaults[j];

    /* Double constants initialization */
    R_Zero = 0.0;
    R_PosInf = 1.0/R_Zero;
    R_NegInf = -1.0/R_Zero;
    R_Nan = R_Zero/R_Zero;

    /* Command table -- we initialize it here as it is part of the
     * initial configuration, since command names may be changed via
     * redis.conf using the rename-command directive. */
    server.commands = dictCreate(&commandTableDictType);
    server.orig_commands = dictCreate(&commandTableDictType);
    populateCommandTable();

    /* Debugging */
    server.watchdog_period = 0;
}

initServerConfig() 函数还可能包括一些检查和验证步骤,以确保配置的正确性和合法性。例如,可以检查指定的监听端口是否已被占用,或者验证用户提供的参数是否符合要求。

初始化的内容

初始化的内容涵盖以下几个方面:

  1. 网络监听相关:配置绑定地址和TCP端口等网络监听参数。
  2. 虚拟内存相关:设置swap文件和page大小等虚拟内存参数。
  3. 保存机制:定义保存数据的策略,例如在多长时间内有多少次更新才进行保存。
  4. 复制相关:配置复制功能,包括是否作为slave、master的地址和端口等。
  5. Hash相关设置:对Hash结构相关的参数进行初始化。
  6. 初始化命令表:完成命令表的初始化,包括注册支持的命令、命令的参数和执行函数等。

通过对以上方面进行初始化,可以确保服务器在启动时具备合适的配置和参数设置。这样做有助于提高服务器的性能和稳定性,并且可以提供更好的用户体验。

为了进一步优化代码,建议将以上初始化的内容模块化,以提高代码的可读性和可维护性。可以将不同方面的初始化逻辑分别放到不同的函数中,然后在 initServerConfig() 函数中依次调用这些初始化函数,以实现整体的初始化过程。

总结而言,通过对不同方面的内容进行初始化,可以提高服务器性能和稳定性,同时对代码进行优化和模块化,有助于提高代码的可读性和可维护性

保存机制的初始化策略

在服务器的保存机制中,需要确定初始化策略,即在多长时间内有多少次更新才进行保存。通过合理的设置保存策略,可以确保数据的稳定性和一致性,并在必要时提供数据的恢复和备份。

优化的初始化策略

在这里插入图片描述

  1. 频率设置:根据业务需求和数据敏感性,确定保存的频率。例如,可以将保存频率设置为每小时一次,或每天一次等。

  2. 变更阈值:定义在指定时间段内所发生的变更次数,超过该阈值才触发保存操作。这个阈值可以根据服务器的负载情况、数据更新频率和可接受的数据损失程度进行调整。例如,如果服务器的负载较高且数据更新频率较高,可以设置较小的阈值以保证数据的实时性。

  3. 异常处理:考虑到突发情况和异常场景,建议在策略中加入异常处理机制。例如,可以设置一个最大保存间隔时间,即使在达到变更阈值之前,如果超过该时间没有进行保存,也会触发保存操作,以避免数据丢失。

  4. 监控和报警:实现监控和报警系统,用于监控保存操作是否成功,并及时发送警报通知相关人员,以便及时采取措施解决潜在的问题。

// 1小时内1次更新
appendServerSaveParams(60*60,1);

// 5分钟内100次更新
appendServerSaveParams(300,100);

// 1分钟内10000次更新
appendServerSaveParams(60,10000);

指定配置文件

如果在启动服务器时指定了配置文件,那么在下面的“加载配置文件”步骤中,将根据配置文件的内容来更改服务器的某些配置。

这个步骤的目的是为了实现根据用户指定的配置文件来自定义服务器的设置。通过加载配置文件,可以轻松地更改服务器的各种参数和选项,以适应不同的需求和环境。系统会读取配置文件的内容,并解析其中的特定参数。然后,根据这些参数的值,进行相应的配置更改,例如修改服务器的端口号、调整缓存大小、设定权限等。

这种动态加载配置文件的方式可以提供更大的灵活性和可配置性。用户可以根据自己的需求,通过修改配置文件来定制服务器的行为,而无需修改源代码或重新编译。

使用配置文件还可以方便地管理和维护服务器配置。通过将配置项集中存储在配置文件中,可以更加清晰地查看和修改服务器的设置,而不需要深入代码中查找。

加载配置文件

当指定了配置文件之后,Redis会使用loadServerConfig()函数来加载配置文件。这个过程非常简单,主要是通过标准I/O库打开配置文件,然后逐行读取并覆盖之前设置的默认配置。

配置文件的使用可以让用户根据自己的需求来自定义Redis的设置。通过编辑配置文件,可以轻松地更改Redis的各种参数和选项,以适应不同的使用场景和需求。

注意,在Redis的代码包中,有一个默认配置文件。如果在启动Redis服务器时没有指定配置文件,那么Redis不会使用这个默认文件中的配置,而是会使用之前步骤中的默认初始化配置。

默认配置文件中的配置项与之前步骤中默认初始化的配置项可能会有所不同。因此,如果没有指定配置文件,不能认为Redis的行为会按照默认配置文件进行。

默认的数据保存策略

在默认配置文件中,这些配置项定义了Redis在何时将数据写入磁盘进行持久化。每个配置项都由两个参数组成:时间间隔和更新次数。有一个典型的数据保存策略示例

每15分钟进行1次更新
save 900 1
每5分钟进行100次更新
save 300 10
每1分钟进行10000次更新
save 60 10000

在上述示例中,第一个配置项表示每隔15分钟,Redis会对数据进行一次更新并写入磁盘。第二个配置项表示每隔5分钟,Redis会对数据进行100次更新后才写入磁盘。第三个配置项表示每隔1分钟,Redis会对数据进行10000次更新后才写入磁盘。

这些配置可以根据具体的需求进行调整。通过修改时间间隔和更新次数,可以根据数据更新频率和持久化要求来优化数据保存策略。

初始化服务器

初始化服务器的工作主要在initServer()函数中完成。该函数负责完成之前未完成的工作,并对server变量进行初始化。具体的工作包括设置信号处理、创建clients和slaves列表、创建Pub/Sub通道列表,同时还会创建共享对象。

下面是一些示例的共享对象的创建:

shared.crlf = createObject(REDIS_STRING, sdsnew("\r\n"));
shared.ok = createObject(REDIS_STRING, sdsnew("+OK\r\n"));
shared.err = createObject(REDIS_STRING, sdsnew("-ERR\r\n"));
shared.emptybulk = createObject(REDIS_STRING, sdsnew("$0\r\n\r\n"));

最后,如果启用了虚拟内存机制,还需要初始化虚拟内存相关的内容,例如,Thread I/O等。通过initServer()函数的执行,服务器的各个组件和变量将得到正确的初始化,为后续的操作和功能提供了基础。

注意,在实际应用中,根据具体需求可以对initServer()函数的实现进行进一步优化和扩展,以满足不同的业务需求和性能要求

加载数据库

在完成了上面的所有的初始化工作之后,Redis开始加载数据到内存中,如果启用了appendonly了,则Redis从appendfile加载数据,否则就从dbfile加载数据。

完成了上述所有的初始化工作后,Redis会开始将数据加载到内存中。根据配置的不同,Redis会从不同的地方加载数据。

读取文件的操作

如果启用了持久化机制中的appendonly选项,Redis会从appendonly文件中加载数据;否则,Redis会从数据库文件(dbfile)中加载数据。

这一步是将持久化的数据重新加载到内存中,以便在服务器重启后恢复数据的一部分。这样,用户可以继续使用之前存储的数据,而无需重新生成或重新存储数据。

注意,加载数据的过程可能会花费一些时间,具体的加载速度取决于数据量的大小和硬件性能等因素。在加载数据的过程中,Redis会将数据逐步地加载到内存中,并进行相应的索引和数据结构的构建,以便提供快速和高效的数据访问。

AOF文件加载
loadAppendOnlyFile

了解一下从appendfile中加载数据的过程。在执行loadAppendOnlyFile()函数之前,我们首先需要了解appendfile保存了哪些信息。为此,让我们假设我们执行了以下两条命令,并在配置文件中开启了appendonly选项。

例子:

SET key1 value1
SET key2 value2

在Redis的appendfile中,我们将找到以下数据:

*3\r\n$3\r\nSET\r\n$4\r\nkey1\r\n$6\r\nvalue1\r\n
*3\r\n$3\r\nSET\r\n$4\r\nkey2\r\n$6\r\nvalue2\r\n

上述数据是通过RESP(Redis Serialization Protocol)格式来保存的。RESP是一种简单且高效的序列化协议,用于在Redis中编码和解码数据。

在执行loadAppendOnlyFile()函数时,Redis会读取并解析appendfile的内容,将其中的数据加载到内存中供后续使用。通过存储和加载appendfile中的数据,Redis可以实现持久化机制,并确保在服务器重启后能够恢复之前的数据状态。

注意,使用appendfile来保存数据会增加磁盘空间的占用,因此应根据实际需求和硬件资源来选择是否开启该选项。此外,为了保证数据的一致性和可靠性,建议定期对appendfile进行备份和监测

appendonly.aof

在redis的appendonly.aof文件中,保存的是来自客户端的请求命令。您可能会注意到,在这些保存的命令中,并没有包含GET命令。这是因为在appendonly.aof文件中,只保存了写入数据的请求命令,而不保存读取数据的命令。

在加载数据时,Redis会按照保存的请求命令的顺序重新执行这些命令,以还原先前存储的数据状态。

server.appendonly = 0;
fakeClient = createFakeClient();
startLoading(fp);
  1. Redis在开始加载数据之前会暂时关闭appendonly选项。然后,Redis会创建一个假的Redis客户端,并将server.appendonly设置为0以表示关闭appendonly选项

  2. Redis会使用这个假的客户端来执行startLoading(fp)函数,其中fp是指向appendonly.aof文件的指针。通过这个过程,Redis会逐行读取appendonly.aof文件中的命令,并按顺序执行它们,从而加载数据到内存中。

注意,关闭appendonly选项是为了避免加载数据时出现冲突或错误。加载完数据后,可以再次打开appendonly选项,确保之后的请求命令能够被正确地保存。

执行AOF命令

读取appendonly.aof文件中的命令,并在假的Redis客户端上下文中执行这些命令。在执行命令的过程中,服务器不会对该客户端做任何应答。

fakeClient->argc = argc;
fakeClient->argv = argv;
cmd->proc(fakeClient);

/* 假的客户端不应该有回复 */
redisAssert(fakeClient->bufpos == 0 && listLength(fakeClient->reply) == 0);

这段代码会将加载过程中的命令参数(argc)和命令参数值(argv)分配给假的客户端(fakeClient),然后通过cmd->proc(fakeClient)来执行这些命令。

如果在加载过程中,发现物理内存不足且Redis开启了VM(虚拟内存)功能,那么还需要处理swap操作以释放一些内存空间给加载的数据使用。一旦加载完成,将重新设置appendonly标志,以确保之后的请求命令能够正确保存到appendonly.aof文件中。

RDB文件加载

接下来我们将讨论从数据库文件中加载数据(rdbLoad()函数)的步骤。当Redis没有开启appendonly时,我们需要将数据加载到内存中,主要步骤如下:

从数据库文件中加载数据的基本步骤
  1. 处理SELECT命令,即选择数据库。
  2. 读取key。
  3. 读取value。
  4. 检测key是否已过期。
  5. 将新的对象添加到哈希表中。
  6. 如果需要,设置过期时间。
  7. 如果开启了VM,执行swap操作。

通过以上步骤,我们可以将数据库文件中的数据有效地加载到内存中,以供Redis后续使用。这些步骤确保了数据的正确性和有效性,并在需要的情况下进行过期检查和swap操作,以便更好地管理内存空间。

网络监听

在完成初始化配置和数据加载后,Redis会启动监听服务。值得一提的是,Redis的网络库并不使用libevent或libev等库。

在启动监听服务之后,Redis会开始监听指定的端口,等待来自客户端的连接请求。一旦有客户端请求连接,Redis就会按照定义的协议进行通信,并执行相应的命令或操作。

尽管Redis没有使用libevent或libev这样的外部库来实现网络功能,但它依然能够高效地处理并发连接请求和网络通信。Redis使用了自己的网络库,这样可以更好地控制和优化网络通信的细节,提高效率和可靠性。

通过以上优化,Redis能够在初始化配置和数据加载完成后快速启动监听服务,并通过自身的网络库实现高效的并发网络通信。


Redis总结

Redis是一个开源、高性能的key-value存储系统。它支持数据持久化、多样的数据结构、数据备份以及拥有高性能和原子性操作等特点。此外,Redis提供了丰富的扩展特性,包括发布/订阅、通知和键的过期等,适用于各种实时应用和消息系统。Redis的代码风格简洁,源码可读性强,进一步促进了开发者对Redis的理解和使用。

相信看了本篇内容,你应该对Redis的启动逻辑有了一定的认识,后面会针对于源码进行深入分析,敬请期待!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/784222.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【每日运维】RockyLinux8非容器化安装Mysql、Redis、RabitMQ单机环境

系统版本&#xff1a;RockyLinux 8.6 安装方式&#xff1a;非容器化单机部署 安装版本&#xff1a;mysql 8.0.32 redis 6.2.11 rabbitmq 3.11.11 elasticsearch 6.7.1 前置条件&#xff1a;时间同步、关闭selinux、主机名、主机解析host 环境说明&#xff1a;PC电脑VMware Work…

Hadoop生态体系-HDFS

目录标题 1、Apache Hadoop2、HDFS2.1 设计目标&#xff1a;2.2 特性&#xff1a;2.3 架构2.4 注意点2.5 HDFS基本操作2.5.1 shell命令选项2.5.2 shell常用命令介绍 3、HDFS基本原理3.1 NameNode 概述3.2 Datanode概述 1、Apache Hadoop Hadoop&#xff1a;允许使用简单的编程…

webpack require.context

require.context((directory: String),(includeSubdirs: Boolean) /* 可选的&#xff0c;默认值是 true */,(filter: RegExp) /* 可选的&#xff0c;默认值是 /^\.\/.*$/&#xff0c;所有文件 */,(mode: String) /* 可选的&#xff0c; sync | eager | weak | lazy | lazy-onc…

【C#】使用this进行扩展方法以及静态类和静态成员

2023年&#xff0c;第30周&#xff0c;第2篇文章。给自己一个目标&#xff0c;然后坚持总会有收货&#xff0c;不信你试试&#xff01; 本篇文章主要简单讲讲&#xff0c;使用this进行扩展方法以及静态类和静态成员 目录 一、this扩展1、扩展条件2、举例代码 二、静态知识点1、…

【Linux网络】 网络套接字(三)socket编程_TCP网络程序

目录 TCP网络程序服务端创建套接字并绑定服务端监听服务端获取连接服务器处理请求 客户端客户端创建套接字客户端连接服务器客户端发起请求测试 服务器存在的问题多进程版的TCP网络程序多线程版的TCP网络程序线程池版的TCP网络程序 TCP网络程序总结图 TCP网络程序 服务端 创建…

一站式财务管家工具:Zoho Books审批功能详细介绍

Zoho Books作为一款功能强大的财务管理软件&#xff0c;提供了多种实用的功能&#xff0c;其中审批流程是非常重要的一个。那么&#xff0c;Zoho Books的审批功能是如何实现的呢&#xff1f;本文将为您详细介绍。 1. 什么是审批功能 审批是企业内部重要业务流程中的前置环节&a…

华为数通HCIP-OSPF基础

路由协议 作用&#xff1a;用于路由设备学习非直连路由&#xff1b; 动态路由协议&#xff1a;使路由设备自动学习到非直连路由&#xff1b; 分类&#xff1a; 按照算法分类&#xff1a; 1、距离矢量路由协议&#xff1b;&#xff08;RIP、BGP&#xff09; 只交互路由信息…

基于FPGA实现OSD功能

简介 基于FPGA平台实现简单的OSD的功能,对于FPGA实现OSD只能实行简单的画框和文字叠加,如果实现复杂的车道线画框,则没法实现(起码我个人感觉,这个功能没有思路执行)。 FPGA实现OSD功能需要7系列平台,以及VDMA、OSD等Xilinx公司的IP使用(本功能工程采用Vivado2017.4平台…

windows关闭某个进程

一、使用命令 &#xff08;1&#xff09;winR键打开命令提示符&#xff0c;输入cmd &#xff08;2&#xff09;输入netstat -ano &#xff08;3&#xff09;输入taskkill /f /pid 进程ID。例如&#xff1a;taskkill /f /pid 19216 如果成功终止的话&#xff0c;会出现成功&…

Vue+Nodejs 使用WebSocket创建一个简易聊天室

文章目录 一、页面效果二、架构流程三、技术细节1.客户端2. 服务端 一、页面效果 二、架构流程 使用vue编写前端页面&#xff0c;nodejs处理服务端消息&#xff0c;WebSocket进行实时通信 三、技术细节 1.客户端 <template><div><form onsubmit"return…

Ubuntu录屏软件Kazam

1. 安装 1.1. 桌面右键“打开终端” 1.2. 安装kazam这款软件。 sudo apt-get install kazam 2. 使用 2.1. 安装后打开&#xff0c;我们看看这款软件界面还是很友好很简洁的。 2.2. 除了录像我们还可以截图&#xff0c;也可以选择全屏、窗口、区域的方式录制。 2.3. 如果要录…

Hybird app 热更新工作原理

大家对于原生应用和混合应用已经非常熟悉了&#xff0c;这里就不再进行详细的介绍&#xff0c;用通俗易懂的话解释下他们的一些特点。 1、原生应用 在 Android、iOS 等移动平台上利用提供的开发语言、开发类库、开发工具进行 App 软件开发。比如 Android 是用 Java、Eclipse、…

改进的北方苍鹰算法优化VMD参数,最小包络熵、样本熵、信息熵、排列熵(适应度函数可自行选择,一键修改)包含MATLAB源代码...

今天给大家带来一期由改进的北方苍鹰算法(SCNGO)优化VMD的两个参数。 同样以西储大学数据集为例&#xff0c;选用105.mat中的X105_BA_time.mat数据中1000个数据点。没有数据的看这篇文章。西储大学轴承诊断数据处理&#xff0c;matlab免费代码获取 选取四种适应度函数进行优化&…

【开发问题】flink-cdc不用数据库之间的,不同类型的转化

不同的数据库之期间数据类型转化 问题来源与原因解决过程&#xff0c;思路错误&#xff0c;导致各种错误错误思路是什么 正确解决方式&#xff0c;找官网对应的链接器&#xff0c;数据转化 问题来源与原因 我一开始是flink-cdc&#xff0c;oracle2Mysql&#xff0c;sql 我一开…

Hygon海光电脑:window无法对计算机进行,windows无法对计算机进行启动到下一个安装阶段怎么办...

海光CPU电脑安装第2个系统&#xff0c;Windows10LTSC&#xff0c;U盘引导顺利&#xff0c;安装顺利&#xff0c;在最后一步时出错&#xff1a;。 出错提示 Windows10安装过程中提示&#xff1a; windows无法对计算机进行启动到下一个安装阶段的准备。要安装Windows&#xff0…

时空复杂度详解

&#x1f493;博主个人主页:不是笨小孩&#x1f440; ⏩专栏分类:数据结构与算法&#x1f440; &#x1f69a;代码仓库:笨小孩的代码库&#x1f440; ⏩社区&#xff1a;不是笨小孩&#x1f440; &#x1f339;欢迎大家三连关注&#xff0c;一起学习&#xff0c;一起进步&#…

【CAS6.6源码解析】调试Rest API接口

CAS的web层默认是基于webflow实现的&#xff0c;ui和后端是耦合在一起的&#xff0c;做前后端分离调用和调试的时候不太方便。但是好在CAS已经添加了支持Rest API的support模块&#xff0c;添加相应模块即可。 文章目录 添加依赖并重新build效果 添加依赖并重新build 具体添加…

第54步 深度学习图像识别:MLP-Mixer建模(Pytorch)

基于WIN10的64位系统演示 一、写在前面 &#xff08;1&#xff09;MLP-Mixer MLP-Mixer&#xff08;Multilayer Perceptron Mixer&#xff09;是Google在2021年提出的一种新型的视觉模型结构。它的主要特点是完全使用多层感知机&#xff08;MLP&#xff09;来处理图像&#…

seaborn笔记 pairplot PairGrid

1 数据集 鸢尾花数据集 # Visual Python: Data Analysis > File vp_df pd.read_csv(https://raw.githubusercontent.com/visualpython/visualpython/main/visualpython/data/sample_csv/iris.csv) vp_df 1.1 基本pairplot import seaborn as snsg sns.pairplot(vp_df) …

前端随笔:HTML/CSS/JavaScript和Vue

前端随笔 1&#xff1a;HTML、JavaScript和Vue 最近因为工作需要&#xff0c;需要接触一些前端的东西。之前虽然大体上了解过HTML、CSS和JavaScript&#xff0c;也知道HTML定义了内容、CSS定义了样式、JavaScript定义了行为&#xff0c;但是却没有详细的学习过前端三件套的细节…