mysql中InnoDB存储引擎的Buffer Pool

大家好。众所周知,对于使用InnoDB作为存储引擎的表来说,不管是用于存储用户数据的索引(包括聚簇索引和二级索引),还是各种系统数据,都是存储在磁盘上的。在处理客户端的请求时,当需要访问某个页的数据时,就会把完整的页的数据全部加载到内存中。将整个页加载到内存中后就可以进行读写访问了,在进行完读写访问之后mysql并不会把该页对应的内存空间释放掉,而是将其缓存起来,这样将来有请求再次访问该页面时,就可以省去磁盘IO的开销了。

InnoDB为了缓存磁盘中的页,在MySQL服务器启动的时候就向操作系统申请了一片连续的内存,这片连续的内存就是Buffer Pool(中文名是 缓冲池)。下面我们就来讲一下这个Buffer Pool。

1. Buffer Pool内部组成

InnoDB默认情况下Buffer Pool的大小为128M。我们可以在启动服务器的时候在my.ini文件中配置innodb_buffer_pool_size参数的值,它表示 Buffer Pool 的大小,就像这样:

[server]
innodb_buffer_pool_size = 268435456  #256M

注意:Buffer Pool 也不能太小,最小值为5M(当小于该值时会自动设置成 5M)。

Buffer Pool中默认的缓存页大小和在磁盘上默认的页大小是一样的,都是16KB。为了更好的管理这些在Buffer Pool中的缓存页,InnoDB为每一个缓存页都创建了一些所谓的控制信息 ,这些控制信息包括该页所属的表空间编号、页号、缓存页在Buffer Pool 中的地址、链表节点信息、一些锁信息以及LSN 信息等。

每个缓存页对应的控制信息占用的内存大小是相同的,我们就把每个页对应的控制信息占用的一块内存称为一个控制块,控制块和缓存页是一一对应的,它们都被存放到Buffer Pool中,其中控制块被存放到Buffer Pool的前边,缓存页被存放到 Buffer Pool后边,所以整个 Buffer Pool 对应的内存空间看起来就是这样的:
在这里插入图片描述

每一个控制块都对应一个缓存页,那在分配足够多的控制块和缓存页后,可能剩余的那点儿空间不够一对控制块和缓存页的大小,这个用不到的那点儿内存空间就是上边的碎片块。如果Buffer Pool的大小设置的刚刚好的话,也可能不会产生碎片。

每个控制块大约占用缓存页大小的5%,在MySQL5.7.21版本中,每个控制块占用的大小是808字节。我们设置的innodb_buffer_pool_size并不包含这部分控制块占用的内存空间大小,也就是说InnoDB在为Buffer Pool向操作系统申请连续的内存空间时,这片连续的内存空间一般会比innodb_buffer_pool_size的值大5%左右。

2. free链表的管理

MySQL服务器在刚启动的时候,就需要完成对Buffer Pool的初始化,就是先向操作系统申请BufferPool 的内存空间,然后把它划分成若干对控制块和缓存页。但是此时并没有真实的磁盘页被缓存到Buffer Pool中,之后随着程序的运行,会不断的有磁盘上的页被缓存到Buffer Pool中。

那么从磁盘上读取一个页到Buffer Pool中的时候该放到哪个缓存页的位置呢?这个时候缓存页对应的控制块就派上大用场了,我们可以把所有空闲的缓存页对应的控制块作为一个节点放到一个链表中,这个链表被称作 free链表(或者说空闲链表)。刚刚完成初始化的Buffer Pool中所有的缓存页都是空闲的,所以每一个缓存页对应的控制块都会被加入到free链表中,假设该Buffer Pool中可容纳的缓存页数量为n,那增加了free链表 的效果图就是这样的:
在这里插入图片描述

InnoDB为了更好的管理free链表 ,特意为这个链表定义了一个基节点 ,里边儿包含着链表的头节点地址,尾节点地址,以及当前链表中节点的数量等信息。

注意:链表的基节点占用的内存空间并不包含在为 Buffer Pool 申请的一大片连续内存空间之内,而是单独申请的一块内存空间。

