news 2026/4/23 12:14:21

多路转接select系统调用详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
多路转接select系统调用详解

此文章对应五种IO模型的1.3:I/O多路复用select系统调用的讲解

目录

  • 1. 初识select
  • 2. select函数原型
    • 2.1 参数解释
    • 2.2 函数返回值
    • 2.3 关于fd_set结构
    • 2.4 理解readfds、writefds、exceptfds
  • 3. 理解select执行过程
  • 4. socket就绪条件
  • 5. select的特点
  • 6. select缺点
  • 7. select使用示例
    • 7.1 检测标准输入输出
    • 7.2 使用select实现网络消息传送

1. 初识select

select 是系统提供的经典 IO 多路复用实现,通过 select 系统调用可搭建多路复用输入 / 输出模型,是实现单进程 / 线程管理多个 IO 操作的核心接口之一,核心特性如下:

  • select 系统调用的核心作用,是让程序统一监视多个文件描述符的可读、可写、异常三类核心状态变化
    • 什么叫可读?底层有数据,读事件就绪
    • 什么叫可写?底层有空间,写事件就绪
  • 调用 select 后,进程 / 程序会有阻塞和非阻塞状态,非阻塞状态时的返回值有所不同;阻塞状态,直到被监视的文件描述符中有一个或多个发生了指定的状态变化,或达到预设的等待超时时间,阻塞才会解除。

总结:select就是:通过等待多个fd状态变化的一种就绪事件通知机制!

2. select函数原型

select 的函数原型如下(Linux 系统编程标准接口):

#include<sys/select.h>intselect(intnfds,fd_set*readfds,fd_set*writefds,fd_set*exceptfds,structtimeval*timeout);

2.1 参数解释

  • nfds:需要监视的最大文件描述符值 + 1(文件描述符从 0 开始遍历检测,此参数用于限定内核检测的范围,提升效率);

  • readfds:可读文件描述符集合的指针,传入需监视的可读 fd,返回后仅保留发生可读状态变化的 fd;

  • writefds:可写文件描述符集合的指针,传入需监视的可写 fd,返回后仅保留发生可写状态变化的 fd;

  • exceptfds:异常文件描述符集合的指针,传入需监视的异常 fd,返回后仅保留发生异常状态变化的 fd;

    注:以上三个集合参数,若无需监视对应状态,可传入NULL

  • timeouttimeval结构体指针,用于设置 select 的阻塞等待超时时间,内核会在该时间内等待 fd 状态变化,超出时间则直接返回,有三种核心取值:

    1. NULL:永久阻塞,直到被监视的 fd 中有状态变化才解除阻塞;
    2. 时间值全为 0:非阻塞模式,立即检测所有 fd 状态并返回,不做任何等待;
    3. 设定具体时间值:超时阻塞,若超时前无 fd 状态变化,到点后自动返回。

补充:timeval 结构体定义

timeout 依赖的timeval结构体用于精确设置秒和微秒级超时时间,定义如下:

