mysql更新数据需要redo log,然后把数据保存到内存,有系统调用flus保存到磁盘中,是因为系统IO的原因,系统会把数据放到pagecacache中,如果对数据修改会标记为脏,这个标记是对所有的pagecache的,不是单指一个,然后有系统统一的flus到磁盘中;这就是为什么数据库修改数据保存到redo log中有延迟不是直接写入到磁盘中的原因。
说的不准确,知道大概过程
Linux有一个虚拟文件目录,都会挂载到相应的地方,比如dev3这块磁盘挂载到book虚拟目录上,进入book目录,其实就是进入到dev3中,所以就可以扩容,比如要存10T文件存入某个目录,可以把一个磁盘挂载到这个目录上,就可以保存了。
Linux虚拟文件系统,可以创建一个xxx.img文件,然后挂载到虚拟目录上,然后把可执行程序保存到虚拟目录中,实际操作的就是被挂载的xxx.img这块控件,可执行程序的根目录就是xxx.img,然后卸载xxx.img,把xxx.img挂载到其他虚拟目录也一样可以执行里面的程序,相当于一个容器,docket使用类似的原理;
磁盘IO
pagecache作用:内存中运行着各种应用,包括操作系统(kernel),但是应用不是一次性加载到内存中的,内存是分页存储的,每页大小4K,应用从磁盘加载到内存都是一页一页加载的,每个应用都有一个页表,里面记录页的线性地址,线性地址是虚拟的内存地址需要进行转换,通过内存管理单元(mmu)转换成物理地址,如果多个应用读取相同的一个文件,如a.txt,系统调用(IO)会把资源加载到内存中缓存起来,这个就是pagecache,多个应用读取形同的pagecache,应用都有个一个文件描述符(fd),fd中有偏移量(seek),多个应用使用seek来读取相同pagechache互不干扰,做到文件共享,pagecache是一种内存优化,想要减少系统调用,读取文件设计到IO操作,IO操作是系统调用,会产生80中断,也就是软中断运行状态会从用户态切换到内核态,内核态切换到用户态,所以使用pagecache来进行优化,访问相同的pagecache不会进行系统调用,BufferedOutputStream效率高就是这个原因,buffer有一个8k的缓冲区,不会每次都把数据写入到磁盘中,相当于批处理,批处理就会减少80中断,减少系统调用,所以效率高;FileOutputStream的IO操作比较频繁,所以效率不如批处理的BufferedOutputStream;
pagecache的缺点:使用输入流把数据写入磁盘,不是立刻的写入,而是写入内存中的pagecache中,操作系统会维护一个dirty的变量,表示脏,当内存中的数据到阈值时,操作系统会统一的把脏页刷入磁盘中,这要是为什么redis不设置次操作都持久化的时候,会丢数据的原因,当没有把数据刷入到磁盘中时,突然断电,保存在pagecache中的数据会丢失;当内存到大阈值的时候,会先把数据刷新到磁盘中,然后使用LRU算法进行淘汰
NIO:RandomAccessFile raf = new RandomAccessFile(path, "rw")
NIO使用channerl进行数据写入,主要多了一个内存映射,没有有经过系统调用,就可以把数据映射到pagecache中,kernel进行系统调用把pagecache写入到磁盘,所以nio效率更高;
byteBuffer数据保存到磁盘,先把数据从堆内存复制到堆外内存,然后在保存到pagecache中,最后保存到磁盘;
网络IO
TCP:面相连接的,可靠的传输协议,socket是基于tcp协议的,socket是一个四元组(客户端IP_客户端PORT + 服务端IP_服务端PORT)三次握手以后,操作系统回分配资源空间,四元组会在程序中对应一个fd,程序通过fd映射socket找到对应的内存空间,来进行数据交互(服务端启动以后,会监听端口,当有客户端连接进来以后,三次握手以后分配内存空间)
拥塞:简历socket连接时,回发送win窗口大小,用来优化数据交互,服务端端窗口满了,通过win的值,客户端就能知道,满了就是拥塞,客户端会暂停传送数据包,MTU数据包的大小,MSS是数据包中数据的大小;
网络IO变化
BIO模型
ServiceSocket(端口号,back_log)
1.操作系统创建socket会的到一个文件描述符
2.文件描述符bind端口
3.然后监听该端口,等待客户端的连接,accept(fd3,-》client的fd)此时为阻塞状态
BIO在获取客户端连接的时候会阻塞,得到连接以后读取数据也会阻塞,所以因为读取阻塞才要抛出线程,否者下一个连接获取不到,因为线程在读数据主色中,如何一直没有数据,就一直阻塞,所以抛出线程避免下一个客户端获取不到;
public static void main(String[] args) throws Exception {
ServerSocket server = new ServerSocket(9090,20);
System.out.println("step1: new ServerSocket(9090) ");
while (true) {
Socket client = server.accept(); //阻塞1
System.out.println("step2:client\t" + client.getPort());
new Thread(() -> {
InputStream in = null;
try {
in = client.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
while(true){
String dataline = reader.readLine(); //阻塞2
if(null != dataline){
System.out.println(dataline);
}else{
client.close();
break;
}
}
System.out.println("客户端断开");
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
NIO
nio有两个维度,一个是操作系统,nio是非阻塞io,另一个是jvm中,是new io 新的io,底层调用就是操作系统的nio;在获取连接的时候BIO底层是阻塞的(调用accept(ervice的fd,等待client的fd)),一直在阻塞,现在accept(service的fd)如果没有client的fd就会返回-1,jvm把-1转换成null,返回给程序,这就是NIO的原理,BIO效率低就是因为阻塞,获取客户端阻塞,读取数据阻塞,所以才抛出线程;NIO使用channel,设置 ss.configureBlocking(false); 就不会阻塞,也设置client.configureBlocking(false);客户端也不会阻塞,所以效率高;
public static void main(String[] args) throws Exception {
LinkedList<SocketChannel> clients = new LinkedList<>();
ServerSocketChannel ss = ServerSocketChannel.open();
ss.bind(new InetSocketAddress(9090));
ss.configureBlocking(false); //重点 OS NONBLOCKING!!!
ss.setOption(StandardSocketOptions.TCP_NODELAY, false);
// StandardSocketOptions.TCP_NODELAY
// StandardSocketOptions.SO_KEEPALIVE
// StandardSocketOptions.SO_LINGER
// StandardSocketOptions.SO_RCVBUF
// StandardSocketOptions.SO_SNDBUF
// StandardSocketOptions.SO_REUSEADDR
while (true) {
Thread.sleep(1000);
SocketChannel client = ss.accept(); //不会阻塞? -1NULL
if (client == null) {
System.out.println("null.....");
} else {
client.configureBlocking(false);
int port = client.socket().getPort();
System.out.println("client...port: " + port);
clients.add(client);
}
ByteBuffer buffer = ByteBuffer.allocateDirect(4096); //可以在堆里 堆外
for (SocketChannel c : clients) { //串行化!!!! 多线程!!
int num = c.read(buffer); // >0 -1 0 //不会阻塞
if (num > 0) {
buffer.flip();
byte[] aaa = new byte[buffer.limit()];
buffer.get(aaa);
String b = new String(aaa);
System.out.println(c.socket().getPort() + " : " + b);
buffer.clear();
}
}
}
}
来源:oschina
链接:https://my.oschina.net/u/3141521/blog/4350991