每当需要从磁盘中加载一个页到Buffer Pool中时,就从free链表中取一个空闲的缓存页,并且把该缓存页对应的控制块的信息填上(就是该页所在的表空间、页号之类的信息),然后把该缓存页对应的 free链表节点从链表中移除,表示该缓存页已经被使用了。

3. 缓存页的哈希处理

我们前边说过,当我们需要访问某个页中的数据时,就会把该页从磁盘加载到Buffer Pool中,如果该页已经在Buffer Pool中的话直接使用就可以了。那么我们怎么知道该页在不在 Buffer Pool 中呢?

回想一下,我们其实是根据表空间号 + 页号来定位一个页的,也就相当于表空间号 + 页号是一个key,缓存页就是对应的value,所以我们可以用表空间号 + 页号作为key , 缓存页作为value创建一个哈希表,在需要访问某个页的数据时,先从哈希表中根据表空间号 + 页号看看有没有对应的缓存页,如果有,直接使用该缓存页就好,如果没有,那就从 free链表 中选一个空闲的缓存页,然后把磁盘中对应的页加载到该缓存页的位置。

4. flush链表的管理

当我们修改了Buffer Pool中某个缓存页的数据时,那它就和磁盘上的页不一致了,这样的缓存页也被称为脏页(英文名:dirty page)。由于频繁的往磁盘中写数据会严重的影响程序的性能,所以每次修改缓存页后,我们并不着急立即把修改同步到磁盘上,而是在未来的某个时间点进行同步。

为了知道Buffer Pool 中哪些页是脏页,InnoDB又创建一个存储脏页的链表,凡是修改过的缓存页对应的控制块都会作为一个节点加入到一个链表中,因为这个链表节点对应的缓存页都是需要被刷新到磁盘上的,所以也叫flush链表 。链表的构造和free链表差不多,假设某个时间点Buffer Pool中的脏页数量为n ,那么对应的flush链表 就长这样:
在这里插入图片描述

5. LRU链表的管理

Buffer Pool 对应的内存大小毕竟是有限的,如果需要缓存的页占用的内存大小超过了Buffer Pool大小时,就会把某些旧的缓存页从Buffer Pool中移除,然后再把新的页放进来。 那么应该移除哪些缓存页呢?

我们设立Buffer Pool的初衷就是想减少和磁盘的IO交互,所以最好每次在访问某个页的时候它都已经被缓存到Buffer Pool中了。假设我们一共访问了 n 次页,那么被访问的页已经在缓存中的次数除以n就是所谓的缓存命中率,缓存命中率肯定是越高越好。所以当 Buffer Pool 中不再有空闲的缓存页时,就需要淘汰掉部分最近很少使用的缓存页。

不过,我们怎么知道哪些缓存页最近频繁使用,哪些最近很少使用呢?InnoDB又创建一个链表,由于这个链表是为了按照最近最少使用的原则去淘汰缓存页的,所以这个链表可以被称为LRU链表(LRU的英文全称:Least Recently Used)。

当我们需要访问某个页时,可以这样处理LRU链表 :

  1. 如果该页不在Buffer Pool中,在把该页从磁盘加载到Buffer Pool中的缓存页时,就把该缓存页对应的控制块作为节点塞到链表的头部。
  2. 如果该页已经缓存在Buffer Pool中,则直接把该页对应的控制块移动到LRU链表的头部。
    也就是说:只要我们使用到某个缓存页,就把该缓存页调整到LRU链表 的头部,这样 LRU链表尾部就是最近最少使用的缓存页了。所以当 Buffer Pool中的空闲缓存页使用完时,到LRU链表的尾部找些缓存页淘汰就可以了。

实际上LRU链表的使用并没有上述这么简单,因为存在两种比较尴尬的情况:

情况一:InnoDB认为执行当前的请求可能之后会读取某些页面,于是就预先把它们加载到Buffer Pool中。InnoDB称这种情况为预读 (英文名:read ahead)。

根据触发方式的不同, 预读又可以细分为下边两种:

线性预读: InnoDB提供了一个系统变量 innodb_read_ahead_threshold,如果顺序访问了某个区的页面超过这个系统变量的值,就会触发一次异步读取下一个区中全部的页面到Buffer Pool的请求,异步读取意味着从磁盘中加载这些被预读的页面并不会影响到当前工作线程的正常执行。这个innodb_read_ahead_threshold 系统变量的值默认是56 ,我们可以在服务器启动时通过启动参数或者在服务器运行过程中直接调整该系统变量的值。

随机预读: 如果 Buffer Pool 中已经缓存了某个区的13个连续的页面,不论这些页面是不是顺序读取的,都会触发一次异步读取本区中所有其的页面到Buffer Pool的请求。InnoDB同时提供了innodb_random_read_ahead系统变量,它的默认值为OFF ,也就意味着 InnoDB 并不会默认开启随机预读的功能,如果我们想开启该功能,可以通过修改启动参数或者直接使用 SET GLOBAL 命令把该变量的值设置为 ON。

预读本来是个好事儿,如果预读到Buffer Pool中的页成功的被使用到,那就可以极大的提高语句执行的效率。可是如果用不到呢?这些预读的页都会放到 LRU 链表的头部,但是如果此时Buffer Pool的容量不太大而且很多预读的页面都没有用到的话,这就会导致处在LRU链表尾部的一些缓存页会很快的被淘汰掉,会大大降低缓存命中率。

情况二:当执行需要扫描全表的查询语句时,那就意味着将访问到该表所在的所有页。当访问这些页时,会把它们统统都加载到Buffer Pool中,如果访问的页面特别多,而Buffer Pool又不能全部容纳它们的话,那就会一直“排挤”掉LRU链表尾部的缓存页。这严重的影响到其他查询对 Buffer Pool的使用,从而大大降低了缓存命中率。

因为有这两种情况的存在,所以InnoDB把这个LRU链表按照一定比例分成两截,分别是:

一部分存储使用频率非常高的缓存页,所以这一部分链表也叫做热数据,或者称young区域 。

另一部分存储使用频率不是很高的缓存页,所以这一部分链表也叫做 冷数据 ,或者称old区域 。

在这里插入图片描述

我们是按照某个比例将LRU链表分成两半的,不是某些节点固定是young区域的,某些节点固定是old区域的,随着程序的运行,某个节点所属的区域也可能发生变化。

对于 InnoDB 存储引擎来说,我们可以通过查看系统变量innodb_old_blocks_pct的值来确定old 区域在LRU链表中所占的比例。
在这里插入图片描述

默认情况下, old区域在LRU链表中所占的比例是37% ,也就是说 old 区域大约占 LRU链表的3/8 。这个比例我们可以在启动时修改my.ini中的innodb_old_blocks_pct参数来控制old 区域在 LRU链表中所占的比例。

[server]
innodb_old_blocks_pct = 40

如果在服务器运行期间,我们也可以修改这个系统变量的值,不过这个系统变量属于全局变量,一经修改会对所有客户端生效

SET GLOBAL innodb_old_blocks_pct = 40;

有了这个被划分成young和old区域的LRU链表之后,InnoDB针对上边提到的两种降低缓存命中率的情况进行了优化:

针对预读的页面可能不进行后续访问情况的优化:InnoDB规定,当磁盘上的某个页面在初次加载到Buffer Pool中的某个缓存页时,该缓存页对应的控制块会被放到old区域的头部。这样针对预读到Buffer Pool却不进行后续访问的页面就会被逐渐从old 区域逐出,而不会影响young区域中被使用比较频繁的缓存页。

针对全表扫描时,短时间内访问大量使用频率非常低的页面情况的优化:我们知道全表扫描的执行频率非常低,而且在执行全表扫描的过程中,即使某个页面中有很多条记录,尽管每读取一条记录都算是对页面的一次访问,但是这个过程所花费的时间也是非常少的。所以我们只需要规定,在对某个处在old区域的缓存页进行第一次访问时就在它对应的控制块中记录下来这个访问时间,如果后续的访问时间与第一次访问的时间在某个时间间隔内,那么该页面就不会被从old区域移动到young区域的头部,否则将它移动到young区域的头部。上述的这个间隔时间是由系统变量innodb_old_blocks_time控制的:
在这里插入图片描述

