redis中服务器流程分析

Redis服务端初始化流程 参考链接:https://redissrc.readthedocs.io/en/latest/init/server.html https://www.jianshu.com/p/1166288c6d32?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation https://bl... 阅读详情

redis中服务器流程分析


https://segmentfault.com/a/1190000020014518?utm_source=tag-newest

https://blog.youkuaiyun.com/u011003120/article/details/78277133

https://blog.youkuaiyun.com/weixin_42255385/article/details/82119968

http://www.mamicode.com/info-detail-2401000.html

1 背景

前面总结了redis中数据库的键空间和redis中的事件模型,本节总结下Redis如何处理命令?以及Redis中重要的serverCron周期性事件。

本篇主要介绍几面几个点:

  1. Redis初始化过程
  2. 连接请求、处理命令、发送回复的流程
  3. serverCron周期性事件的内部流程

2 initServer初始化服务器

服务器初始化过程主要有以下几步:

  1. 初始化redisServer结构体
  2. 读取配置信息
  3. 为相应的数据结构分配空间
  4. 读取AOF或者RDB恢复数据库状态
  5. aeMain事件循环

2.1 redisServer结构体

每个redis实例都由一个redisServer结构体来表示,其包含众多属性,而首先就需要初始化相关的属性。

在main主函数中调用initServerConfig来完成这部分工作。

主要内容有:

  • 设置服务器运行ID
  • 设置配置文件路径
  • 设置serverCron的频率
  • 设置端口号
  • 设置位长,32 or 64
  • 设置监听套接字属性,比如监听队列长度、keepalive选项
  • 设置AOF和RDB属性
  • 设置ziplist,intset等适用条件
  • 初始化LRU时钟
  • 创建命令表

initServerConfig执行结束之后,会判断是否为sentinel模式,如果是,只需要初始化sentinel相关选项。

2.2 redis config

接着首先读取redis-server的命令参数,比如--port 6379,或者在命令行中指定redis.conf文件。

接下来,调用loadServerConfig来读取配置文件,读取到的信息保存到一个sds字符串中,然后将sds字符串传递给config.c/loadServerConfigFromString来配置。

我们使用redis时,一般在redis.conf中配置相关选项,比如pid路径、日志文件、端口、AOF和RDB机制、最大内存等数据。

2.3 数据结构分配空间

接下来会调用redis.c/demonize设置为守护进程,接着调用redis.c/initServer为相关redisServer中结构分配空间。

initServer函数的主要流程是:

  • 设置信号处理函数,SIGTERM
  • 打开日志文件,openlog
  • 创建客户端连接链表、从服务器链表
  • 创建共享对象,如:常见回复ok,err,pong,-ERR no such key,1~10000直接的常用整数等
  • 调用aeCreateEventLoop创建事件循环结构体,重要
  • 初始化server.db数组,循环对每个数据库,即redisDb结构体,进行初始化
  • 打开监听端口
  • 注册监听事件,关联事件处理器accptTcpHandler,重要
  • 注册serverCron周期性事件,重要
  • 打开AOF或RDB文件
  • 初始化脚本系统
  • 初始化慢查询系统
  • 初始化BIO系统

initServer执行完毕后,会创建PID文件,设置服务器进程名字,下一步就应该从AOF或者RDB载入数据了。

2.4 读取AOF或者RDB

redis.c/main调用loadDataFromDisk函数,该函数内部根据AOF,还是RDB,调用loadAppendOnlyFile或者rdbLoad来载入文件。

AOF文件载入流程,即loadAppendOnlyFile函数流程:

  1. 打开AOF文件
  2. 创建伪客户端
  3. 读取一行命令
  4. 使用伪客户端执行命令
  5. 文件是否读取完毕?否则继续执行3,是则下一步
  6. 关闭文件,释放伪客户端

RDB文件载入流程,即rdbLoad函数流程大概为:打开文件,循环读取文件的databases部分(需要了解RDB文件的存储结构),直到EOF结束。

2.5 主循环

