MySQL Buffer Pool 详解

news2024/11/15 20:07:18

1. Buffer Pool 概述

Buffer Pool 到底是什么?从字面上看是缓存池的意思,没错,它其实也就是缓存池的意思。它是MySQL当中至关重要的一个组件,可以这么说,MySQL的所有的增删改的操作都是在 Buffer Pool 中执行的。

但是数据不是在磁盘中的吗?怎么会和缓存池又有什么关系呢?大家都知道MySQL数据其实是放在磁盘里面的,从磁盘里面查询数据那肯定需要IO,并且数据库并不知道它将要查找的数据是磁盘的哪个位置,所以这就需要进行随机IO,这样势必会很影响性能。所以一定是先把数据从磁盘中取出,然后放在内存中,下次查询直接从内存中来取,这也就是本文所要记录的Buffer Pool组件。

本篇文章,会详细的介绍 Buffer Pool 的内存结构,让大家彻底明白这里面的每一步执行流程。我们先来看一个总体的流程图,从数据在磁盘中被加载到缓存池中,然后经过一系列的操作最终又被刷入到磁盘的一个过程,都经历了哪些事情。

image-20230228231205608

看完这张图后,大家是不是感觉很熟悉,这不就是执行更新语句的大致流程嘛。(图画的有点简陋,binlog 也是需要经过page cache的,然后在刷盘)由上可见,数据库一系列增删改的操作都是在Buffer Pool中直接完成的,然后在进行脏数据的刷盘。

2. Buffer Pool 内存数据结构

从上文我们可以知道在执行增删改操作的时候数据是会被加载到Buffer Pool 中的,那这些数据到底是以什么形式加载进来的呢?接下来我们就先来了解下相关的内存数据结构。

2.1 数据页

我们在数据库操作的数据都是以表 + 行的方式,而表 + 行仅仅是逻辑上的概念,MySQL并不会像我们一样去操作行数据,而是抽象出来一个一个的数据页概念。磁盘中存放了很多数据页,每个数据页里面存放了很多行数据。MySQL在执行增删改首先会定位到这条数据所在的数据页,然后会将数据所在的数据页加载到 Buffer Pool 中。

每个数据页的大小默认是 16KB,这些参数都是可以调整的。但是建议使用默认的就好,毕竟 MySQL能做到极致的都已经做了。

image-20230301223708090

2.2 缓存页

当数据页被加载到缓冲池中后,Buffer Pool 中也有叫缓存页的概念与其一一对应,大小同样是 16KB,但是 MySQL还为每个缓存也页开辟额外的一些空间,用来描述对应的缓存页的一些信息,例如:数据页所属的表空间,数据页号,这些描述数据块会额外占据一些空间。

那这些缓存页是什么时候创建的呢?实际上当 MySQL启动的时候,就会初始化 Buffer Pool,这个时候 MySQL 会根据系统中设置的 innodb_buffer_pool_size 大小去内存中申请一块连续的内存空间,这个内存区域比配置的值稍微大一些,因为描述数据也是占用一定的内存空间的。当在内存区域申请完毕之后, MySQL会将内存区域划分为一个个的缓存页和对应的描述数据。

image-20230301224321057

3. Page 和 链表

上面说了每个数据页会被加载到一个缓存页中,那MySQL是怎么区分哪些缓存页是空闲的状态,是可以用来存放数据页的呢?这时候就需要说到Buffer Pool中的三种Page和链表了。

3.1 Page

首先我们先来了解下Page的概念,在Buffer Pool中有如下三种Page,用来区分缓存页的状态。

  • Free Page(空闲页)

    表示此Page 未被使用,位于 Free 链表。

  • Clean Page(干净页)

    此Page 已被使用,但是页面未发生修改,位于LRU 链表。

  • Dirty Page(脏页)

    此Page 已被使用,页面已经被修改,其数据和磁盘上的数据已经不一致。当脏页上的数据写入磁盘后,内存数据和磁盘数据一致,那么该Page 就变成了干净页。脏页 同时存在于LRU 链表和Flush 链表。

3.2 链表

3.2.1 Free 链表

什么是free链表呢?它是 MySQL 为 Buffer Pool 设计的一个双向链表,它的作用就是用来保存空闲缓存页的描述块,或者可以这么理解,每个空闲缓存页的描述数据块组成了一个双向链表,这个链表就是free链表。