structtimeval{longtv_sec;// 秒longtv_usec;// 微秒(1秒=10^6微秒)};

2.2 函数返回值

select 调用的返回值为 int 类型,不同返回值对应不同执行结果,仅返回值大于 0 时表示有文件描述符状态发生变化,具体说明如下:

  • 返回值 > 0:执行成功,返回状态发生变化的文件描述符总数(可读、可写、异常状态的 fd 数量累加);
  • 返回值 = 0:超时返回,在timeout设定的时间内,无任何被监视的文件描述符状态发生变化,程序解除阻塞继续执行;
  • 返回值 = -1:调用失败,此时错误原因会存入系统全局变量errno传入的 readfds、writefds、exceptfds 和 timeout 参数值会变得不可预测,不可继续使用

常见错误值(errno)及含义

select 调用失败时,errno常见的取值及对应错误原因如下:

  • EBADF:传入的文件描述符为无效值,或对应的文件 / 套接字已被关闭;
  • EINTR:select 调用过程中被系统信号中断(如收到 SIGINT、SIGCHLD 等信号);
  • EINVAL:参数非法,常见原因是nfds为负值,或timeout结构体中的时间值设置无效(如微秒数 tv_usec≥10^6);
  • ENOMEM:内核内存资源不足,无法完成 select 的相关初始化操作。

补充使用要点

若因EINTR导致 select 调用失败,通常可在代码中做重入处理(重新调用 select),这类情况属于可恢复的错误,并非真正的程序异常。

常见的程序片段如下:

fs_set readset;FD_SET(fd,&readset);select(fd+1,&readset,NULL,NULL,NULL);if(FD_ISSET(fd,readset)){……}

2.3 关于fd_set结构

fd_set是系统为 select 专门定义的文件描述符集合类型,其底层实现为整数数组,本质是一块位图,通过位图中每一位的置 1 / 置 0来标识对应的文件描述符是否被纳入监视范围 ——位图的第 n 位,对应文件描述符 fd=n,位为 1 表示监视该 fd,位为 0 表示不监视。

为避免直接操作位图带来的位运算错误、提升代码可读性,系统提供了一组专用的接口函数来操作fd_set,无需开发者手动处理位运算,接口如下:

#include<sys/select.h>// 清除描述符集合set中,fd对应的位(置0,取消对该fd的监视)voidFD_CLR(intfd,fd_set*set);// 检测描述符集合set中,fd对应的位是否为1(非0表示已置1,0表示未置1)intFD_ISSET(intfd,fd_set*set);// 设置描述符集合set中,fd对应的位(置1,加入对该fd的监视)voidFD_SET(intfd,fd_set*set);// 将描述符集合set中所有位清0,初始化空集合voidFD_ZERO(fd_set*set);

2.4 理解readfds、writefds、exceptfds

这三个参数是输入输出型参数

以readfds为例,其余同理:

输入时:

用户告诉内核,你要帮我关心哪些fd上的读事件

比特位的位置,表示fd编号,比特位的内容,表示是否关心。

例如:0000 1111,表示只关心0~3这四个fd

输出时:

内核告诉用户,你让我关心的哪些fd上面的读事件已经就绪

比特位的位置,表示fd的编号,比特位的内容,表示是否就绪

例如:0000 0010,表示只有fd=1的文件描述符的内容是就绪的。

fd_set是位图结构,一次性可以向里面添加多个fd

细节:

  1. 位图是输入输出的,所以,将来这个位图一定会被频繁变更
  2. 位图有多少个比特位,就决定了select最多能关心多少个fd。(fd_set是一个系统提供的数据类型(struct):fd_set大小固定!)
  3. readfds:如果把fd添加到readfds集合中,表示改fd,只关心读事件。告诉内核,你只需要帮我关心fd的读事件。

3. 理解select执行过程

理解 select 模型的核心,是掌握内核对 fd_set 的修改逻辑。为简化理解,我们做一个经典的简化假设:取fd_set的长度为 1 字节(实际系统中fd_set长度由宏FD_SETSIZE定义,Linux 默认对应 1024 个 fd),fd_set的每 1 个二进制位对应 1 个文件描述符 fd,位图的第 n 位对应文件描述符 fd=n,1 字节的fd_set最多可监视 fd=0~7 共 8 个文件描述符。

以下通过可读事件监视的完整步骤,直观演示 select 的执行过程:

  1. 定义并初始化 fd_set 集合:执行fd_set set; FD_ZERO(&set);,将集合所有位清 0,此时 set 的二进制表示为0000 0000
  2. 添加需监视的 fd:若要监视 fd=5 的可读事件,执行FD_SET(5, &set);,将第 5 位置 1,此时 set 变为0001 0000
  3. 继续添加监视 fd:再依次执行FD_SET(2, &set);FD_SET(1, &set);,将第 2 位、第 1 位置 1,此时 set 变为0001 0011
  4. 调用 select 阻塞等待:执行select(6, &set, NULL, NULL, NULL);,进入阻塞状态。👉 关键参数呼应:nfds=6是监视的最大 fd(5)+1;后三个参数为 NULL,表示仅监视可读事件、不监视可写 / 异常事件、永久阻塞(直到有 fd 触发事件);
  5. 内核检测到事件,select 解除阻塞并返回:若此时 fd=1、fd=2 上触发了可读事件,fd=5 无任何事件,select 会立即返回(返回值为 2,对应 2 个状态变化的 fd),内核会重写传入的 fd_set 集合,仅保留触发事件的 fd 对应位为 1,未触发的位全部清 0,此时 set 变为0000 0011(fd=5 对应的位被清空)。

核心执行要点

select 的执行过程,本质是用户层设置监视集合 → 内核层检测 fd 状态 → 内核重写集合并返回的过程,其中最关键的是:

  • select 调用返回后,原传入的 fd_set 会被内核覆盖重写,仅保留触发对应事件的 fd 位为 1,未触发事件的 fd 位会被强制清 0;
  • 正因内核会重写 fd_set,每次重新调用 select 前,都需要重新执行 FD_ZERO 初始化 + FD_SET 设置监视 fd,否则会因脏数据导致监视异常。

4. socket就绪条件

select 监视 socket 触发的可读、可写、异常事件,本质是检测 socket 满足就绪条件,只有当 socket 符合对应就绪规则时,内核才会将其标记为状态变化,触发 select 返回。以下是 Linux 下 socket 触发可读、可写、异常事件的核心就绪条件,其中异常就绪为选学内容。

读就绪(socket 触发可读事件的条件)

满足以下任一条件,socket 即处于读就绪状态,对其执行无阻塞读操作可直接获取结果:

  1. socket 内核接收缓冲区中的数据字节数,大于等于低水位标记 SO_RCVLOWAT(Linux 系统默认值为 1 字节),此时无阻塞读会成功,且返回值大于 0;
  2. TCP 通信中,对端主动关闭连接(发送 FIN 包),此时对该 socket 执行读操作,会直接返回 0(表示读到流结束);
  3. 处于 listen 状态的监听 socket上有新的客户端连接请求,此时调用 accept () 可无阻塞获取新的连接描述符;
  4. socket 上存在未处理的错误,此时对其执行无阻塞读操作会返回 - 1,且系统全局变量 errno 会置为对应的错误码。

写就绪(socket 触发可写事件的条件)

满足以下任一条件,socket 即处于写就绪状态,对其执行无阻塞写操作可直接获取结果:

  1. socket 内核发送缓冲区的可用空闲字节数(剩余可写入空间),大于等于低水位标记 SO_SNDLOWAT(Linux 系统默认值为 1024 字节),此时无阻塞写会成功,且返回值大于 0;
  2. socket 的写端被关闭(如调用 shutdown (fd, SHUT_WR) 关闭写端,或 TCP 对端关闭连接),此时对该 socket 执行写操作会触发SIGPIPE 信号,若忽略该信号,写操作会返回 - 1;
  3. 对 socket 执行非阻塞 connect ()后,连接成功建立或连接失败,此时 socket 会触发写就绪,可通过 getsockopt () 获取连接结果;
  4. socket 上存在未处理的错误,此时对其执行无阻塞写操作会返回 - 1,且系统全局变量 errno 会置为对应的错误码。

异常就绪(选学)

socket 触发异常就绪的核心条件为:socket 接收到 TCP 带外数据(紧急数据)

  • 带外数据与 TCP 协议的紧急模式相关,TCP 协议头中的紧急指针字段用于标识带外数据的位置,内核会为带外数据开辟独立的缓冲区,不会与普通数据混存;
  • 带外数据的传输优先级高于普通数据,触发异常就绪后,可通过 recv ()/recvmsg () 的 MSG_OOB 标志读取带外数据,同学们可课后自行收集相关资料深入学习。

5. select的特点

select 作为经典的 IO 多路复用实现,其设计特性决定了使用上的核心特点,同时也存在明显的固有局限性,核心特点如下:

  1. 可监视的文件描述符数量存在硬限制

    select 能监视的最大 fd 数量,由fd_set的底层大小决定,而fd_set的容量由系统宏FD_SETSIZE固定定义(与sizeof(fd_set)一一对应)。例如服务器上sizeof(fd_set)=512字节,每 1 位对应 1 个 fd,则最大可监视512*8=4096个文件描述符;常见 Linux 系统默认FD_SETSIZE=1024(即sizeof(fd_set)=128字节),默认最大监视 1024 个 fd。

    该限制为内核级硬限制,若需调整需重新编译内核,开发中一般不建议修改,这也是 select 在高并发场景下的主要缺陷。

  2. 必须额外维护独立数据结构保存待监视的 fd

    由于 select 返回后,内核会清空 fd_set 中无事件发生的 fd 对应位,仅保留触发事件的 fd 位,导致原监视集合被覆盖无法复用。因此实际开发中,需在将 fd 加入 select 监视集合的同时,额外使用一个独立的数组(如 int 数组)保存所有待监视的 fd,该数组的核心作用有两点:

    • 作为 select 返回后的检测源:select 返回后,遍历该数组,结合FD_ISSET逐个判断数组中的 fd 是否在改写后的 fd_set 中,以此确定具体哪个 fd 触发了事件,避免盲目遍历所有可能的 fd,提升检测效率;
    • 作为重新初始化监视集合的数据源:每次调用 select 前,需先执行FD_ZERO清空 fd_set,再遍历该数组,通过FD_SET将所有待监视 fd 重新加入集合;同时遍历数组的过程中,可同步获取待监视 fd 的最大值maxfd,为 select 的第一个参数nfdsmaxfd+1)提供准确值。