执行完上述初始化过程后,redis.c/main调用aeMain进行事件循环,去处理文件事件和周期性时间事件。

3 连接请求、命令处理、发送回复

3.1 连接请求

客户端的连接请求由acceptTcpHandler来处理,该函数接受连接、创建客户端、注册客户端的命令请求与回复处理器、添加到server.clients链表中。

监听套接字的请求处理器每次执行可以处理多个连接请求:

/*
 * 处理连接请求
 */
void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
    int cport, cfd, max = MAX_ACCEPTS_PER_CALL;
    char cip[REDIS_IP_STR_LEN];
    REDIS_NOTUSED(el);
    REDIS_NOTUSED(mask);
    REDIS_NOTUSED(privdata);
    //处理多个连接请求,至多MAX_ACCEPTS_PER_CALL,当连接没准备好时break(非阻塞)
    while(max--) {
        // accept 客户端连接
        cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);
        if (cfd == ANET_ERR) {
            if (errno != EWOULDBLOCK)
                redisLog(REDIS_WARNING,
                    "Accepting client connection: %s", server.neterr);
            return;
        }
        redisLog(REDIS_VERBOSE,"Accepted %s:%d", cip, cport);
        // 为客户端创建客户端状态(redisClient)、添加到server.clients链表,并注册读写事件处理器
        acceptCommonHandler(cfd,0);
    }
}

3.2 命令请求

先说下命令总体的执行流程:

  1. 客户端键入命令,转换为Redis的协议格式,发送给服务器
  2. 服务器的可写事件被激活,调用readQueryFromClient读入命令,存储到redisClient.queryBuf缓冲区中
  3. 分析client缓冲区中的命令请求,查询命令表,保存到redisClient.cmd中
  4. 调用命令的执行函数,redisCommand.proc
  5. 做后续工作,比如慢查询日志、AOF工作
  6. 向客户端返回回复

下面分步来说。

客户端服务器通信协议

Redis客户端与服务器的通信协议是如下格式:

*<参数数量>
$<参数 1 的字节数量> CR LF
<参数 1 的数据> CR LF
...
$<参数 N 的字节数量> CR LF
<参数 N 的数据> CR LF

举个栗子:

执行命令:set mykey myvalue

该命令的协议如下:

*3
$3
SET
$5
mykey
$7
myvalue</pre>

在redisClient.queryBuf保存为:"*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n"

readQueryFromClient读入命令

/*
 * 读取客户端的查询缓冲区内容
 */
void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
    redisClient *c = (redisClient*) privdata;
    int nread, readlen;
    size_t qblen;
    // ...........省略
    // 分配空间,读入内容
    qblen = sdslen(c->querybuf);
    if (c->querybuf_peak < qblen) c->querybuf_peak = qblen;
    c->querybuf = sdsMakeRoomFor(c->querybuf, readlen);
    nread = read(fd, c->querybuf+qblen, readlen);

    // 读入出错
    if (nread == -1) {
        if (errno == EAGAIN) { //非阻塞EAGIN
            nread = 0;
        } else {
            redisLog(REDIS_VERBOSE, "Reading from client: %s",strerror(errno));
            freeClient(c);
            return;
        }
    // read返回0,可能是客户端关闭了连接
    } else if (nread == 0) {
        redisLog(REDIS_VERBOSE, "Client closed connection");
        freeClient(c);
        return;
    }
    if (nread) {
        // 根据内容,更新查询缓冲区(SDS) free 和 len 属性,并将 '\0' 正确地放到内容的最后
        sdsIncrLen(c->querybuf,nread);
        // 记录服务器和客户端最后一次互动的时间
        c->lastinteraction = server.unixtime;
        // 如果客户端是 master 的话,更新它的复制偏移量
        if (c->flags & REDIS_MASTER) c->reploff += nread;
    } else {
        // 在 nread == -1 且 errno == EAGAIN 时运行
        server.current_client = NULL;
        return;
    }
    // ...省略部分
	
    // 从查询缓存重读取内容,创建参数,并执行命令
    // 函数会执行到缓存中的所有内容都被处理完为止
    processInputBuffer(c);

    server.current_client = NULL;
}