另外 free 链表还会有一个基础节点,它会引用该链表的头结点和尾结点,还会记录节点的个数(也就是可用的空闲的缓存页的个数)。

大致如下所示:

image-20230301225816645

当加载数据页到缓存池中的时候, MySQL会从 free 链表中获取一个描述数据的信息,根据描述节点的信息拿到其对应的缓存页,然后将数据页信息放到该缓存页中,同时将链表中的该描述数据的节点移除。这就是数据页被读取到 Buffer Pool 中的缓存页的过程。

但 MySQL是怎么知道哪些数据页已经被缓存了,哪些没有被缓存呢。实际上数据库中还有一个哈希表结构,它通过存储表空间号 + 数据页号作为key,缓存页对应的地址作为其value,这样数据页在加载的时候就会通过哈希表中的key来确定数据页是否被缓存了。

image-20230301230753967

3.2.2 Flush 链表

上面我们已经知道Free 链表是用来保存空闲缓存页的,那MySQL是如何判断那些缓存页是脏页,从而进一步进行刷盘的呢?

针对这个问题,MySQL设计出了 Flush 链表,它的作用就是记录被修改过的脏数据所在的缓存页对应的描述数据。结构和 Free 链表是相同的。

另外,当某个脏页被刷新到磁盘后,其空间就腾出来了,然后又会跑到 Free 链表中了。

3.2.2 LRU链表

如果系统一直在进行数据库的增删改操作,数据库内部的基本流程就是:

image-20230301231659403

我们这里用 redis 做类比,以便更好的帮助大家明白其原理。

如果 redis 的内存不够使用了,是不是有相应的淘汰策略?最基本的准则就是淘汰掉不经常使用到的key。Buffer Pool 也类似,它也会有内存不够使用的情况,它是通过 LRU 链表来维护的。LRU 即 Least Recently Uesd(最近最少使用)。

LRU相信大家应该都了解,这里就不画图描述了。简而言之,就是每次查询数据的时候如果数据已经在缓存页中,那么就会将该缓存页对应的描述信息放到LRU链表的头部,如果不在缓存页中,就去磁盘中查找,如果查找到了,就将其加载到缓存中,并将该数据对应的缓存页的描述信息插入到LRU链表的头部。也就是说最近使用的缓存页都会排在前面,而排在后面的说明是不经常被使用到的。

最后,如果 Buffer Pool 不够使用了,那么 MySQL就会将 LRU 链表中的尾节点刷入到磁盘中,为 Buffer Pool 腾出内存空间。

但是LRU也存在一定的问题,针对这些问题,我们先来了解下MySQL的预读机制。

4. MySQL 预读机制

预读是Mysql提高性能的一个重要的特性。预读 就是IO 异步将多个数据页读入 Buffer Pool 的一个过程,并且这些页被认为是很快就会被读取到的。InnoDB使用两种预读算法来提高I/O性能:线性预读和随机预读。

为了区分这两种预读的方式,我们可以把线性预读放到以extent(64个page为一个extent)为单位,而随机预读放到以extent中的page为单位。线性预读着眼于将下一个extent提前读取到buffer pool中,而随机预读着眼于将当前extent中的剩余的page提前读取到buffer pool中。

4.1 Linear线性预读

线性预读的单位是extend,一个extend中有64个page。线性预读的一个重要参数是innodb_read_ahead_threshold,是指在连续访问多少个页面之后,把下一个extend读入到buffer pool中,不过预读是一个异步的操作。当然这个参数不能超过64,因为一个extend最多只有64个页面。
例如,innodb_read_ahead_threshold = 56,就是指在连续访问了一个extend的56个页面之后把下一个extend读入到buffer pool中。

4.2 Random随机预读

随机预读方式则是表示当同一个extent中的一些page在buffer pool中发现时,Innodb会将该extent中的剩余page一并读到buffer pool中。由于随机预读方式给innodb 带来了一些不必要的复杂性,同时在性能也存在不稳定性,在5.5中已经将这种预读方式废弃,默认是OFF。

5. Buffer Pool 空间管理

5.1 LRU 带来的问题

5.1.1 预读失效