这个 innodb_old_blocks_time 的默认值是1000 毫秒,也就意味着对于从磁盘上被加载到LRU链表的old区域的某个页来说,如果第一次和最后一次访问该页面的时间间隔小于1s,那么该页是不会被加入到young区域的。
我们也可以在服务器启动或运行时设置 innodb_old_blocks_time的值,方法和innodb_old_blocks_pct参数一样。

注意:如果innodb_old_blocks_time的值设置 0 ,那么每次访问一个页面时就会把该页面放到 young区域的头部。

正是因为将 LRU 链表划分为young和old区域这两个部分,又添加了innodb_old_blocks_time这个系统变量,才使得预读机制和全表扫描造成的缓存命中率降低的问题得到了遏制,因为用不到的预读页面以及全表扫描的页面都只会被放到old区域,而不影响young区域中的缓存页。

6. 其他的一些链表

为了更好的管理Buffer Pool中的缓存页,除了上边提到的一些措施,InnoDB还引进了一些其他的链表 ,比如unzip LRU链表用于管理解压页,zip clean链表用于管理没有被解压的压缩页, zip free数组中每一个元素都代表一个链表,它们组成所谓的伙伴系统来为压缩页提供内存空间等等,反正是为了更好的管理这 Buffer Pool引入了各种链表或其他数据结构,了解即可。

7. 刷新脏页到磁盘

mysql后台有专门的线程每隔一段时间负责把脏页刷新到磁盘,这样可以不影响用户线程处理正常的请求。主要有两种刷新路径:

从LRU链表的冷数据中刷新一部分页面到磁盘:后台线程会定时从LRU链表尾部开始扫描一些页面,扫描的页面数量可以通过系统变量innodb_lru_scan_depth来指定,如果从里边儿发现脏页,会把它们刷新到磁盘。这种刷新页面的方式被称之为BUF_FLUSH_LRU 。

从flush链表 中刷新一部分页面到磁盘:后台线程也会定时从flush链表 中刷新一部分页面到磁盘,刷新的速率取决于当时系统是不是很繁忙。这种刷新页面的方式被称之为BUF_FLUSH_LIST 。

有时候后台线程刷新脏页的进度比较慢,导致用户线程在准备加载一个磁盘页到Buffer Pool时没有可用的缓存页,这时就会尝试看看LRU链表 尾部有没有可以直接释放掉的未修改页面,如果没有的话会不得不将LRU链表尾部的一个脏页同步刷新到磁盘。这种刷新单个页面到磁盘中的刷新方式被称之为BUF_FLUSH_SINGLE_PAGE 。

当然,有时候系统特别繁忙时,也可能出现用户线程批量的从flush链表中刷新脏页的情况,很显然在处理用户请求过程中去刷新脏页是一种严重降低处理速度的行为。

8. 多个Buffer Pool实例

我们上边说过, Buffer Pool本质是InnoDB向操作系统申请的一块连续的内存空间,在多线程环境下,访问Buffer Pool中的各种链表都需要加锁处理,在Buffer Pool特别大而且多线程并发访问特别高的情况下,单一的 Buffer Pool 可能会影响请求的处理速度。所以在Buffer Pool特别大的时候,我们可以把它们拆分成若干个小的Buffer Pool,每个Buffer Pool 都称为一个实例,它们都是独立的,所以在多线程并发访问时并不会相互影响,从而提高并发处理能力。我们可以在服务器启动的时候通过设置my.ini中的innodb_buffer_pool_instances的值来修改Buffer Pool 实例的个数:

[server]
innodb_buffer_pool_instances = 2

这样就表明我们要创建2个 Buffer Pool 实例,示意图就是这样:
在这里插入图片描述

每个Buffer Pool实例实际占用的内存空间用下面这个公式计算:

innodb_buffer_pool_size/innodb_buffer_pool_instances