延伸使用要点

正因为 select 存在 “需重新初始化监视集合” 的特点,每次调用 select 都需要执行 FD_ZERO、FD_SET 的初始化操作,且需遍历数组获取 maxfd,这些操作会随着待监视 fd 数量的增加带来额外的性能开销,这也是 select 在高并发场景下性能下降的原因之一。

6. select缺点

select 虽实现了 IO 多路复用,但受限于设计年代的技术背景,存在多处固有缺陷,使其在高并发网络场景下性能表现不佳,核心缺点如下:

  • 接口使用繁琐,需手动重复初始化 fd 集合:因 select 返回后内核会覆盖重写 fd_set,每次调用 select 前都必须手动执行 FD_ZERO 清空集合 + FD_SET 重新添加待监视 fd,且需额外维护独立数组保存 fd,从开发使用角度来说操作繁琐、代码冗余。
  • fd 集合存在用户态到内核态的频繁拷贝开销:每次调用 select,都需要将用户态的 fd_set 完整拷贝到内核态,待监视的 fd 数量越多,拷贝的数据量越大,系统内存和 CPU 的开销会显著增加。
  • 内核层存在全量 fd 遍历检测开销:内核接收到拷贝后的 fd_set 后,会逐个遍历所有待监视的 fd,检测其是否处于就绪状态,fd 数量越多,遍历的耗时越长,高并发场景下该操作会成为明显的性能瓶颈。
  • 可监视的 fd 数量存在内核级硬限制:能监视的最大 fd 数由系统宏FD_SETSIZE固定定义(默认多为 1024),该限制无法通过简单的代码修改突破,需重新编译内核,完全无法适配高并发的网络服务场景。