上面我们提到了缓冲池的预读机制可能会预先加载相邻的数据页。假如加载了 20、21 相邻的两个数据页,如果只有页号为 20 的缓存页被访问了,而另一个缓存页却没有被访问。此时两个缓存页都在链表的头部,但是为了加载这两个缓存页却淘汰了末尾的缓存页,而被淘汰的缓存页却是经常被访问的。这种情况就是预读失效,被预先加载进缓冲池的页,并没有被访问到,这种情况是不是很不合理?

5.1.2 缓冲池污染

还有一种情况是当执行一条 SQL 语句时,如果扫描了大量数据或是进行了全表扫描,此时缓冲池中就会加载大量的数据页,从而将缓冲池中已存在的所有页替换出去,这种情况同样是不合理的。这就是缓冲池污染,并且还会导致 MySQL 性能急剧下降。

5.2 基于冷热数据分离的LRU链表

为了解决这种问题,于是就有了基于冷热数据分离的LRU链表。

所谓的冷热分离,就是将 LRU 链表分成两部分,一部分是经常被使用到的热数据,另一部分是被加载进来但是很少使用的冷数据。通过参数innodb_old_blocks_pct 参数控制的,默认为37,也就是 37% 。整体结构大致如下所示:
在这里插入图片描述

所以数据在从磁盘被加载到缓存池的时候,首先是会被放在冷数据区的头部,然后在一定时间之后,如果再次访问了这个数据,那么这个数据所在的缓存页对应描述数据就会被放转移到热数据区链表的头部。

那为什么说是在一定的时间之后呢,假设某条数据刚被加载到缓存池中,然后紧接着又被访问了一次,这个时候假设就将其转移到热数据区链表的头部,但是以后就再也不会被使用了,这样子是不是就还是会存在之前的问题呢?

所以 MySQL通过innodb_old_blocks_time来设置数据被加载到缓存池后的多少时间之后再次被访问,才会将该数据转移到热数据区链表的头部,该参数默认是1000单位为:毫秒,也就是1秒之后,如果该数据又被访问了,那么这个时候才会将该数据从 LRU 链表的冷数据区转移到热数据区。

现在再回头看下上面的两个问题,是不是发现以及完全解决了?反正数据加载进来都在冷数据区,一定时间后访问的数据才会被移动到热数据区。

这个时候我们再来看上面提到的Buffer Pool内存不够的问题,现在是不是就觉得很简单了?我们只需要直接将冷数据区的尾节点的描述数据对应的缓存页刷到磁盘即可。

你以为LRU到这就结束了吗?不。MySQL觉得这还不够完美,刚刚我们都是在研究冷数据区,在一定规则下将冷数据区的数据加载到热数据区的头部。那热数据区呢?热数据区的数据本来就是频繁访问的,难道每次访问我们都将其移动到热数据区头部吗?这势必会对性能有一定的影响,所以MySQL针对热数据区也有相对的规则。

该规则就是:如果被访问的数据所在的缓存页在热数据区的前25%,那么该缓存页对应的描述数据是不会被转移到热数据链表的头部的,只有当被访问的缓存页对应的描述数据在热数据区链表的后75%,该缓存页的描述数据才会被转移到热数据链表的头部

举个例子来说,假设热数据区有100个缓存页描述数据,当被访问的缓存页在前25个的时候,热数据区的链表是不会有变化的,当被访问的缓存页在26~100(也就是数据在热数据区链表的后75%里面)的时候,这个时候被访问的缓存页才会被转移到链表的头部。

到此为止, MySQL对于LUR 链表的优化就堪称完美了。

6. 刷盘

上文我们知道内存不够的时候,会将冷数据区尾节点数据进行刷盘,那在Buffer Pool中的刷盘机制到底是怎么样的呢?

这里大家可能会担心脏页还没刷到磁盘的时候,MySQL 宕机了,这不就丢失数据了吗?实际上这个大家完全不需要担心,我们更新数据的时候会将相关操作记录在redo log日志中,通过 redo log 日志从而让 MySQL 拥有了崩溃恢复的能力。

接下来我们看下哪几种情况会触发脏页的刷新:

  • 当 redo log 日志满了的情况下,会主动触发脏页刷新到磁盘;
  • Buffer Pool 空间不足时,需要将一部分数据页淘汰掉,如果淘汰的是脏页,需要先将脏页同步到磁盘;
  • MySQL 认为空闲时,后台线程回定期将适量的脏页刷入到磁盘;
  • MySQL 正常关闭之前,会把所有的脏页刷入到磁盘;