processInputBuffer处理缓冲区

其中会循环读取client.queryBuf,调用processInlineBuffer来解析命令,然后调用processCommand执行命令,重置客户端。

该过程可以如下伪代码表示:

void processInputBuffer(redisClient *c) {
    //循环读取缓冲区数据
    while(sdslen(c->querybuf)) {
	    //解析缓冲区命令
		processInlineBuffer(c);
		//执行命令
		processCommand(c);
		//重置客户端
		resetClient(c);
	}
}

重点说一下redisCommand结构

struct redisCommand {
    // 命令名字
    char *name;
    // 实现函数
    redisCommandProc *proc;
	//....

服务器通过processInlineBuffer解析命令后,保存到client.argc和client.argv中,然后调用processCommand,其内部会调用lookupCommand去查找命令表。

命令表是一个字典,保存在redisServer.commands中,字典的键是一个命令名字,比如set,get,del等;字典值是一个redisComand结构体,其记录了一个redis命令的实现信息。

查表的过程很简单,直接通过dictFetchValue即可,将查询结果保存到client.cmd中。

/*
 * 根据给定命令名字(SDS),查找命令
 */
struct redisCommand *lookupCommand(sds name) {
    return dictFetchValue(server.commands, name);
}

执行命令

processCommand中调用call函数,执行命令实现函数:

call(c,REDIS_CALL_FULL); --> c->cmd->proc(c);

call函数中更新统计信息

call函数是真正执行命令的地方,是redis的核心函数,除了执行命令,还会统计命令执行时间,写入慢查询日志,发送给监视器,写入AOF,传播到slave节点。

// 调用命令的实现函数,执行命令
void call(redisClient *c, int flags) {
    //Note:其中省略了部分函数
	//转发给监视器
    replicationFeedMonitors(c,server.monitors,c->db->id,c->argv,c->argc);
    // 计算命令开始执行的时间
    start = ustime();
    // 执行实现函数
    c->cmd->proc(c);
    // 计算命令执行耗费的时间
    duration = ustime()-start;
    // 计算命令执行之后的 dirty 值
    dirty = server.dirty-dirty;
    // 如果有需要,将命令放到 SLOWLOG 里面
    if (flags & REDIS_CALL_SLOWLOG && c->cmd->proc != execCommand)
        slowlogPushEntryIfNeeded(c->argv,c->argc,duration);
    // 更新命令的统计信息
    if (flags & REDIS_CALL_STATS) {
        c->cmd->microseconds += duration;
        c->cmd->calls++;
    }
    // 将命令复制到 AOF 和 slave 节点
    if (flags & REDIS_CALL_PROPAGATE) {
            propagate(c->cmd,c->db->id,c->argv,c->argc,flags);
    }
    server.stat_numcommands++;
}

回复客户端

在调用命令实现函数后,比如setCommand,它会调用addReply将回复保存到客户端的输出缓冲区中,并注册客户端套接字的可写事件,关联到sendReplyToClient事件处理器,当套接字可写时,会执行networking.c/sendReplyToClient,发送回复给客户端。

经过上述步骤,就完成了Redis命令的执行过程。

4 周期性事件serverCron内部流程

redis在初始化过程中注册了serverCron周期性事件,频率默认hz=10,即每100毫秒执行一次。

serverCron中完成众多工作,会定期对服务器自身的状态进行检查和调整,

  1. 更新服务器的各类统计信息,比如时间、内存占用、每秒执行的命令次数
  2. 调用databaseCron,清理数据库过期键值,对字典进行收缩操作
  3. 处理SIGTERM信号,关闭服务器
  4. 调用clientCron,关闭和清理失效客户端
  5. 执行被延迟的BGRWRITEAOF,因为在BGSAVE期间,客户端的BGRWRITEAOF会被延迟
  6. 检查BGSAVE和BGRWRITEAOF子进程的执行状态,如果已经执行完,则需要执行后续步骤
  7. 将AOF缓冲区内容写入AOF文件
  8. 如果是主服务器,则定义对从服务器同步
  9. 集群模式,则定期同步和连接测试

serverCron内部源码

int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
    int j;