也就是总共的大小除以实例的个数,结果就是每个Buffer Pool实例占用的大小。不过也不是说 Buffer Pool实例创建的越多越好,分别管理各个 Buffer Pool也是需要性能开销的,InnoDB规定:innodb_buffer_pool_size的值小于1G的时候设置多个实例是无效的,InnoDB会默认把innodb_buffer_pool_instances的值修改为1。

9. innodb_buffer_pool_chunk_size

每次当我们要重新调整 Buffer Pool的大小时,都需要重新向操作系统申请一块连续的内存空间,然后将旧的Buffer Pool中的内容复制到这一块新空间,这是极其耗时的。所以MySQL决定不再一次性为某个Buffer Pool实例向操作系统申请一大片连续的内存空间,而是以一个所谓的 chunk为单位向操作系统申请空间。也就是说一个Buffer Pool实例其实是由若干个chunk组成的,一个chunk就代表一片连续的内存空间,里边儿包含了若干缓存页与其对应的控制块,画个图表示就是这样:
图片

上图代表的Buffer Pool就是由2个实例组成的,每个实例中又包含2个 chunk。我们在服务器运行期间调整Buffer Pool的大小时就是以chunk 为单位增加或者删除内存空间,而不需要重新向操作系统申请一片大的内存,然后进行缓存页的复制。这个所谓的chunk的大小是我们在启动操作 MySQL服务器时通过innodb_buffer_pool_chunk_size 启动参数指定的,它的默认值是 134217728 ,也就是128M 。

注意:innodb_buffer_pool_chunk_size的值只能在服务器启动时指定,在服务器运行过程中是不可以修改的。

10. 配置Buffer Pool时的注意事项

  1. innodb_buffer_pool_size 必须是 innodb_buffer_pool_chunk_size × innodb_buffer_pool_instances的倍数(这主要是想保证每一个 Buffer Pool 实例中包含的 chunk 数量相同)。

默认的innodb_buffer_pool_chunk_size的值是 128M , innodb_buffer_pool_instances的值是16 ,那么这两个值的乘积就是 2G ,也就是说 innodb_buffer_pool_size 的值必须是2G或者2G的整数倍。我们在启动 MySQL 服务器是这样指定启动参数的:

mysqld --innodb-buffer-pool-size=8G --innodb-buffer-pool-instances=16

如果我们指定的innodb_buffer_pool_size大于2G并且不是2G的整数倍,那么服务器会自动的把innodb_buffer_pool_size的值调整为2G的整数倍,比如我们在启动服务器时指定的innodb_buffer_pool_size 的值是 9G,那么服务器会自动把 innodb_buffer_pool_size 的值调整为10G。

  1. 如果在服务器启动时innodb_buffer_pool_chunk_size × innodb_buffer_pool_instances的值已经大于 innodb_buffer_pool_size的值,那么 innodb_buffer_pool_chunk_size 的值会被服务器自动设置为innodb_buffer_pool_size/innodb_buffer_pool_instances 的值。

比方说我们在启动服务器时指定的innodb_buffer_pool_size的值为 2G,innodb_buffer_pool_instances 的值为16,innodb_buffer_pool_chunk_size 的值为 256M :

mysqld --innodb-buffer-pool-size=2G --innodb-buffer-pool-instances=16 --innodb-buffer-pool-chunk-size=256M

由于 256M × 16 = 4G ,而 4G > 2G ,所以innodb_buffer_pool_chunk_size 值会被服务器改写为innodb_buffer_pool_size/innodb_buffer_pool_instances 的值,也就是:2G/16 = 128M。

11. 查看Buffer Pool的状态信息

MySQL为我们提供了SHOW ENGINE INNODB STATUS语句来查看关于 InnoDB 存储引擎运行过程中的一些状态信息,其中就包括 Buffer Pool的一些信息,我们看一下(为了突出重点,我们只把输出中关于Buffer Pool 的部分提取了出来):