在我们开启了慢 SQL 监控后,如果你发现**「偶尔」会出现一些用时稍长的 SQL**,这可能是因为脏页在刷新到磁盘时可能会给数据库带来性能开销,导致数据库操作抖动。如果经常出现这种现象,就需要调大 Buffer Pool 空间或 redo log 日志的大小。

7. Buffer Pool 配置相关

7.1 并发性能

我们平时的系统绝对不可能每次只有一个请求来访问的,说白了就是如果多个请求同时来执行增删改,那他们会并行的去操作 Buffer Pool 中的各种链表吗?如果是并行的会不会有什么问题。

实际上 MySQL在处理这个问题的时候考虑得非常简单,就是: Buffer Pool 一次只能允许一个线程来操作,一次只有一个线程来执行这一系列的操作,因为MySQL 为了保证数据的一致性,操作的时候必须缓存池加锁,一次只能有一个线程获取到锁

这个时候,大家这时候肯定满脑子问号。串行那还谈什么效率?大家别忘记了,这一系列的操作都是在内存中操作的,实际上这是一个瞬时的过程,在内存中的操作基本是几毫秒的甚至微妙级别的事情。

但是话又说回来,串行执行再怎么快也是串行,虽然不是性能瓶颈,这还有更好的优化办法吗?那肯定的 MySQL早就设计好了这些规则。那就是 Buffer Pool 是可以有多个的,可以通过 MySQL的配置文件来配置,参数分别是:

#  Buffer Pool  的总大小
innodb_buffer_pool_size=8589934592
#  Buffer Pool  的实例数(个数)
innodb_buffer_pool_instance=4

这个时候可能大家又会有新的问题, 多个Buffer Pool,那我们到底是怎么确定我们需要的缓存页在那个Buffer Pool中呢?

实际上在上文将free链表的时候我们已经给出答案了,那就是通过数据页缓存哈希表。根据表空间号+数据页号,我们就可以得到具体的缓存页地址了。

7.2 动态调整Buffer Pool大小

我们现在来讨论下 Buffer Pool 的大小能否动态调整。

假设我们现在的 Buffer Pool 的大小是 2GB大小,现在想将其扩大到 4GB,现在说一下如果真的要这么做,我们的 MySq 需要做哪些事情。首先 ,MySQL 需要向操作系统申请一块大小为 4G 的连续的地址连续的内存空间,然后将原来的 Buffer Pool 中的数据拷贝到新的 Buffer Pool 中。

这样可能吗?如果原来的是8G,扩大到 16G,那这个将原来的数据复制到新的 Buffer Pool 中是不是极为耗时的,所以这样的操作 MySQL必然是不支持的。但实际上这样的需求是客观存在的,那 MySQL是如何解决的呢?

为了处理这种情况,MySQL设计出 chunk 机制来解决。那什么是chunk机制呢?

chunk是 MySQL 设计的一种机制,这种机制的原理是将 Buffer Pool 拆分一个一个大小相等的 chunk 块,每个 chunk 默认大小为 128M(可以通过参数innodb_buffer_pool_chunk_size 来调整大小),也就是说 Buffer Pool 是由一个个的chunk组成的。

假设 Buffer Pool 大小是2GB,而一个chunk大小默认是128M。那么也就是说一个2GB大小的 Buffer Pool 里面由16个 chunk 组成,每个chunk中有自己的缓存页和描述数据,而 free 链表、flush 链表和 lru 链表是共享的。

在这里插入图片描述

那么说到这里大家应该都知道MySQL如何通过 chunk 机制来调整大小了吧?需要扩大的时候我们只需要新申请一个个的 chunk 就可以了。

这样不但不需要申请一块很大的连续的空间,更不需要将复制数据。这样就能达到动态调整大小了。

8. 总结

本篇文章我们详细讨论了 Buffer Pool 的内存结构,从 free 链表到 lru 链表,从 Buffer Pool 到 chunk,从磁盘中加载一个数据页到 Buffer Pool 到最后该数据页又被刷回到磁盘中的一整个过程,他的每一步都做了什么。