    if (server.watchdog_period) watchdogScheduleSignal(server.watchdog_period);

    /* Update the time cache. */
    updateCachedTime();

    // 记录服务器执行命令的次数
    run_with_period(100) trackOperationsPerSecond();

	//更新LRU时间
    server.lruclock = getLRUClock();

    // 记录服务器的内存峰值
    if (zmalloc_used_memory() > server.stat_peak_memory)
        server.stat_peak_memory = zmalloc_used_memory();

    // 服务器进程收到 SIGTERM 信号,会在sigtermHandler中打开shutdown_asap标志,在此处则关闭服务器
    if (server.shutdown_asap) {

        // 尝试关闭服务器
        if (prepareForShutdown(0) == REDIS_OK) exit(0);

        // 如果关闭失败,那么打印 LOG ,并移除关闭标识
        redisLog(REDIS_WARNING,"SIGTERM received but errors trying to shut down the server, check the logs for more information");
        server.shutdown_asap = 0;
    }

    // 打印数据库的键值对信息
    run_with_period(5000) {
        for (j = 0; j < server.dbnum; j++) {
            long long size, used, vkeys;

            // 可用键值对的数量
            size = dictSlots(server.db[j].dict);
            // 已用键值对的数量
            used = dictSize(server.db[j].dict);
            // 带有过期时间的键值对数量
            vkeys = dictSize(server.db[j].expires);

            // 用 LOG 打印数量
            if (used || vkeys) {
                redisLog(REDIS_VERBOSE,"DB %d: %lld keys (%lld volatile) in %lld slots HT.",j,used,vkeys,size);
                /* dictPrintStats(server.dict); */
            }
        }
    }

    // 如果服务器没有运行在 SENTINEL 模式下,那么打印客户端的连接信息
    if (!server.sentinel_mode) {
        run_with_period(5000) {
            redisLog(REDIS_VERBOSE,
                "%lu clients connected (%lu slaves), %zu bytes in use",
                listLength(server.clients)-listLength(server.slaves),
                listLength(server.slaves),
                zmalloc_used_memory());
        }
    }

    // 检查客户端,关闭超时客户端,并释放客户端多余的缓冲区
    clientsCron();

    // 对数据库执行各种操作,删除过期键,缩小字典
    databasesCron();

    // 如果 BGSAVE 和 BGREWRITEAOF 都没有在执行,并且有一个 BGREWRITEAOF 在等待,那么执行被延迟的BGREWRITEAOF
    if (server.rdb_child_pid == -1 && server.aof_child_pid == -1 &&
        server.aof_rewrite_scheduled)
    {
        rewriteAppendOnlyFileBackground();
    }