7. select使用示例

7.1 检测标准输入输出

只检测标准输入:

#include<stdio.h>#include<unistd.h>#include<sys/select.h>intmain(){fd_set read_fds;FD_ZERO(&read_fds);FD_SET(0,&read_fds);for(;;){printf("> ");fflush(stdout);intret=select(1,&read_fds,NULL,NULL,NULL);if(ret<0){perror("select");continue;}if(FD_ISSET(0,&read_fds)){charbuf[1024]={0};read(0,buf,sizeof(buf)-1);printf("input: %s",buf);}else{printf("error! invaild fd\n");continue;}FD_ZERO(&read_fds);FD_SET(0,&read_fds);}return0;}

说明:

  • 当只检测文件描述符0(标准输入)时,因为输入条件只有在你有输入信息的时候,才成立,所以如果一直不输入,就会产生超时信息。

7.2 使用select实现网络消息传送

  1. main函数创建SelectServer类型的智能指针svr

构造函数

  1. 创建TcpSocket套接字
  2. 将存储文件描述符的数组清空
  1. svr调用SelectServer类中的Start函数
  1. 创建select函数需要的变量,fd_set rfds
  2. 使用FD_ZERO函数将rfds清空
  3. select函数的第一个参数需要放置最大文件描述符+1,所以要判断并存放最大文件描述符
  4. 调用select函数,timeout使用nullptr(阻塞模式)
  5. 跟据select函数的返回值n来判断是否可以调用后续处理函数
  1. 当n>0时表示有事件就绪,调用Dispatcher事件派发器函数
  1. 找到已经就绪的文件描述符判断是连接就绪还是普通读事件就绪,并执行不同的后续函数
  1. 如果是连接就绪,调用Accepter连接管理器函数,添加连接;如果连接超过限制,则关闭连接请求
  2. 如果是普通事件就绪,调用IO处理器Recver函数。(此处只使用了读事件,功能是将收到的数据进行打印)