最后我们再来回顾下三个链表:

  • Free链表

    用来存放空闲的缓存页的描述数据,如果某个缓存页被使用了,那么该缓存页对应的描述数据就会被从free链表中移除。

  • Flush链表

    被修改的脏数据都记录在 Flush 中,同时会有一个后台线程会不定时的将 Flush 中记录的描述数据对应的缓存页刷新到磁盘中,如果某个缓存页被刷新到磁盘中了,那么该缓存页对应的描述数据会从 Flush 中移除,同时也会从LRU链表中移除(因为该数据已经不在 Buffer Pool 中了,已经被刷入到磁盘,所以就也没必要记录在 LRU 链表中了),同时还会将该缓存页的描述数据添加到free链表中,因为该缓存页变得空闲了。

  • LRU链表

    数据页被加载到 Buffer Pool 中的对应的缓存页后,同时会将缓存页对应的描述数据放到 LRU 链表的冷数据的头部,当在一定时间过后,冷数据区的数据被再次访问了,就会将其转移到热数据区链表的头部,如果被访问的数据就在热数据区,那么如果是在前25%就不会移动,如果在后75%仍然会将其转移到热数据区链表的头部。

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

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

相关文章

90%的人都不算会爬虫,这才是真正的技术,从0到高手的进阶

很多人以为学会了urlib模块和xpath等几个解析库,学了Selenium就会算精通爬虫了,但到外面想靠爬虫技术接点私活,才发现寸步难行。 龙叔我做了近20年的程序员,今天就告诉你,真正的爬虫高手应该学哪些东西,就…

相向双指针 leetcode 15 16 18 611

简单介绍 与同向双指针区别就是&#xff0c;同向查找的是子串最明显得区别就是两侧进行搜索比较快 基本得模板如下 while(left < right) {if( ){}else if(){}else{ // left与right得遍历while(){}} }具体以题目为例 两数之和比较简单 不展示了 leetcode 15 三数之和 这个…

NOC·核桃编程马拉松复赛低年级组 A 卷真题

1. 舞台区如下所示,角色足球的初始坐标为(0,0)。等待下图程序运行完毕后,足 球的坐标是多少?( ) A(100,100) B(-100,100) C(100,-100) D(0,0) 2. 亮亮设计了一个“击落陨石”的游戏。点击开始按钮后,陨石会出现在舞台区的随机位置,碰到炸弹后就会消失…

webrtc处理视频丢包的机制

1.摘要WebRTC是一个开源的实时交互式音频和视频通信框架。本文讨论了WebRTC中用于处理视频通信路径中数据包丢失的一些机制。讨论了各种系统细节&#xff0c;提出了一种基于时间层的自适应混合NACK/FEC方法。结果显示了该方法如何控制实时视频通信的质量权衡2.介绍WebRTC[1]是一…

特征工程——Tabular Data Features multimodal features

一、前言 机器学习时期&#xff0c;要花费大量的时间在特征设计上&#xff0c;好的输入数据可以让训练事半功倍。而有了深度学习后&#xff0c;神经网络可以自动实现特征提取&#xff0c;解放了手工(理论上是这样&#xff0c;实际也是要进行特征筛选的&#xff0c;因为在应用中…

小程序容器技术在微服务架构中的应用

随着移动互联网的发展&#xff0c;小程序已经成为了一种非常流行的应用方式&#xff0c;它可以在不安装任何应用的情况下&#xff0c;直接在移动终端设备&#xff08;如&#xff1a;App&#xff0c;iPad等&#xff09;中运行。微服务架构则是一种的分布式系统架构&#xff0c;可…

Ubuntu16.04 源码安装nginx

nginx源码包&#xff1a;http://nginx.org/download/ 目录1.可联网设备直接通过apt-get安装2.不可联网设备通过安装包编译和移植2.1 首先选择一台可联网的设备2.2 将编译好的文件夹拷贝到目标设备上问题&#xff1a;nginx启动 nginx: [emerg] bind() to 0.0.0.0:80 failed (98:…

机器学习100天(三十四):034 先验概率、条件概率

《机器学习100天》完整目录:目录 机器学习100天,今天讲的是:先验概率、条件概率。 一、先验概率、条件概率 机器学习中,我们经常听到两个数学概念:先验概率、后验概率。抛开复杂数学公式不谈,我们通过一个实际的例子来帮助大家理解这两种概率。 最近天气炎热,红色石头…

问题三十四:傅立叶变换——高通滤波