mysql> SHOW ENGINE INNODB STATUS\G
(...省略前边的许多状态)
----------------------
BUFFER POOL AND MEMORY
----------------------
Total memory allocated 13218349056;
Dictionary memory allocated 4014231
Buffer pool size   786432
Free buffers       8174
Database pages     710576
Old database pages 262143
Modified db pages  124941
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 6195930012, not young 78247510485
108.18 youngs/s, 226.15 non-youngs/s
Pages read 2748866728, created 29217873, written 4845680877
160.77 reads/s, 3.80 creates/s, 190.16 writes/s
Buffer pool hit rate 956 / 1000, young-making rate 30 / 1000 not 605 / 1000
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 710576, unzip_LRU len: 118
I/O sum[134264]:cur[144], unzip sum[16]:cur[0]
--------------
(...省略后边的许多状态)
mysql>

我们来详细看一下这里边的每个值都代表什么意思:

Total memory allocated: 代表 Buffer Pool 向操作系统申请的连续内存空间大小,包括全部控制块、缓存页、以及碎片的大小。
Dictionary memory allocated: 为数据字典信息分配的内存空间大小,注意这个内存空间和Buffer Pool没啥关系,不包括在Total memory allocated中。
Buffer pool size: 代表该Buffer Pool可以容纳多少缓存页,单位是页。
Free buffers: 代表当前Buffer Pool还有多少空闲缓存页,也就是 free链表中还有多少个节点。
Database pages: 代表 LRU 链表中的页的数量,包含young和old 两个区域的节点数量。
Old database pages: 代表LRU链表old区域的节点数量。
Modified db pages: 代表脏页数量,也就是flush链表中节点的数量。
Pending reads: 正在等待从磁盘上加载到Buffer Pool中的页面数量。当准备从磁盘中加载某个页面时,会先为这个页面在Buffer Pool中分配一个缓存页以及它对应的控制块,然后把这个控制块添加到LRU的old区域的头部,但是这个时候真正的磁盘页并没有被加载进来,Pending reads 的值会跟着加1。
Pending writes LRU: 即将从LRU链表中刷新到磁盘中的页面数量。
Pending writes flush list: 即将从flush链表中刷新到磁盘中的页面数量。
Pending writes single page: 即将以单个页面的形式刷新到磁盘中的页面数量。
Pages made young: 代表LRU链表中曾经从old区域移动到young 区域头部的节点数量。这里需要注意,一个节点每次只有从old区域移动到 young区域头部时才会将Pages made young的值加1,也就是说如果该节点本来就在young区域,由于它符合在young区域1/4后边的要求,下一次访问这个页面时也会将它移动到young区域头部,但这个过程并不会导致 Pages made young 的值加1。
Page made not young: 在将innodb_old_blocks_time设置的值大于0时,首次访问或者后续访问某个处在old区域的节点时由于不符合时间间隔的限制而不能将其移动到young区域头部时, Page made not young 的值会加1。这里需要注意,对于处在young区域的节点,如果由于它在young区域的1/4处而导致它没有被移动到young 区域头部,这样的访问并不会将Page made not young的值加1。
youngs/s: 代表每秒从old区域被移动到young区域头部的节点数量。
non-youngs/s: 代表每秒由于不满足时间限制而不能从old区域移动到young区域头部的节点数量。
Pages read、 created、 written: 代表读取,创建,写入了多少页。后边跟着读取、创建、写入的速率。
Buffer pool hit rate: 表示在过去某段时间,平均访问1000次页面,有多少次该页面已经被缓存到Buffer Pool 了。
young-making rate: 表示在过去某段时间,平均访问1000次页面,有多少次访问使页面移动到young区域的头部了。需要大家注意的一点是,这里统计的将页面移动到young区域的头部次数不仅仅包含从old 区域移动到young 区域头部的次数,还包括从young区域移动到young 区域头部的次数。
not (young-making rate): 表示在过去某段时间,平均访问1000次页面,有多少次访问没有使页面移动到young区域的头部。需要大家注意的一点是,这里统计的没有将页面移动到young区域的头部次数不仅仅包含因为设置了innodb_old_blocks_time 系统变量而导致访问了 old 区域中的节点但没把它们移动到 young 区域的次数,还包含因为该节点在 young 区域的前1/4处而没有被移动到 young 区域头部的次数。
LRU len: 代表 LRU链表中节点的数量。
unzip_LRU: 代表 unzip_LRU链表中节点的数量。
I/O sum: 最近50s读取磁盘页的总数。
I/O cur: 现在正在读取的磁盘页数量。
I/O unzip sum: 最近50s解压的页面数量。
I/O unzip cur: 正在解压的页面数量。