    // 检查 BGSAVE 或者 BGREWRITEAOF子进程是否已经执行完毕
    if (server.rdb_child_pid != -1 || server.aof_child_pid != -1) {
        int statloc;
        pid_t pid;

        // 接收子进程发来的信号,非阻塞
        if ((pid = wait3(&statloc,WNOHANG,NULL)) != 0) {
            int exitcode = WEXITSTATUS(statloc);
            int bysignal = 0;

            if (WIFSIGNALED(statloc)) bysignal = WTERMSIG(statloc);

            // BGSAVE 执行完毕
            if (pid == server.rdb_child_pid) {
                backgroundSaveDoneHandler(exitcode,bysignal);

            // BGREWRITEAOF 执行完毕
            } else if (pid == server.aof_child_pid) {
                backgroundRewriteDoneHandler(exitcode,bysignal);

            } else {
                redisLog(REDIS_WARNING,
                    "Warning, detected child with unmatched pid: %ld",
                    (long)pid);
            }
            updateDictResizePolicy();
        }
    } else {

        // 既然没有 BGSAVE 或者 BGREWRITEAOF 在执行,那么检查是否需要执行它们
        // 即:判断是否满足变化参数,遍历所有保存条件,看是否需要执行 BGSAVE 命令
         for (j = 0; j < server.saveparamslen; j++) {
            struct saveparam *sp = server.saveparams+j;
            // 检查是否有某个保存条件已经满足了
            if (server.dirty >= sp->changes &&
                server.unixtime-server.lastsave > sp->seconds &&
                (server.unixtime-server.lastbgsave_try >
                 REDIS_BGSAVE_RETRY_DELAY ||
                 server.lastbgsave_status == REDIS_OK))
            {
                redisLog(REDIS_NOTICE,"%d changes in %d seconds. Saving...",
                    sp->changes, (int)sp->seconds);
                // 执行 BGSAVE
                rdbSaveBackground(server.rdb_filename);
                break;
            }
         }

        // 判断是否需要进行AOF重写,是则触发BGREWRITEAOF
         if (server.rdb_child_pid == -1 &&
             server.aof_child_pid == -1 &&
             server.aof_rewrite_perc &&
             // AOF 文件的当前大小大于执行 BGREWRITEAOF 所需的最小大小
             server.aof_current_size > server.aof_rewrite_min_size)
         {
            // 上一次完成 AOF 写入之后,AOF 文件的大小
            long long base = server.aof_rewrite_base_size ?
                            server.aof_rewrite_base_size : 1;

            // AOF 文件当前的体积相对于 base 的体积的百分比
            long long growth = (server.aof_current_size*100/base) - 100;

            // 如果增长体积的百分比超过了 growth ,那么执行 BGREWRITEAOF
            if (growth >= server.aof_rewrite_perc) {
                redisLog(REDIS_NOTICE,"Starting automatic rewriting of AOF on %lld%% growth",growth);
                // 执行 BGREWRITEAOF
                rewriteAppendOnlyFileBackground();
            }
         }
    }

    // 根据 AOF 政策,考虑是否需要将 AOF 缓冲区中的内容写入到 AOF 文件中
    run_with_period(1000) {
        if (server.aof_last_write_status == REDIS_ERR)
            flushAppendOnlyFile(0);
    }

    // 关闭那些需要异步关闭的客户端
    freeClientsInAsyncFreeQueue();

    // 清除被暂停的客户端
    clientsArePaused();

    // 主从复制的cron函数,周期性执行,默认1秒一次
    // 重连接主服务器、向主服务器发送 ACK 、判断数据发送失败情况、断开本服务器超时的从服务器,等等
    run_with_period(1000) replicationCron();

    // 集群的cron函数,周期性执行,默认1秒10次,向各个节点发送PING消息进行故障检测
    run_with_period(100) {
        if (server.cluster_enabled) clusterCron();
    }

    // sentinel 模式下的cron函数,周期性的发送INFO命令、PING命令、执行故障转移等
    run_with_period(100) {
        if (server.sentinel_mode) sentinelTimer();
    }

    // 集群操作相关,不懂此处
    run_with_period(1000) {
        migrateCloseTimedoutSockets();
    }

    // 增加 loop 计数器
    server.cronloops++;

    //返回时间间隔,代表周期性时间
    return 1000/server.hz;
}

5 Rerference

  1. 黄健宏. Redis设计与实现[M]. 机械工业出版社, 2014.
Redis源码剖析(一)服务器与客户端交互流程 Redis中的C/S模型Redis底层还是基于网络请求的,对于单机数据库而言,网络请求仅仅是在一台机器上交互,即服务器客户端都在一台计算机上当在终端输入redis-serve时,便启动了一个Redis服务器,随后开始初始化内部数据,对于Redis而言包括 读取配置文件初始化内部参数 创建默认数据库(默认为16个) 创建监听套接字并绑定回调函数(接收客户端连接请求) 执行事件驱动循环,开始响应客户端请 阅读详情

相关推荐

从应用到底层:36张图带你进入Redis世界(下)