代码测试指令

make # 编译

./selectserver 8080 # 开启服务端

telnet 127.0.0.1 8080 # 本机客户端测试,新开终端;连接成功后输入任意数据,即可在服务端回显
【免费】linux网络-多路转接select的使用资源-CSDN下载

SelectServer.hpp

#pragmaonce#include<iostream>#include<memory>#include<unistd.h>#include"Socket.hpp"#include"Log.hpp"usingnamespaceSocketModule;usingnamespaceLogModule;classSelectServer{conststaticintsize=sizeof(fd_set)*8;conststaticintdefaultfd=-1;public:SelectServer(intport):_listensock(std::make_unique<TcpSocket>()),_isrunning(false){_listensock->BuildTcpSocketMethod(port);for(inti=0;i<size;i++){_fd_array[i]=defaultfd;}_fd_array[0]=_listensock->Fd();}voidStart(){_isrunning=true;while(_isrunning){// 因为:listensockfd 也是一个fd, 进程怎么知道listenfd上面有新连接到来呢?// auto res = _listensock->Accept(); // 我们在select 这里,可以进行accpet马?// 将listencoskfd添加到select内部, 让OS帮我关心listensockfd上面的读事件fd_set rfds;// 定义 读 fds集合FD_ZERO(&rfds);// 清空fdsintmaxfd=defaultfd;for(inti=0;i<size;i++){if(_fd_array[i]==defaultfd)continue;// 1.每次select之前,都要对rfds进行重置FD_SET(_fd_array[i],&rfds);// 有没有设置到内核中 ? 1 or 0// 2.最大fd,一定是变化的if(maxfd<_fd_array[i]){maxfd=_fd_array[i];// 更新出最大fd}}PrintFd();// struct timeval timeout = {2, 0};// select 返回之后,你怎么还知道哪些fd需要被添加到rfds,让select关心呢?// 所以:select要进行完整的设计,需要借助一个辅助数组!保存服务器历史获取过的所有的fdintn=select(maxfd+1,&rfds,nullptr,nullptr,nullptr);switch(n){case-1:LOG(LogLevel::ERROR)<<"select error";break;case0:LOG(LogLevel::INFO)<<"time out ...";break;default:LOG(LogLevel::DEBUG)<<"有事件就绪了..., n:"<<n;Dispatcher(rfds);break;}}_isrunning=false;}// 事件派发器voidDispatcher(fd_set&rfds){// 不仅仅是新连接到来,也包括读事件就绪 // 指定的文件描述符,在rfds里面,就证明该fd就绪了for(inti=0;i<size;i++){if(_fd_array[i]==defaultfd)continue;// fd合法,不一定就绪if(FD_ISSET(_fd_array[i],&rfds)){// fd_array[i] 上面一定是读就绪了// listensockfd 新连接到来,也是读事件就绪// sockfd 数据到来,读事件就绪if(_fd_array[i]==_listensock->Fd()){// listensockfd 新连接到来Accepter();}else{// 普通的事件就绪Recver(_fd_array[i],i);}}}}// 链接管理器:将新连接的文件描述符,添加到_fd_array数组中voidAccepter(){InetAddr client;intsockfd=_listensock->Accept(&client);// accept会不会阻塞if(sockfd>=0){// 获取新连接到来成功,然后呢??能不能直接read/recv?,coskfd是否读就绪,我们不清楚// 只有谁最清楚?未来sockfd上是否有事件就绪?答:select!// 将新的sockfd,托管给select!// 如何托管?将新的fd放入辅助数组!!LOG(LogLevel::INFO)<<"get a new link, sockfd: "<<sockfd<<", client is: "<<client.StringAddr();intpos=0;for(;pos<size;pos++){if(_fd_array[pos]==defaultfd)break;}if(pos==size){LOG(LogLevel::WARNING)<<"select server full";close(sockfd);}else{_fd_array[pos]=sockfd;}}}// IO处理器voidRecver(intfd,intpos){charbuffer[1024];// 我在这里读取的时候,会不会阻塞??ssize_t n=recv(fd,buffer,sizeof(buffer)-1,0);// recv写的时候有bug吗?if(n>0){buffer[n]=0;std::cout<<"client say@ "<<buffer<<std::endl;}elseif(n==0){LOG(LogLevel::INFO)<<"client quit...";// 1.不要让select在关心这个fd了_fd_array[pos]=defaultfd;// 2.关闭fdclose(fd);}else{LOG(LogLevel::ERROR)<<"recv error";// 1.不要让select在关心这个fd了_fd_array[pos]=defaultfd;// 2.关闭fdclose(fd);}}voidPrintFd(){std::cout<<"_fd_array[]: ";for(inti=0;i<size;i++){if(_fd_array[i]==defaultfd)continue;std::cout<<_fd_array[i]<<" ";}std::cout<<"\r\n";}voidStop(){_isrunning=false;}~SelectServer(){}private:std::unique_ptr<Socket>_listensock;bool_isrunning;int_fd_array[size];};