好了,到这里我们就讲完了,大家有什么想法欢迎留言讨论。也希望大家能给作者点个关注,谢谢大家!最后依旧是请各位老板有钱的捧个人场,没钱的也捧个人场,谢谢各位老板!

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

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

相关文章

思维导图——幕布

一、前言 幕布是一款专注于简化和组织信息的大纲笔记应用,它旨在帮助用户高效地整理知识点、优化工作流程以及规划个人生活。 二、软件特点 幕布工具的核心优势在于其能够快速将用户的输入转换成清晰的思维导图,便于视觉化地理解和记忆信息。 幕布还具…

插入mysql报错:Incorrect string value: ‘\xF0\xAC\x8C\x97\xE5\x9E...‘

原因分析 这个错误通常发生在使用MySQL数据库时,尝试将包含四字节UTF-8字符(通常表示为Unicode码点大于UFFFF的字符)插入到一个不支持这种字符的字符集列中。一般在插入睡眠emoji表情时容易遇到 解决 -- 设置数据库编码utf8mb4 ALTER DAT…

TrollInstallerX小白一键安装巨魔商店 分分钟安装成功

概述 TrollInstallerX 是一款通用的 TrollStore 安装程序。它注重可靠性和易用性。它的速度也非常快,能够在几秒钟内将 TrollStore 和/或其持久性助手安装到最新设备上。 TrollInstallerX 支持所有运行 iOS 14.0 - 16.6.1 的设备,包括 arm64 和 arm64e。…

若依分离版—增加通知公告预览及缩放功能

若依分离版—增加通知公告预览及缩放功能 前言开发通知公告 前言 若依分离版的通知公告没有预览功能&#xff0c;想开发通知公告功能 开发通知公告 效果如下 具体开发内容 修改若依notice代码如下。 <template><div class"app-container"><el-…

103.网络游戏逆向分析与漏洞攻防-ui界面的设计-加速功能的开关设计

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果&#xff0c;代码看不懂是正常的&#xff0c;只要会抄就行&#xff0c;抄着抄着就能懂了 内容…

李廉洋:6.4-6.5黄金原油再次走低,美盘行情分析及最新策略。

黄金消息面分析&#xff1a;全球债券周二上涨&#xff0c;呼应美债隔夜的涨势。美联储或早降息的押注增强了主权债务的吸引力。澳大利亚和新西兰10年期债券收益率下跌至少8个基点&#xff0c;先前数据显示&#xff0c;美国5月份工厂活动萎缩的速度加快。日本10年期债券收益率下…

Nginx location 与 Rewrite

Nginx正则表达式 location 通过前缀或正则匹配用户的URL访问路径做页面跳转、访问控制和代理转发 location 大致可以分为三类&#xff1a; 精准匹配&#xff1a;location / {...} 一般匹配&#xff1a;location / {...} 正则匹配&#xff1a;location ~ / {...} location…

ctfshow解题,知识点学习

1.easy_zip&#xff08;misc&#xff09; 1&#xff09;打开环境后是一个压缩包&#xff0c;解压里面有个flag.txt文件需要密码&#xff0c; 2&#xff09;直接用工具爆破&#xff0c;即可找到密码 2.easy_eval 1&#xff09;进入题目环境&#xff0c;先进行代码审计 首先说是…

让你的博客实现负载均衡

零、缘起 有时候博客突然挂了&#xff0c;发现服务器厂商出了问题&#xff0c;很忧伤&#xff0c;我正在写着或查阅自家博客那种不可xx的内容。这时想着&#xff0c;如果这个博客有负载均衡就好了&#xff0c;空了想着实现下。 一分钟了解负载均衡的一切 选择第二种【反向代…