高通滤波器是一种可以通过去除图像低频信息来增强高频信息的滤波器。在图像处理中&#xff0c;高通滤波器常常用于去除模糊或平滑效果&#xff0c;以及增强边缘或细节。在本篇回答中&#xff0c;我们将使用Python和OpenCV实现高通滤波器。 Step 1&#xff1a;加载图像并进行傅…

JVM系统优化实践(6):年轻代、老年代与数据计算

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客&#xff0c;欢迎您来&#xff0c;欢迎您再来&#xff5e;上回说道如果当前Survivor区中年龄相同的一批对象总大小 ≥ Survivor总数 50%&#xff0c;那么这批对象及比它们年龄更大的对象&#xff0c;就都直接进入老年代。…

三八女神节有哪些数码好物?2023年三八女神节数码好物清单

2023年的三八女神节就快到了&#xff0c;大家还在烦恼&#xff0c;不知道有哪些数码好物&#xff1f;在此&#xff0c;我来给大家分享几款三八女神节实用性强的数码好物&#xff0c;一起来看看吧。 一、蓝牙耳机&#xff1a;南卡小音舱 参考价&#xff1a;239 推荐理由&…

flutter window安装过程

这里写自定义目录标题#下载相关官网地址&#xff1a;https://flutter.cn/docs/get-started/install/windows 根据官网下载相关包flutter_windows_3.7.5-stable.zip 解压到c盘&#xff0c;在path配置相关解压路径(c:\flutter)。 执行 where flutter dart &#xff0c;发现没有提…

Pytorch深度学习实战3-5:详解计算图与自动微分机(附实例)

目录1 计算图原理2 基于计算图的传播3 神经网络计算图4 自动微分机5 Pytorch中的自动微分5.1 梯度缓存5.2 参数冻结1 计算图原理 计算图(Computational Graph)是机器学习领域中推导神经网络和其他模型算法&#xff0c;以及软件编程实现的有效工具。 计算图的核心是将模型表示…

Vue3 企业级项目实战:项目须知与课程约定

本节内容很重要&#xff0c;希望大家能够耐心看完。 Vue3 企业级项目实战 - 程序员十三 - 掘金小册Vue3 Element Plus Spring Boot 企业级项目开发&#xff0c;升职加薪&#xff0c;快人一步。。「Vue3 企业级项目实战」由程序员十三撰写&#xff0c;2744人购买https://s.ju…

解决方案| anyRTC 融合其他厂商视频会议系统方案

背景 视频会议市场经历疫情后&#xff0c;不管是硬件视频会议还是云视频会议已经在各行各业铺开使用&#xff0c;特别是政府行业&#xff0c;职能部门除了几大硬件视频会议外&#xff0c;也开始逐渐尝试云视频会议&#xff0c;视频会议的场景运用除了日常的交流、沟通、学习外…

开启互联网赚钱模式

随着互联网的发展&#xff0c;现在几乎会玩手机和电脑的都离不开网络&#xff0c;自然出现了很多网络赚钱的项目&#xff0c;受到了很多新人创业者和做副业兼职者的欢迎。很多朋友都想利用电脑或手机在网上赚钱。其实不管做什么项目&#xff0c;都有一个过程&#xff0c;没有什…

【监控】Linux部署postgres_exporter及PG配置(非Docker)

目录一、下载及部署二、postgres_exporter配置1. 停止脚本stop.sh2. 启动脚本start.sh3. queries.yaml三、PostgreSQL数据库配置1. 修改postgresql.conf配置文件2. 创建用户、表、扩展等四、参考一、下载及部署 下载地址 选一个amd64下载 上传至服务器&#xff0c;解压 tax…

$ 6 :选择、循环

if-else语句 #include <stdio.h> //判断输入值是否大于0 int main() {int i;while (scanf("%d",&i)){if (i > 0)//不要在括号后加分号{printf("i is bigger than O\n");}else {printf("i is not bigger than O\n");}}return O; } …

cglib代理解析

工作原理 使用 <dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version></dependency>对类和接口分别进行代理 DemoService package com.fanqiechaodan.user.service;/*** author fa…

itop-3568 开发板系统编程学习笔记(3)目录 IO

【北京迅为】嵌入式学习之Linux系统编程篇 https://www.bilibili.com/video/BV1zV411e7Cy/ 个人学习笔记 文章目录mkdir() 函数opendir() 和 closedir() 函数readdir() 函数综合实验mkdir() 函数 头文件&#xff1a; #include <sys/types.h> #include <sys/stat.h&g…