总感觉哪里不对,但是又说不上来 从应用到底层:36张图带你进入Redis世界(上) 4.6 事务 MySQL 中的事务还是挺多道道的还要,而在Redis中的事务只要有如下三步: 关于事务具体结论: 1、redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。  2、Redis事务没有隔离级别的概念:批量操作在发送 EXEC 命令前被放入队列缓存,并不会被实际执行,也就不存在事务内的查询要看到事务里的更新,事务外查询不能看到。 3、Redis不保证原子性:Redis中..

uuuyy_的博客 997

Redis代码阅读3--Redis网络监听(3)

是介绍Redis网络监听的最后一篇文章,着重分析定时时间处理函数serverCron,这个函数其实已经和网络监听没多大关系了,当时因为其绑定在Redis自定义的事件库的定时事件上,所以放到一起来讲。serverCron的这个函数对Redis的正常运行来说很重要,对于Redis的使用者来说,最重要的就是能够迅速直观地看到Redis的当前的运行状况(keys,sizes,memory等),server...

olylakers的博客 241

Rasa课程、Rasa培训、Rasa面试、Rasa实战系列之Docker Tracker Store Redis服务部署

Rasa课程、Rasa培训、Rasa面试、Rasa实战系列之Docker Tracker Store Redis服务部署 Redis 是一种内存数据结构存储,用作分布式内存 键值 数据库、缓存和消息代理, Redis 支持不同类型的抽象数据结构,例如字符串、列表、映射、集合、排序集、 位图、 空间索引。本文使用Redis服务部署Rasa Track Store及Rasa Lock Store。 查找Redis镜像 查询Redis下载地址 https://hub.docker.com/_/redis 安

大模型与Agent智能体 2119

ubuntu简单安装redis

1. 安装redis (1)安装redis server: sudo apt-get install redis-server (安装后redis默认启动) (2)查看redis进程: ps -aux|grep redis (127.0.0.1:6379对应进程号) (3)查看redis服务状态: netstat -nlt|grep 6379 (tcp/tcp 6均处于listen状态) (4)查看redis服务器装填:sudo /etc/init.d/redis-server status .

Jalen备忘录 430

使用 Python 获取网络数据

本文介绍了 Python 获取网络数据、使用 requests 库、编写爬虫代码以及使用 IP 代理的方法。Python 的 requests 库非常方便,可以帮助我们快速访问网络资源。编写爬虫程序需要掌握正则表达式、HTTP 协议等知识,同时需要注意爬取速度和频率,以避免对被爬取网站造成负担。最后,使用 IP 代理可以帮助我们隐匿身份,保护我们的网络安全。

互联说 2695

同一台电脑使用模拟ModbusTcp主从通讯的时候,Wireshark抓取不到包

在使用ModBus Slave和Poll模拟主从通讯的时候,发现Wireshark怎么都抓取不到包怎么排查和解决?

HUJIANGMING的博客 705

跟着大彬读源码 - Redis 7 - 对象编码之简单动态字符串

目录 1 SDS 的定义 2 SDS 对比 C 字符串有哪些优势 总结 Redis 没有直接使用 C 语言传统的字符串表示(以空字符串结尾的字符数组),而是构建了一种名为简单动态字符串(simple dynamic string)的抽象类型,并将 SDS 用作 Redis ...

dianfuquan4988的博客 192

FPGA 核脉冲梯形成型 Verilog 实现:从 MATLAB 到 50MHz 采样率部署

本文详细解析了FPGA实现核脉冲梯形成型的完整流程,从MATLAB仿真到50MHz采样率的Verilog硬件部署。重点探讨了梯形成型算法对负指数信号的优化处理,包括定点数量化、流水线架构设计及资源优化技巧,为核辐射探测与高能物理实验提供高效解决方案。

weixin_30920513的博客 390

Redis2.2.2源码学习——信号的处理

Redis对信号的处理如下: 1)忽略的信号:SIGHUP(终端session关闭)、SIGPIPE(管道错误) 2)出错相关信号:SIGSEGV SIGBUS SIGFPE SIGILL 处理方式:触发段错误的处理函数中, 1.主要将错误信息写入日志 2.将该信号处理信号设置为默认,重新在本身发送该信号(执行信号的默认处理) 3)进程结束信号:SIGTERM(该信号用于请求中止进程,kill命令缺省发送) 1.server.shutdown_asap = 1; 和程序出错信号相比,SI