MoE 大模型的前世今生

节前&#xff0c;我们星球组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学。 针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 合集&#x…

Vue3整合Tailwindcss之padding样式类

04 常用基础样式 padding 样式类 什么是内边距 基础样式 ClassPropertiesp-0padding: 0px;px-0padding-left: 0px; padding-right: 0px;py-0padding-top: 0px; padding-bottom: 0px;ps-0padding-inline-start: 0px;pe-0padding-inline-end: 0px;pt-0padding-top: 0px;pr-0pa…

kafka-生产者监听器(SpringBoot整合Kafka)

文章目录 1、生产者监听器1.1、创建生产者监听器1.2、发送消息测试1.3、使用Java代码创建主题分区副本1.4、application.yml配置----v1版1.5、屏蔽 kafka debug 日志 logback.xml1.6、引入spring-kafka依赖1.7、控制台日志 1、生产者监听器 1.1、创建生产者监听器 package co…

图片批量纵向拼接神器:轻松插入间隔像素,生成真彩绚丽长图,让你的创意无限延伸!

在数字艺术的世界里&#xff0c;图片拼接早已不再是简单的组合&#xff0c;而是创意与技术的融合。如果你正在寻找一款能够快速将图片进行纵向拼接&#xff0c;并且能轻松插入间隔像素&#xff0c;同时保证色彩绚丽的神器&#xff0c;那么&#xff0c;我们首助编辑高手的长图拼…

如何实现单例模式及不同实现方法分析-设计模式

这是 一道面试常考题&#xff1a;&#xff08;经常会在面试中让手写一下&#xff09; 什么是单例模式 【问什么是单例模式时&#xff0c;不要答非所问&#xff0c;给出单例模式有两种类型之类的回答&#xff0c;要围绕单例模式的定义去展开。】 单例模式是指在内存中只会创建…

React 中的 ForwardRef的使用

React 中的 forwardRef Hooks 是指将子组件的 Dom 节点暴露给给父组件&#xff0c;在 React 中如果想要访问 Dom 节点是通过 useRef 这个 hooks&#xff0c;而 forwardHook 在 useRef 做了扩展。useRef 是当前组件中间中的节点&#xff0c;而 forwardRef 相当于做了一层封装将父…

springAOP 使用aop代替SqlsessionUtil业务层操作

在Maven框架pom配置文件中导入spring相关依赖&#xff1a; <dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency><dependency…

mac配置Personal Access Tokens

背景 在macbook环境中&#xff0c;使用idea、android studio、xcode时&#xff0c;使用gitlab需要登录&#xff0c;而直接使用文明密码是不允许登录的&#xff0c;这时就需要换种方式&#xff0c;这里有两种&#xff1a;ssh、Access Tokens&#xff0c;在公用电脑上推荐使用Ac…

第一个SpringBoot项目

目录 &#x1f4ad;1、新建New Project IDEA2023版本创建Sping项目只能勾选17和21&#xff0c;却无法使用Java8&#xff1f;&#x1f31f; 2、下载JDK 17&#x1f31f; &#x1f4ad;2、项目创建成功界面 1、目录 &#x1f31f; 2、pom文件&#x1f31f; &#x1f4ad;3、…

【数据分享】中国民政统计年鉴(1949-2022)

大家好&#xff01;今天我要向大家介绍一份重要的中国民政统计数据资源——《中国民政统计年鉴》。这份年鉴涵盖了从1949年到2022年中国民政统计全面数据&#xff0c;并提供限时免费下载。&#xff08;无需分享朋友圈即可获取&#xff09; 数据介绍 从1949年到2022年&#xf…

云原生架构案例分析_4.某电商业务云原生改造

名称解释&#xff1a; AHAS&#xff1a;应用高可用服务&#xff08;Application High Availability Service&#xff09;是一款专注于提高应用高可用能力的SaaS产品&#xff0c;主要包含多活容灾、故障演练和流量防护三个独立的功能模块。其中流量防护已迁移至微服务治理服务MS…