深入理解网络非阻塞 I/O:NIO

🔭 嗨,您好 👋 我是 vnjohn,在互联网企业担任 Java 开发,CSDN 优质创作者
📖 推荐专栏:Spring、MySQL、Nacos、Java,后续其他专栏会持续优化更新迭代
🌲文章所在专栏:网络 I/O
🤔 我当前正在学习微服务领域、云原生领域、消息中间件等架构、原理知识
💬 向我询问任何您想要的东西,ID:vnjohn
🔥觉得博主文章写的还 OK,能够帮助到您的,感谢三连支持博客🙏
😄 代词: vnjohn
⚡ 有趣的事实:音乐、跑步、电影、游戏
目录
- 前言
- 非阻塞式 I/O 模型
- 图解分析
- 源码实践
- Socket 服务端代码
- Socket 客户端代码
- 流程说明
- configureBlocking
- 客户端连接
- C10K 问题
- 源码
- 流程分析
- 错误排查
- BIO vs NIO
- 阻塞的套接字函数调用
- 两者肉眼可见区别
- NIO 为什么速度慢?
- 总结
前言
Unix/Linux 下可用的 I/O 模型有以下五种:
- 阻塞式 I/O
- 非阻塞式 I/O
- I/O 复用(select、poll)
- 信号驱动式 I/O(SIGIO)
- 异步 I/O
在 Linux 中操作内核时,所有的无非三种操作,分别是输入、输出、报错输出
0-输入
1-输出
2-报错输出
一个输入操作通常包括两个不同的阶段:
- 等待数据准备好
- 从内核向进程复制数据
对于一个套接字(Socket)的输入操作,第一步通常涉及等待数据从网络中;当所等待分组到达时,它被复制到内核中的某个缓冲区,第二步就是把数据从内核缓冲区复制到应用进程缓冲区
非阻塞式 I/O 模型
进程把一个套接字设置成非阻塞是在通知内核:当所请求的 I/O 操作非得把本进程投入睡眠才能完成时,不要把本进程投入睡眠,而是返回一个错误

前三次调用 recvfrom 时没有数据可返回,因此内核转而立即返回一个 EWOULDBLOCK 错误;第四次调用 recvfrom 时已有一个数据报准备好,它被复制到应用进程缓冲区,于是 recvfrom 成功返回,接着处理数据
EWOULDBLOCK:E 是 Error,WOULD BLOCK 是可能会被阻塞的意思
表示当前没有数据可读或没有缓冲区可写,需要等待下一次读写事件再尝试读写,非阻塞模式下可以继续尝试读写
当一个应用进程像这样对一个非阻塞描述符,循环调用 recvfrom 时,我们称之为轮询(Polling);应用进程持续轮询内核,以查看某个操作是否就绪;这么做往往耗费大量 CPU 时间,不过这种模型偶尔也会遇到,通常是在专门提供某一种功能的系统中才有.
图解分析