ordeder的专栏 3070

基于CPCC_S_TASK_LIST_MAINTAIN的工艺路线组件批量分配实战与避坑指南

本文详细解析了SAP PP模块中基于CPCC_S_TASK_LIST_MAINTAIN函数实现工艺路线组件批量分配的实战技巧与避坑指南。针对批量分配的核心挑战,提供了参数配置、BAPI协同使用、Excel导入等解决方案,并分享了大容量数据处理优化经验,帮助用户高效完成工艺路线组件分配任务。

weixin_33563728的博客 306

CTF实战:手把手教你用Python破解转轮机加密(附完整脚本)

本文详细介绍了如何使用Python逆向破解转轮机加密,特别针对CTF比赛中的Crypto类题目。通过解析转轮机加密原理、实战Python脚本编写及调试技巧,帮助参赛者快速掌握破解方法,提升在攻防世界等CTF比赛中的解题效率。

silver的专栏 1116

解决(error) ERR Errors trying to SHUTDOWN. Check logs.问题~

该问题出现在我在使用shutdown关闭redis服务器时,出现该问题的原因是由于。第一步:在该目录下使用touch命令创建日志文件。第二步:将日志文件配置到redis.conf中。配置完成后需要重启服务器。

m0_64365419的博客 1005

【AFAC2026金融智能-赛题二】2亿像素的面条图,我切了1000刀拼回完美Markdown

摘要: AFAC2026金融智能创新大赛的「复杂金融文档还原挑战」任务是将高分辨率金融文档截图转换为结构化Markdown文本。面对2亿像素的“面条图”和严格的计算限制(禁用主流大模型,仅允许调用FinixDoc-VL API且模型需小于10M),作者提出四层流水线解决方案: 图像预处理:通过文本行投影检测在行间隙处智能切图,避免截断文字; API调度:5路并发轮询请求,结合内容哈希缓存与指数退避重试机制优化调用效率; 后处理:采用最长公共子串匹配去除文本重叠,并设计表格对齐算法修复跨块表格; 结果输出:按

170

redis源码分析八--从客户端发送set命令后看服务端的执行流程上

1、Redis服务端的执行流程 Redis程序分为客户端和服务端,之间通信是通过socket来传递指令信息,这里有一个问题就是我客户端发送一个set命令后服务器会做哪一些操作呢?具体操作语句见下图 这里我设置了一个字符串number值为12306传递给Redis服务器端来进行保存,服务器端是如何实时监听我们的发送指令,并且如何解析的呢? 首先我给出服务器端的调用流程图如下 首先服务器端在启动时...

曳凡的博客 461

【软考高项论文】论信息系统项目管理的干系人绩效域

例如,在处理与电信运营商合作铺设光纤网络的问题时,考虑到其在通信设施建设和运维方面的专业性和重要性,我们将之定位为高权力高利益的干系人,给予了高度关注和优先沟通。项目全程,我们对干系人绩效域的执行效果进行了定期检查和评估,通过定量与定性的数据收集,确认了项目在干系人管理上的成效,具体表现为:建立了稳固的工作关系网络,使干系人普遍认可项目目标;鉴于本项目牵涉到政府各级部门、企事业单位、市民团体、技术研发伙伴等多种类型的干系人,且干系人的诉求和影响力各异,因此,有序且持续的干系人参与管理显得尤为关键。

Programming Talk 1681

32、运算放大器的工作原理与应用

本文详细介绍了运算放大器的工作原理与多种应用场景,涵盖开环与闭环放大器的基本概念、理想运算放大器的特性及其‘黄金规则’。文章深入解析了反相放大器、同相放大器、单位增益跟随器与反相器的配置和增益计算,并探讨了电压比较器及窗口比较器的工作机制。同时提供了运算放大器在实际应用中的选型流程、使用注意事项以及常见故障排查方法,帮助读者全面掌握运算放大器的设计与应用要点。