Main.cc

#include"SelectServer.hpp"intmain(intargc,char*argv[]){if(argc!=2){std::cout<<"Usage: "<<argv[0]<<" port"<<std::endl;exit(USAGE_ERR);}uint16_tport=std::stoi(argv[1]);std::unique_ptr<SelectServer>svr=std::make_unique<SelectServer>(port);svr->Start();return0;}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/14 13:23:58

【开题答辩全过程】以 基于SSM的电子书店管理系统设计与实现为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人&#xff0c;语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

作者头像 李华
网站建设 2026/4/23 11:38:45

【航空旅客满意度大数据分析平台】(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码

【航空旅客满意度大数据分析平台】(设计源文件万字报告讲解)&#xff08;支持资料、图片参考_相关定制&#xff09;_文章底部可以扫码 项目亮点&#xff1a; ✅ 完整的大数据分析项目&#xff0c;涵盖数据采集、清洗、存储、分析、可视化全流程 ✅ 基于HadoopHiveSqoopSpring B…

作者头像 李华
网站建设 2026/4/23 11:38:39

计算机小程序毕设实战-基于springboot+小程序的温州博物馆小程序的设计与实现基于Springboot+Uniapp的博物馆预约微信小程序【完整源码+LW+部署说明+演示视频,全bao一条龙等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/4/23 11:34:55

计算机毕业设计springboot学生作业管理系统 基于Spring Boot的在线作业提交与评阅平台 高校课程作业数字化管理平台的设计与实现

计算机毕业设计springboot学生作业管理系统bcgch &#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。 本系统采用Java作为开发语言&#xff0c;以Spring Boot框架为核心技术支撑&am…

作者头像 李华
网站建设 2026/4/23 11:36:27

波在光纤中传播,变折射率光纤解析

comsol波在光纤中得传播&#xff0c;可变折射率光纤 光纤通信系统的性能很大程度上取决于光在纤芯中的传输特性。对于渐变折射率光纤而言&#xff0c;其纤芯折射率呈现非均匀分布&#xff0c;这种结构能有效减小模式色散。在COMSOL中实现这类仿真时&#xff0c;有个特别有意思…

作者头像 李华
网站建设 2026/4/16 10:59:04

MongoDB索引类型详解与管理指南

MongoDB索引&#xff1a;核心类型与管理实战 MongoDB索引为用户提供了一种高效查询数据的方式。在没有索引的情况下查询数据&#xff0c;查询将不得不搜索数据库中的所有记录以找到匹配查询的数据。在MongoDB中&#xff0c;没有索引的查询称为集合扫描。集合扫描会导致各种性能…

作者头像 李华