- 当有新的连接进来时,主线程负责执行 accept 连接客户端,clone 出一个子进程新的 sockfd 去 accept/read,等待其他客户端连接时是非阻塞的,读取客户端数据也是非阻塞的,只是返回给客户端是 EWOULDBLOCK 状态
- NIO 采用的处理方式:主线程阻塞去等待客户端连接,以非阻塞的方式为每个客户端读取数据
NIO 核心的参数设置:configureBlocking(false)
源码实践
Socket 服务端代码
package org.vnjohn.nio.server;import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.LinkedList;/*** @author vnjohn* @since 2023/12/2*/
public class SocketNIOServer {public static void main(String[] args) throws Exception {// 链表集合存放所有的 socket client 实例LinkedList<SocketChannel> clients = new LinkedList<>();// 服务端开启监听:接受客户端ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.bind(new InetSocketAddress(8090));
// serverSocketChannel.configureBlocking(true);// OS NONBLOCKING 只让接受客户端并不阻塞serverSocketChannel.configureBlocking(false);System.out.println("step1: new ServerSocketChannel(8090)");while (true) {// 接受客户端的连接Thread.sleep(1000);// 不会阻塞// 在操作系统中返回:-1,在 Java 程序中返回 NULLSocketChannel socketChannel = serverSocketChannel.accept();// accept 调用内核:// 1、没有客户端连接进来,返回值:在 BIO 时一直卡/阻塞着,但是在 NIO ,不卡着,返回 -1,NULL// 如果来客户端的连接,accept 返回的是这个客户端的 fd5if (socketChannel == null) {System.out.println(".....");} else {// 服务端 listen socket <连接请求三次握手后,通过 accept 得到连接的 sockfd>,连接 socket <连接后读写使用>socketChannel.configureBlocking(false);int port = socketChannel.socket().getPort();System.out.println("step2:client\t" + port);clients.add(socketChannel);}// 堆内:allocate -> HeapByteBuffer// 堆外:allocateDirect -> DirectByteBufferByteBuffer buffer = ByteBuffer.allocateDirect(4096);// 遍历已经链接进来的客户端读写数据// 这里采用串行化的方式进行接收,可以更改为采用 BIO 方式,一个客户端抛出一个线程进行接收处理for (SocketChannel client : clients) {// 不会阻塞,返回:> 0、-1、0int num = client.read(buffer);if (num > 0) {buffer.flip();byte[] bufferByte = new byte[buffer.limit()];buffer.get(bufferByte);String b = new String(bufferByte);System.out.println(client.socket().getPort() + " : " + b);buffer.clear();}}}}
}
Socket 客户端代码
package org.vnjohn.nio.client;import java.io.*;
import java.net.Socket;/*** @author vnjohn* @since 2023/12/2*/
public class SocketNIOClient {public static void main(String[] args) {try {Socket client = new Socket("172.16.249.10", 8090);client.setSendBufferSize(20);client.setTcpNoDelay(true);// false 优化,true 不优化client.setOOBInline(false);OutputStream out = client.getOutputStream();InputStream in = System.in;BufferedReader reader = new BufferedReader(new InputStreamReader(in));while (true) {String line = reader.readLine();if (line != null) {byte[] bb = line.getBytes();for (byte b : bb) {out.write(b);}}}} catch (IOException e) {e.printStackTrace();}}
}
流程说明
172.16.249.10 是之前作为 node1 节点所在 IP
将以上两个 java 源文件上传到 node1 虚拟节点上,所在目录:/opt/java
1、在虚拟节点上安装好 Java 环境
2、将源文件所在的 package 包名,通过 vim 命令将 package 包名删除首行.
3、将 Java 源文件进行编译为 .class 文件 > javac SocketServer.java、javac SocketClient.java
configureBlocking
追踪应用程序与操作系统中的交互信息
cd /opt/java
strace -ff -o out java SocketNIOServer
正常的流程都是先 socket()、bind()、listen()、accept()
关于这四个函数的详细介绍可以阅读博主「网络 I/O」专栏中的另外一篇博文:
深入理解网络阻塞 I/O:BIO
先设置 configureBlocking = true,代表当前设置 SocketChannel 是阻塞式运行的
若设置 configureBlocking = true 时,观察追踪到操作系统的操作信息,能够详细看到我们的操作系统基于内核是一个阻塞态:
accept(4,:说明当前程序是阻塞运行的

再设置 configureBlocking = false,代表当前设置 SocketChannel 是非阻塞式运行的
accept(4, 0x7f8c0d4ee0, [28]) = -1 EAGAIN (Resource temporarily unavailable)
在操作系统侧,返回的是 -1,说明是非阻塞运行的

按照代码执行的逻辑来看,控制台会一直打印:…,说明当前 SocketChannel 是非阻塞运行的.

说明通过设置 configureBlocking = false 就可以实现不阻塞,若没有客户端进行连接在操作系统中返回的是 -1,而在 Java 中返回是 null
客户端连接
在 node2 节点:172.16.249.10,运行 SocketNIOClient 程序代码
1、移除首行 package 包名
2、cd /opt/java,编译 Java 源文件:javac SocketNIOClient.java
3、运行 Java 可执行程序:java SocketNIOClient

当客户端连接以后,就会在 out.pid 文件中分配一个文件描述符给到当前这个客户端,如下:
accept(4, {sa_family=AF_INET6, sin6_port=htons(32972), inet_pton(AF_INET6, “::ffff:172.16.249.11”, &sin6_a ddr), sin6_flowinfo=htonl(0), sin6_scope_id=0}, [28]) =
5
当在客户端中发送数据时,比如:123456,在 out.pid 文件中会触发系统调用 R/W 读写


在 out.pid 文件中,会发现大量的 EAGAIN 字眼,代表当次资源暂不可用,这个操作可能等下次重试后可用
EAGAIN 官方定义:“Resource temporarily unavailable.” The call might work if you try again later. The macro EWOULDBLOCK is another name for EAGAIN; they are always the same in the GNU C Library.
EWOULDBLOCK 官方定义:“Operation would block.” In the GNU C Library, this is another name for EAGAIN (above). The values are always the same, on every operating system.
两者都代表含义是一样的,在 GUN C 库中,EWOULDBLOCK 的另外一个名称称之为 EAGAIN
C10K 问题
当 C10K 出现时,若有 1W 个客户端建立连接,在 BIO 时需要抛出 1W 个线程,此时就会造成资源消耗越多,任务调度就会变得越多,内核态用户态之间的切换也会越多
当在 NIO 时进行使用,在内核中又会出现另外一个问题,接着向下分析
源码
package org.vnjohn.nio.client;import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
import java.util.LinkedList;/*** @author vnjohn* @since 2023/12/2*/
public class C10KClient {public static void main(String[] args) {LinkedList<SocketChannel> clients = new LinkedList<>();InetSocketAddress serverAddr = new InetSocketAddress("172.16.249.10", 8090);// 端口号的问题:65535for (int i = 10000; i < 25000; i++) {try {SocketChannel client = SocketChannel.open();// 172.16.249.10:10000 172.16.249.10:8090client.bind(new InetSocketAddress("1172.16.249.10", i));client.connect(serverAddr);clients.add(client);} catch (IOException e) {e.printStackTrace();}}for (int i = 25000; i < 50000; i++) {try {SocketChannel client = SocketChannel.open();// 172.16.249.10:25000 172.16.249.10:8090client.bind(new InetSocketAddress("172.16.249.10", i));client.connect(serverAddr);clients.add(client);} catch (IOException e) {e.printStackTrace();}}System.out.println("total clients:"+ clients.size());try {System.in.read();} catch (IOException e) {e.printStackTrace();}}
}
修改 SocketNIOServer 服务端代码,将两行代码注释

其目的是为了让服务端能够快速与客户端建立连接,而不是 1s 处理一个客户端连接,以便我们在 Linux 中来分析最大连接数
流程分析
1、先将 SocketNIOServer 休整的代码,重新编译执行:strace -ff -o out java SocketNIOServer
2、先 C10K 问题的客户端代码编译为 class 文件,然后通过 java 运行
3、接着观察几个情况
首先是观察 socket、TCP/IP 条目信息,可以看到有很多条类目,客户端->服务端之间的二元组信息「IP:端口」

观察服务端接收客户端连接的情况,是否保持正常

客户端在满足条件时,bind 客户端是处于正常状态的,而一旦超过了内核所配置的最大的大小以后,会发现会出现一个 Error 错误信息
java.net.SocketException: Too many open files

从异常信息上来看,意思是文件描述符超过了限制的大小.
错误排查
该错误是可以被修复的,但是修复了意义上不大,这种非阻塞运行的方式对内核这块的资源损耗还是很大的
java.net.SocketException: Too many open files
可打开的文件描述符大小是受内核配置的限制的,但它也是支持可配置的,查看命令:ulimit -a 或 ulimit -n

open files = 1024:一个进程最多可以打开 1024 个文件描述符
可通过 ulimit -SHn Xxx 命令来临时调整大小
- S:soft 软
- H:hard 硬
- n:文件描述符
调整后可以通过:ulimit -n 再查看修改后的大小
在内核中会根据上限的物理内存估算出一个总文件描述符大小
[root@node1 java]# cat /proc/sys/fs/file-max
146266
BIO vs NIO
套接字的默认状态是阻塞的,当你未设置参数 configureBlocking 时,这就意味着当发出一个不能立即完成的套接字调用时,其进程被投入睡眠状态,等待相应操作完成
阻塞的套接字函数调用
可能阻塞的套接字调用可分为以下四类:
- 输入操作,包括:read、readv、recv、recvfrom、recvmsg 五个函数
既然 TCP 是字节流协议,该进程的唤醒只要有一些数据到达,这些数据即可能是单个字节,也可以是一个完整的 TCP 分节中的数据,若想等到某个固定的数目的数据可读为止,需要调用的 readn 函数或者指定 MSG_WAITALL 标志
既然 UDP 是数据报协议,若一个阻塞的 UDP 套接字的接收缓冲区为空,对它调用输入函数的进程将被投入睡眠,直到有 UDP 数据报到达
若某个进程对一个阻塞的 TCP 套接字调用这些输入函数之一,而且该套接字的接收缓冲区中没有数据可读,该进程将被投入睡眠,直到有一些数据到达
对于非阻塞的套接字,若输入操作不能被满足(对于 TCP 套接字即至少有一个字节的数据可读,对于 UDP 套接字即有一个完整的数据报可读)响应调用将立即返回一个 EWOULDBLOCK |EAGAIN 错误
- 输出操作,包括:write、writev、send、sendto、sendmsg 五个函数,对于一个 TCP 套接字,内核将从应用进程的缓冲区到该套接字的发送缓冲区复制数据
对于阻塞的套接字,若其发送缓冲区没有空间,进程将投入睡眠,直到有空间为止
对于一个非阻塞的 TCP 套接字,若其发送缓冲区中根据没有空间,输出函数调用将立即返回一个 EWOULDBLOCK |EAGAIN 错误,若其发送缓冲区有一些空间,返回值将是内核能够复制到该缓冲区中的字节数,该字节数也称之为不足计数
- 接受外来连接,即 accept 函数
若对一个阻塞的套接字调用 accept 函数,并且尚无新的连接到达,调用进程将被投入睡眠
若对一个非阻塞的套接字调用 accept 函数,并且尚无新的连接到达,accept 调用将立即返回一个 EWOULDBLOCK |EAGAIN 错误
- 发起外出连接,即用于 TCP 的 connect 函数,connect 同样可用于 UDP,不过它不能使一个 “真正” 连接建立起来,它只是使内核保存对端的 IP 地址、端口号
TCP 连接建立涉及到一个三次握手过程,而且 connect 函数一直要等到客户收到对于自己的 SYN -> ACK 为止才返回,这意味着 TCP 的每个 connect 总会阻塞其调用进程至少有一个到服务器的 RT 时间
若对于一个非阻塞的 TCP 套接字调用 connect,并且连接不会立即建立,连接的建立能照样发起(臂如:送出 TCP 三次握手的第一个分组)不过会返回一个 EINPROGRESS 错误
两者肉眼可见区别
BIO 会在主线程阻塞式的去接受一个 socket 客户端的连接,在操作系统内核中不会返回 -1,一直卡着不动: accept(4,
客户端读取数据时也是阻塞式的,没有 -1、0、>0,所以在使用 BIO 时要单独开辟新的线程去专门读取来自客户端的数据,而主线程只是阻塞式的负责接收来自客户端的连接;当客户端的连接很多时,线程的数量就会很多,线程之间的切换和任务频繁的调度就显而易见了.
主线程阻塞式接收客户端的连接:accept
每一个客户端有一个子线程负责去接收客户端的数据:read、readv
在 NIO 中,可配置式的支持配置阻塞或非阻塞,通过:configureBlocking 参数来进行配置,true:阻塞、false:非阻塞
- accept 调用了操作系统内核有以下两种情况
当没有客户端连接进来时,在 BIO 时会一直阻塞着,但是在 NIO 时,操作系统内核层面会返回 -1,而在 Java 应用程序中会返回 NULL
当有客户端连接进来时,accept 返回的是分配给这个客户端的文件描述符 socketfd,Java 中返回的是一个 SocketChannel 对象
- socket 对象分为以下两种:
服务端 ServerSocketChannel:连接请求三次握手完成后,该对象可以通过 accept 方法获取到客户端的 socket -> SocketChannel
客户端 SocketChannel:客户端与服务端之间建立好连接以后,通过该 socket 来负责读写数据使用
两者 socket 都需要配置为非阻塞式运行,才能够保证连接、读取数据都是非阻塞运行的!!!
NIO 为什么速度慢?
NIO 优势在于可以通过一个或多个线程来解决 N 个 IO 连接阻塞的问题
它的问题在于:在本篇博文通过 C10K 问题来模拟当其到达了 1W 个客户端,但是每次进行读取时都会循环一次,会带有 O(n) 复杂度的 recv 系统函数调用,可能在这些客户端中只有几个客户端是有数据的额,所以在这期间会有很多系统函数调用是没有意义的,浪费的只是系统的资源以及给操作系统内核带来没必要的压力
所以,从 NIO 来看,应该考虑的是只做那么有必要的事,没必要的事应该尽量的去避免它发生
总结
该篇博文主要介绍的是 I/O 模型中的非阻塞 I/O -> NIO,简要分析了 NIO 非阻塞式 I/O 简要的模型,通过图解分析的方式告知它与 BIO 之间的区别,通过实践代码的方式来分析非阻塞 I/O 在系统调用中所涉及到的流程,同时也介绍了 C10K 问题给非阻塞式 I/O 带来的不利之处,最后介绍了上篇 BIO 博文与 NIO 之间的相关的区别以及 NIO 为什么速度会慢的原因,希望能够得到你的支持,感谢三连
四元组唯一:源 IP、源端口、目标 IP、目标端口
🌟🌟🌟愿你我都能够在寒冬中相互取暖,互相成长,只有不断积累、沉淀自己,后面有机会自然能破冰而行!
博文放在 网络 I/O 专栏里,欢迎订阅,会持续更新!
如果觉得博文不错,关注我 vnjohn,后续会有更多实战、源码、架构干货分享!
推荐专栏:Spring、MySQL,订阅一波不再迷路
大家的「关注❤️ + 点赞👍 + 收藏⭐」就是我创作的最大动力!谢谢大家的支持,我们下文见!
相关文章:
深入理解网络非阻塞 I/O:NIO
🔭 嗨,您好 👋 我是 vnjohn,在互联网企业担任 Java 开发,CSDN 优质创作者 📖 推荐专栏:Spring、MySQL、Nacos、Java,后续其他专栏会持续优化更新迭代 🌲文章所在专栏&…...
Hdoop学习笔记(HDP)-Part.07 安装MySQL
目录 Part.01 关于HDP Part.02 核心组件原理 Part.03 资源规划 Part.04 基础环境配置 Part.05 Yum源配置 Part.06 安装OracleJDK Part.07 安装MySQL Part.08 部署Ambari集群 Part.09 安装OpenLDAP Part.10 创建集群 Part.11 安装Kerberos Part.12 安装HDFS Part.13 安装Ranger …...
[数据结构]HashSet与LinkedHashSet的底层原理学习心得
我们区分list和set集合的标准是三个:有无顺序,可否重复,有无索引。 list的答案是:有顺序,可重复,有索引。这也就是ArrayList和LinkedList的共性 set的答案是:顺序内部再区分,不可以重复…...
使用unity开发Pico程序,场景中锯齿问题
1、问题 使用unity【非HDR】开发Pico程序,场景中锯齿问题,设置了unity的抗锯齿和渲染方式,及悬挂抗锯齿的脚本,都不能很好的解决项目中图片、文字的锯齿问题,通过摸索找到了妥善的方法 1、修改项目中图片的 GenerateMIpMaps 为勾…...
Spring | Spring的基本应用
目录: 1.什么是Spring?2.Spring框架的优点3.Spring的体系结构 (重点★★★) :3.1 Core Container (核心容器) ★★★Beans模块 (★★★) : BeanFactoryCore核心模块 (★★★) : IOCContext上下文模块 (★★★) : ApplicationContextContext-support模块 (★★★)SpE…...
项目开发维护技术文档(梳理总结中)
目录 项目名称——惠誉灵境 一、项目背景 二、架构设计 1.技术栈 2.架构图 3.代码结构 三、模块划分 1.平台首页 2.登录模块 3.系统模块 (1)系统首页 (2)组织架构 (3)权限管控 ①角色管理 (4&am…...
【接口测试】Apifox实用技巧干货分享
前言 不知道有多少人和我有着这样相似的经历:从写程序只要不报错就不测试😊,到写了程序若是有bug就debug甚至写单元测试,然后到了真实开发场景,大哥和你说,你负责的功能模块的所有接口写完要测试一遍无误在…...
车联网架构设计(一)_消息平台的搭建
车联网是物联网的一个主要应用方向,车辆通过连接车联网平台,实时进行消息的交互,平台可以提供车辆远程控制,故障检测,车路协同等各方面的功能。 我在车联网行业从事了很长时间的技术工作,参与了整个车联网…...
(蓝桥杯)1125 第 4 场算法双周赛题解+AC代码(c++/java)
题目一:验题人的生日【算法赛】 验题人的生日【算法赛】 - 蓝桥云课 (lanqiao.cn) 思路: 1.又是偶数,又是质数,那么只有2喽 AC_Code:C #include <iostream> using namespace std; int main() {cout<<2;return 0; …...
也可Adobe Animate
Animate CC 由原Adobe Flash Professional CC 更名得来,2015年12月2日:Adobe 宣布Flash Professional更名为Animate CC,在支持Flash SWF文件的基础上,加入了对HTML5的支持。并在2016年1月份发布新版本的时候,正式更名为…...
【面试HOT200】回溯篇
系列综述: 💞目的:本系列是个人整理为了秋招面试的,整理期间苛求每个知识点,平衡理解简易度与深入程度。 🥰来源:材料主要源于【CodeTopHot300】进行的,每个知识点的修正和深入主要参…...
JVM——内存溢出和内存泄漏
目录 1. 内存溢出和内存泄漏内存泄漏的常见场景解决内存溢出的思路1.发现问题 – Top命令2.发现问题 – VisualVM3.发现问题 – Arthas4.发现问题 – Prometheus Grafana5.发现问题 – 堆内存状况的对比:将指定名称绑定到初始化程序的子对象或元素。简而言之,它们使我们能够从元组或结构中声明多个变量。与引用一样,结构化绑定是现有对象的别名;与引用不同,结构化绑定不必是引用类型(referen…...
Mover Creator 用户界面
1 “开始”对话框 首次打开 Mover Creator 时,出现的第一个页面是“开始”对话框,如下所示。从这里开始,用户可以选择开始设计飞机、武器或发动机。在上述每种情况下,用户都可以创建新模型或编辑现有模型。 1.1 新建模型 如果用…...
『Nginx安全访问控制』利用Nginx实现账号密码认证登录的最佳实践
📣读完这篇文章里你能收获到 如何创建用户账号和密码文件,并生成加密密码配置Nginx的认证模块,实现基于账号密码的登录验证 文章目录 一、创建账号密码文件1. 安装htpasswd工具1.1 CentOS1.2 Ubuntu 二、配置Nginx三、重启Nginx 在Web应用程…...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...