u5v6w7的博客 164

Redis源码分析(一)redis.c //redis-server.c

Redis源码分析(一)redis.c //redis-server.c 入口函数 int main() 4450 int main(int argc, char **argv) { 4451 initServerConfig(); 4452 if (argc == 2) { 4453 ResetServerSaveParams(); 4454 ...

csdn_kou的博客 979

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

ERR Errors trying to SHUTDOWN. Check logs.

使用shutdown命令报错: ERR Errors trying to SHUTDOWN. Check logs. 原因: 我们的日志文件没有配置 执行命令,会进行保存,但是权限不够,也就是要给rbd文件赋予权限。 解决办法: 1、进入我们的redis.conf配置文件,找到logfile设置日志文件路径,配置日志的路径 usr/local/bin/redis-log.log,然后保存退出 2、授予权限信息 # 给日志文件授予权限 chmod 777 /usr/local/bin/redis-l

七一 2063

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

rasa会话数据存储 RedisTrackerStore 连接哨兵

rasa RedisTrackerStore 连接哨兵

weixin_41021342的博客 2459

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

深入了解Redis:概念、工作原理、流程和应用详细介绍

深入了解Redis:概念、工作原理、流程和应用详细介绍

深耕嵌入式领用多年, 致力于分享嵌入式领域技术! 2941

Redis实战(10)-一条命令在Redis是如何执行的?

Redis Server一旦和某客户端建立连接,就会在事件驱动框架中注册可读事件,对应客户端的命令请求。

JavaEdge全是干货的技术号 1764

Redis企业实战篇

1.基于Session实现登录

weixin_53693850的博客 301

为了拿捏 Redis 数据结构,我画了 40 张图(完整版)

大家好,我是小林。 Redis 为什么那么快? 除了它是内存数据库,使得所有的操作都在内存上进行之外,还有一个重要因素,它实现的数据结构,使得我们对数据进行增删查改操作时,Redis 能高效的处理。 因此,这次我们就来好好聊一下 Redis 数据结构,这个在面试中太常问了。 注意,Redis 数据结构并不是指 String(字符串)对象、List(列表)对象、Hash(哈希)对象、Set(集合)对象和 Zset(有序集合)对象,因为这些是 Redis 键值对中值的数据类型,也就是数据的保存形式,这些对象的底

小林coding 1万+

redis系列三:redis命令的执行过程

redis命令的执行过程

HK_KIKY的博客 2628

16张图吃透 Redis 架构演进全过程(循序渐进)

现如今 Redis 变得越来越流行,几乎在很多项目中都要被用到,不知道你在使用 Redis 时,有没有思考过,Redis 到底是如何稳定、高性能地提供服务的?你也可以尝试回答一下以下这些问题:我使用 Redis 的场景很简单,只使用单机版 Redis 会有什么问题吗?我的 Redis 故障宕机了,数据丢失了怎么办?如何能保证我的业务应用不受影响?为什么需要主从集群?它有什么优势?什么是分片集群?我真的需要分片集群吗?...如果你对 Redis 已经有些了解,肯定也听说过数据持久化、主从复制、哨兵这些概念,它

Java搜索工程技术栈 2797

读书笔记:redis开发与运维 基础篇

redis特性 1>速度快:redis所有数据都是存放在内存中,是redis速度快的主要原因,是有c编写,一般来说用c编写的程序距离操作系统近,执行速度更快,非阻塞I/O,使用epoll作为I/O多路复用的技术实现,不再网路I/O上浪费过多时间,使用单线程架构,预防了多线程可能产生竞争的问题,对于服务端开发来说,锁和线程切换通常是性能杀手(但是单线程有一个问题就是每个命令执行时间有要...

通往架构师的成长学习之路 2605
上一篇: 【linux】free命令中cached和buffers的区别
下一篇: 深入 C++ 的 unique_ptr
TuxedoLinux
博客等级 码龄9年 268粉丝 34原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值