云贝教育 |【技术文章】PG的缓存管理器原理

news2024/11/15 21:42:40

PG的缓存管理器原理

缓冲区管理器管理共享内存和持久存储之间的数据传输 , 它会对 DBMS 的性能产生重大影响。 PostgreSQL 缓冲区管理器工作非常高效。

本章介绍 PostgreSQL 缓冲区管理器。第一部分提供概述 ,后续部分描述以下主题:

· 缓冲区管理器结构

· 缓冲区管理器锁

· 缓冲区管理器的工作原理

· 环形缓冲区

· 脏页的刷新

图1.1  缓冲区管理器、存储和后端进程之间的关系

一、 概述

本节介绍理解后续各节中的描述所必需的关键概念。

1.1 缓冲区管理器结构

PostgreSQL 缓冲区管理器包含缓冲区表、缓冲区描述符和缓冲池 ,这些将在下一节中介绍。缓 冲池层存储数据文件页 ,例如表和索引 ,以及自由空间映射和可见性映射。缓冲池是一个数组, 其中每个槽存储数据文件的一页。缓冲池数组的索引称为 buffer_ids。

1.2 缓冲区标签

在PostgreSQL中 ,所有数据文件的每一页都可以分配一个唯一的标签 ,即缓冲区标签。 当缓冲区管理器收到请求时 , PostgreSQL 使用所需页面的 buffer_tag。

buffer_tag 有五个值:

· specOid :包含目标页的关系所属表空间的OID。

· dbOid :包含目标页面的关系所属数据库的OID。

· rel Number:包含目标页面的关系文件的编号。

· block Num:关系中目标页的块号。

· fork Num:页面所属关系的分叉号。表、自由空间图和可见性图的分叉号分别定义为 0、 1 和 2。

例如 , buffer_tag '{ 16821, 16384, 37721, 0, 7}' 标识位于表的第七个块中的页 ,其 OID 和分 叉号分别为 37721 和 0。该表包含在 OID 为 16384 的数据库中,位于 OID 为 16821 的表空 间下。

类似地, buffer_tag“ { 16821, 16384, 37721, 1, 3} ”标识位于自由空间映射的第三块中的页 面 ,其OID和分叉号分别为37721和1。

1.3 后端进程如何读取页面

本小节描述后端进程如何从缓冲区管理器读取页面(图 1.2)。

图 1.2  后端如何从缓冲区管理器读取页面

(1) 当读取表或索引页时,后端进程向缓冲区管理器发送包含该页的 buffer_tag 的请求。

(2) 缓冲区管理器返回存储所请求页面的槽的buffer_ID。如果请求的页面未存储在缓冲池中,则缓冲区管理器将该页面从持久存储加载到缓冲池槽之一,然后返回该槽的 buffer_ID。

(3) 后端进程访问buffer_ID的slot(读取所需的页面)。

当后端进程修改缓冲池中的页面(例如 ,通过插入元组)时, 尚未刷新到存储的已修改页面被称为脏页面。

1.4 页面替换算法

当所有缓冲池槽都被占用并且所请求的页没有被存储时,缓冲管理器必须在缓冲池中选择一页来替换所请求的页。通常,在计算机科学领域,页面选择算法被称为页面替换算法,并且所选择的页面被称为受害者页面。

自计算机科学出现以来,对页面替换算法的研究一直在进行。许多替代算法被提出,PostgreSQL从1. 1版本开始使用时钟扫描算法。 时钟扫描比以前版本中使用的 LRU 算法更简单、更高效。

1.5 冲洗脏页

脏页最终应该被刷新到存储中。但是,缓冲区管理器需要帮助才能执行此任务。在 PostgreSQL 中,两个后台进程(checkpointer 和后台 writer)负责此任务。

1.6 节描述了检查点和后台编写器。

关于直接IO

PostgreSQL 版本 15 及更早版本不支持直接 I/O,尽管已经对此进行了讨论。请参阅有关 pgsql-ML的讨论和本文。

在版本 16 中,添加了 debug-io-direct 选项 。该选项供开发人员改进 PostgreSQL 中直接 I/O 的使用。如果开发顺利的话,Direct I/O将在不久的将来正式支持 。

二、 缓冲区管理器结构

PostgreSQL 缓冲区管理器包含三层:缓冲区表、缓冲区描述符和缓冲池(图 1.3):

图 1.3  缓冲区管理器的三层结构

缓冲区表层 :存储存储页的 buffer_tag 与保存存储页各自元数据的描述符的 buffer_ids 之间关系的哈希表。

缓冲区描述符层 :缓冲区描述符数组。每个描述符与缓冲池槽位一一对应 ,并保存对应槽位中所存储页面的元数据。

缓冲池层 :存储数据文件页的数组。数组中的每个槽称为 buffer_ids。

请注意 ,为了方便起见 ,采用术语“缓冲区描述符层 ” ,并且仅在本文档中使用。

这些层将在以下小节中详细描述。

2.1 缓冲表

缓冲表在逻辑上可以分为三部分: 哈希函数、 哈希桶槽和数据条目(图1.4)。

内置哈希函数将 buffer_tags 映射到哈希桶槽。即使哈希桶槽位的数量大于缓冲池槽位的数量,也可能会发生冲突。因此,缓冲表使用单独的链接和链表方法来解决冲突。当数据条目映射到同一个桶槽时,该方法将条目存储在同一个链表中,如图1.4所示。

图 1.4  缓冲表

数据条目包含两个值:页面的 buffer_tag 和保存页面元数据的描述符的 buffer_id。例如,数据条目“ Tag_A, id = 1 ”表示buffer_id为1的缓冲区描述符存储用Tag_A标记的页面的元数据。

例如, buffer_tag“ { 16821, 16384, 37721, 1, 3} ”通过hash运算之后bucket slot = 1;而 buffer_tag“ { 16821, 16384, 54784, 0, 13} ”通过hash运算之后bucket slot =3。

2.2.缓冲区描述符

缓冲区描述符的结构在 9.6 版本中得到了改进。首先,我解释 9.5 及更早版本中的缓冲区描述符,然后解释 9.6 及更高版本中的缓冲区描述符与以前的版本有何不同。

2.2.1  9.5 或更早版本

9.5及更早版本中的缓冲区描述符结构保存了相应缓冲池槽中存储页面的元数据。缓冲区描述符结构由 BufferDesc 结构定义。以下是一些主要字段:

tag保存了相应缓冲池槽中存储页的buffer_tag。 (缓冲区标签在第 1.1.2 节中定义。)

buf_id标识描述符。相当于对应缓冲池槽的buffer_id。

refcount保存当前正在访问关联存储页面的 PostgreSQL 进程数。它也称为引脚数。

◦ 当 PostgreSQL 进程访问存储的页面时,引用计数必须加 1 (refcount++)。访问页面 后,refcount必须减1( refcount--)。

◦ 当引用计数为零时,关联的存储页面将被取消固定,这意味着它当前未被访问。否则,它会被固定。

usage_count保存关联存储页自加载到相应缓冲池槽以来已被访问的次数。 它用于页面替换算法(第 4.4 节)。

content_lock和 io_in_progress_lock是轻量级锁,用于控制对关联存储页面的访问。这些字段在第 3.2 节中描述。

标志可以保存关联存储页面的多种状态。主要状态如下:

· dirty bit表示存储的页是脏的。

· valid bit存储的页是否有效,即可以读取或写入。

◦ 如果该位有效 ,则对应的缓冲池槽存储了一个页面,并且描述符保存了页面元数据,并且可以读取或写入所存储的页面。

◦ 如果该位无效,则描述符不保存任何元数据,并且无法读取或写入存储的页面。

· io_in_progress bit指示缓冲区管理器是否正在从存储器读取关联页或将关联页写入存储 器。

buf_hdr_lock是一个自旋锁,保护以下字段 :flags、 usage_count、 refcount。

freeNext是指向下一个描述符的指针,用于生成空闲列表,这将在下一小节中进行描述。

2.2.2  版本 9.6 或更高版本

缓冲区描述符结构由 BufferDesc 结构定义。

tag保存了相应缓冲池槽中存储页的buffer_tag。

· buf_id 标识描述符。

· content_lock 是一个轻量级锁,用于控制对关联存储页面的访问。

· freeNext 是指向下一个描述符的指针,用于生成空闲列表。

· states可以保存关联存储页面的多个状态和变量,例如refcount和usage_count。

flags、usage_count 和 refcount 字段已组合成单个 32 位数据(状态)以使用 CPU 原子操作。 因此,io_in_progress_lock 和自旋锁 (buf_hdr_lock) 已被删除, 因为不再需要保护这些值。

2.2.3  描述符状态

为了简化以下描述 ,定义了三种描述符状态:

Empty:当对应的缓冲池槽没有存储页面时(即refcount和usage_count为0),该描述符的状态为空。

Pinned:当相应的缓冲池槽存储一个页面并且任何 PostgreSQL 进程正在访问该页面时(即 refcount 和 use_count 大于或等于 1),该缓冲区描述符的状态被 pinned。

Unpinned:当相应的缓冲池槽存储了一个页面,但没有 PostgreSQL 进程正在访问该页面 (即, usage_count 大于或等于 1,但 refcount 为 0) 时,该缓冲区描述符的状态为unpinned。

在下图中 ,缓冲区描述符的状态由彩色框表示。

另外 ,脏页被表示为“X ”。例如,未固定的脏描述符由 X 表示。

2.3  缓冲区描述符层

缓冲区描述符的集合形成一个数组,在本文档中称为缓冲区描述符层。

当PostgreSQL服务器启动时,所有缓冲区描述符的状态都是空的。在 PostgreSQL 中,这些描述符包含一个称为 freelist 的链表(图 1.5)。

需要注意的是,PostgreSQL 中的空闲列表与 Oracle 中的空闲列表是完全不同的概念。

PostgreSQL 中的空闲列表只是空缓冲区描述符的链接列表。在 PostgreSQL 中,第 5.3.4 节中描述的自由空间映射与 Oracle 中的自由列表具有相同的用途。

图 1.5  缓冲区管理器初始状态

(1) 从空闲列表的顶部检索一个空描述符,并将其固定(即将其 refcount 和 use_count 加 1)。

(2) 在缓冲表中插入一个新条目,将第一页的tag映射到检索到的描述符的 buffer_id。

(3) 将新页从存储器加载到相应的缓冲池槽中。

(4) 将新页面的元数据保存到检索到的描述符中。

第二页和后续页面以类似的方式加载。第 4.2 节提供了更多详细信息。

图 1.6 显示了第一页是如何加载的。

从空闲列表中检索的描述符始终保存页面的元数据。换句话说,非空描述符一旦被使用就不会返回到空闲列表。但是,当发生以下情况之一时,相应的描述符会再次添加到空闲列表中,并将描 述符状态设置为“空 ”:

1. 表或索引被删除。

2. 数据库被删除。

3. 使用 VACUUM FULL 命令清理表或索引。

缓冲区描述符层包含一个无符号的 32 位整数变量,即 nextVictimBuffer。该变量用于第 4.4 节中描述的页面替换算法。

2.4  缓冲池

缓冲池是一个简单的数组,用于存储数据文件页,例如表和索引。缓冲池数组的索引称为buffer_ids。

缓冲池槽大小为8 KB,等于页面的大小。因此,每个槽可以存储整个页。

三 缓冲区管理器锁

缓冲区管理器使用许多锁来实现多种目的。本节描述了后续各节中的解释所必需的锁。

请注意 ,本节中描述的锁是缓冲区管理器同步机制的一部分。它们不涉及任何 SQL 语句或 SQL 选项。

3.1 缓冲区表锁

Buf MappingLock保护整个缓冲表的数据完整性。 它是一种轻量级锁,可以在共享和独占模式下使用。当在缓冲表中搜索条目时,后端进程持有共享的Buf MappingLock。当插入或删除条目时,后端进程持有排他锁。

Buf MappingLock 被分成多个分区以减少缓冲表中的争用(默认为 128 个分区)。每个Buf MappingLock分区保护对应的哈希桶槽的一部分

图1.7展示了Buf MappingLock分裂效果的典型例子。两个后端进程可以同时以独占模式持有各自的Buf MappingLock分区以插入新的数据条目。如果 Buf MappingLock 是单个系统范围的锁,则两个进程都必须等待另一个进程完成,具体取决于哪个进程先启动。

图 1.7 两个进程以独占方式同时获取Buf MappingLock的各自分区来插入新的数据条目。

缓冲表需要许多其他锁。例如,缓冲表内部使用自旋锁来删除条目。然而,省略了对这些其他锁的描述,因为它们在本文档中不是必需的。

3.2 每个缓冲区描述符的锁

在9.5或更早版本中,每个缓冲区描述符使用两个轻量级锁,content_lock和io_in_progress_lock,来控制对相应缓冲池槽中存储页面的访问。 当检查或更改其自身字段的值(即, usage_count、 refcount、flags) 时,将使用自旋锁(buf_hdr_lock)。

在 9.6 版本中,缓冲区访问方法得到了改进。io_in_progress_lock 和自旋锁 (buf_hdr_lock) 已被删除。9.6 及更高版本不使用这些锁,而是使用 CPU 原子操作来检查和更改它们的值。

3.2.1 内容锁

content_lock 是一个典型的强制访问限制的锁。它可以在共享和独占模式下使用。

读取页面时,后端进程会获取存储该页面的缓冲区描述符的共享 content_lock(buffer pin)

执行以下操作之一时会获取独占 content_lock:

· 将行(即元组)插入存储页面或更改存储页面内元组的 t_xmin/t_xmax 字段。

· 物理删除元组或压缩存储页面上的可用空间。

· 冻结存储页面内的元组。 

官方 README 文件提供了更多详细信息。

3.2.2. io_in_progress_lock(9. 5 或更早版本)

在版本 9.5 或更早版本中,io_in_progress 锁用于等待缓冲区上的 I/O 完成。 当 PostgreSQL 进程从存储加载页面数据或向存储写入页面数据时,该进程在访问存储时会获取相应描述符的独占 io_in_progress 锁。

3.2.3 自旋锁(9.5 或更早版本)

当检查或更改标志或其他字段(例如 refcount 和 use_count)时,将使用自旋锁。下面给出两个自旋锁使用的具体例子:

(1) 固定缓冲区描述符:

· 1. 获取缓冲区描述符的自旋锁。

· 2. 将其refcount和usage_count的值增加1。

· 3. 释放自旋锁。

(2) 将脏位设置为“ 1 ”:

· 1. 获取缓冲区描述符的自旋锁。

· 2. 使用按位运算将脏位设置为“ 1 ”。

· 3. 释放自旋锁。

改变其他位以相同的方式执行。

四 缓冲区管理器的工作原理

本节介绍缓冲区管理器的工作原理。 当后端进程想要访问所需的页面时,它会调用 Read BufferExtended函数。

Read BufferExtended 函数的行为取决于三种逻辑情况。以下小节对每种情况进行了描述。此外,最后一小节描述了 PostgreSQL 时钟扫描页面替换算法。

4.1  访问缓冲池中存储的页面

首先,描述最简单的情况,其中所需的页面已经存储在缓冲池中。在这种情况下,缓冲区管理器执行以下步骤:

(1) 创建所需页面的buffer_tag(在本例中 , buffer_tag为'Tag_C')并使用哈希函数计算包含所创建的buffer_tag的关联条目的哈希桶槽位。

(2)以共享方式获取覆盖所获取的哈希桶槽位的Buf MappingLock分区(该锁将在步骤(5) 中释放)。

(3) 查找标签为“ Tag_C ”的条目 ,并从该条目中获取buffer_id。在此示例中 , buffer_id 为 2。

(4) 固定buffer_id 2 的缓冲区描述符,将描述符的refcount 和usage_count 加1。(第3.2 节描述了固定)。

(5) 释放Buf MappingLock。

(6) 访问buffer_id为2的缓冲池槽。

然后 , 当从缓冲池槽中的页面读取行时, PostgreSQL进程获取相应缓冲区描述符的共享 content_lock。 因此 ,缓冲池槽可以同时被多个进程读取。

当向页面插入(以及更新或删除)行时, Postgres 进程会获取相应缓冲区描述符的独占 content_lock。 (请注意 ,该页的脏位必须设置为“ 1 ”。)

访问页面后,相应缓冲区描述符的refcount值减1。

4.2  将页面从存储加载到空槽

在第二种情况下,假设所需的页面不在缓冲池中,并且空闲列表具有空闲元素(空描述符)。在 这种情况下,缓冲区管理器执行以下步骤:

( 1)查找缓冲表(我们假设没有找到)。

。 1. 创建所需页面的buffer_tag(本例中buffer_tag为“ Tag_E ”)并计算哈希桶槽位。 

。 2、以共享方式获取Buf MappingLock分区。

。 3. 查找缓冲表。 (根据假设没有找到。)

。 4. 释放Buf MappingLock。

(2) 从freelist中获取空缓冲区描述符,并pin它。本例中获取的描述符的buffer_id为4。

(3) 以独占方式获取Buf MappingLock分区。(该锁将在步骤(6)中释放。)

(4) 创建一个新的数据条目,其中包含 buffer_tag 'Tag_E' 和 buffer_id 4. 将创建的条目插入到缓冲表中。

(5) 将所需的页数据从存储加载到buffer_id 4的缓冲池槽中,如下所示:

。 1、9.5及之前版本 ,获取对应描述符的独占io_in_progress_lock。

。 2. 将相应描述符的io_in_progress位设置为“ 1 ” ,以防止其他进程访问。

。 3. 将所需的页面数据从存储器加载到缓冲池槽。

。 4. 更改相应描述符的状态: io_in_progress 位设置为“0 ” ,有效位设置为“ 1 ”。 

。 5. 在9.5或更早版本中 ,释放io_in_progress_lock。

(6) 释放Buf MappingLock。

(7) 访问buffer_id为4的缓冲池槽。

图 1.9  将页面从存储加载到空槽。

4.3  将页面从存储加载到受害者缓冲池槽

在这种情况下,假设所有缓冲池槽都被页面占用,但未存储所需的页面。缓冲区管理器执行以下步骤:

(1) 创建所需页面的buffer_tag并查找缓冲表。在此示例中,我们假设 buffer_tag 为 “ Tag_M ”(未找到所需页面)。

(2) 使用时钟扫描算法选择受害者缓冲池槽。从缓冲区表中获取包含受害者池槽的 buffer_id 的旧条目,并将受害者池槽固定在缓冲区描述符层中。在此示例中,受害者槽的 buffer_id 为 5, 旧条目为“ Tag_F, id =5 ”。 时钟扫描将在下一小节中描述。

(3) 如果受害者页面数据脏了,则刷新(写入和fsync) ;否则进行步骤(4)。

在用新数据覆盖之前,必须将脏页写入存储。刷新脏页的过程如下:

 1. 获取buffer_id为5的描述符的共享content_lock和独占io_in_progress锁(在步骤6中释放)。

 2、改变对应描述符的状态; io_in_progress 位设置为“ 1 ”,just_dirtied 位设置为“0 ”。

 3、根据情况调用XLogFlush()函数 ,将WAL缓冲区上的WAL数据写入当前WAL段文件 (详细内容略 ,WAL和XLogFlush函数在第9章中介绍)。

 4. 将受害者页面数据刷新到存储中。

 5、改变对应描述符的状态; io_in_progress 位设置为“0 ” ,有效位设置为“ 1 ”。

 6. 释放io_in_progress 和content_lock 锁。

(4) 以独占模式获取覆盖包含旧条目的槽的旧Buf MappingLock分区。

(5) 获取新的Buf MappingLock分区并将新条目插入缓冲表:

 1. 创建由新的 buffer_tag“ Tag_M ”和受害者的 buffer_id 组成的新条目。

 2. 获取新的 Buf MappingLock 分区 ,该分区覆盖包含独占模式下的新条目的槽。

 3. 将新条目插入缓冲表。

图 1.10  将页面从存储加载到受害者缓冲池槽。

(6) 从缓冲表中删除旧条目 ,并释放旧的Buf MappingLock分区。

(7) 将所需的页面数据从存储器加载到牺牲缓冲槽中。然后,用buffer_id 5更新描述符的标志;脏位被设置为 0,其他位被初始化。

(8) 释放新的Buf MappingLock分区。

(9) 访问buffer_id为5的缓冲池槽。

五 页面替换算法: 时钟扫描

本节的其余部分描述时钟扫描算法。该算法是NFU( NotFrequentlyUsed) 的变体,开销较低; 它有效地选择不常用的页面。

将缓冲区描述符想象成一个循环列表(图 1.12) 。 nextVictimBuffer 是一个无符号 32 位整数,始终指向缓冲区描述符之一并顺时针旋转。

算法的伪代码和描述如下

(1) 获取nextVictimBuffer指向的候选缓冲区描述符。

(2) 如果候选缓冲区描述符未固定 ,则继续步骤(3)。否则,继续步骤(4)。

(3)如果候选描述符的usage_count为0 ,则选择该描述符对应的slot作为victim ,并执行步骤(5)。否则 ,将该描述符的usage_count减1并继续执行步骤(4)。

(4) 将 nextVictimBuffer 前进到下一个描述符(如果在末尾 ,则回绕)并返回到步骤 (1)。 重复此过程 ,直到找到受害者。

(5) 返回受害者的buffer_id。

具体例子如图1. 12所示。缓冲区描述符显示为蓝色或青色框,框中的数字显示每个描述符的usage_count。

1. nextVictimBuffer 指向第一个描述符(buffer_id 1)。但是,该描述符会被跳过,因为它已被固定。

2. nextVictimBuffer 指向第二个描述符(buffer_id 2)。该描述符已取消固定 ,但其use_count 为 2。 因此, usage_count 减 1,并且 nextVictimBuffer 前进到第三个候选者。

3. nextVictimBuffer 指向第三个描述符(buffer_id 3)。该描述符已取消固定 ,并且其usage_count为0。 因此,这是本轮的受害者。

每当nextVictimBuffer扫描一个unpinned描述符时, 它的usage_count就会减1。 因此 , 如果缓冲池中存在unpinned描述符,该算法总是可以通过旋转nextVictimBuffer找到一个 usage_count为0的victim。

补充ORACLE LRU算法

六 环形缓冲器

当读取或写入大表时, PostgreSQL 使用环形缓冲区而不是缓冲池。环形缓冲区是一个小的临时缓冲区。 当满足以下任一条件时,环形缓冲区被分配给共享内存:

1. 批量阅读:

当扫描大小超过缓冲池大小(shared_buffers / 4) 四分之一的关系时。在本例中,环形缓冲区大小为 256 KB。

2. 批量写入:

当执行下面列出的 SQL 命令时。在本例中 ,环形缓冲区大小为 16 MB。

· COPY FROM command.

· CREATE TABLE AS command.

· CREATE MATERIALIZED VIEW or REFRESH MATERIALIZED VIEW command. · ALTER TABLE command.

3. Vacuum进程:

当 autovacuum 执行真空处理时。在本例中 ,环形缓冲区大小为 256 KB。

为什么是 256 KB?答案在缓冲区管理器源目录下的自述文件中进行了解释。

对于顺序扫描 ,使用 256 KB 环。 它足够小 ,可以放入 L2 缓存 ,这使得将页面从操作系统缓存传输到共享缓冲区缓存变得高效。更少通常就足够了 ,但环必须足够大 ,以容纳扫描中同时固定的所有页面。

七 刷新脏页

除了替换受害者页面之外 ,检查指针和后台编写器还会将脏页面刷新到存储中。两个进程具有相同的功能 ,即刷新脏页,但它们具有不同的角色和行为。

检查点进程将检查点记录写入 WAL 段文件 ,并在检查点开始时刷新脏页。 9.7 节描述了检查点及其开始时间。

后台写入器的作用是减少检查点密集写入的影响。后台写入器继续一点一点地刷新脏页 ,对数据库活动的影响最小。默认情况下 ,后台写入器每 200 毫秒唤醒一次(由 bgwriter_delay 定义)并最多刷新 bgwriter_l ru_maxpages (默认为 100 页)。

为什么检查点与后台编写器分离?

在9.1或更早的版本中,后台编写器定期进行检查点处理。在9.2版本中 ,检查点进程已与后台写入进程分离。 由于标题为“ Separating bgwriter and checkpointer ”的提案中描述了原因, 因此其中的句子如下所示。

目前(2011 年) bgwriter 进程执行后台写入、检查点和其他一些职责。这意味着我们无法在不停止后台写入的情况下执行最终检查点 fsync , 因此在一个进程中执行这两件事会对性能产生负面影响。此外 ,我们在 9.2 中的目标是用锁存器取代轮询循环以降低功耗。 bgwriter 循环的复杂性很高 ,似乎不太可能提出使用锁存器的干净方法。

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

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

相关文章

OSG动画与声音-路径动画之导出与导入(2)

路径的导出示例 路径的导出示例的代码如程序清单10-2所示。 1. // 创建路径 2. osg::ref_ptr<osg::AnimationPath> createAnimationPath(osg::Vec3 ¢er, 3. float radius, float looptime) 4. { 5. // 创建一个Path对象 6. osg::ref_ptr<…

【史上最细教程】服务器MySQL数据库完成主从复制

文章目录 MySQL完成主从复制教程准备&#xff1a;原理&#xff1a;步骤&#xff1a; 推荐文章 MySQL完成主从复制教程 主从复制&#xff08;也称 AB 复制&#xff09;就是将一个服务器&#xff08;主服务器&#xff09;的数据复制到一个或多个MySQL数据库服务器&#xff08;从…

云安全之盾:ZStack 云主机安全防护解决方案全方位保护云环境

随着云计算的蓬勃发展&#xff0c;网络威胁愈发复杂&#xff0c;涵盖了从勒索病毒到APT攻击的各种威胁类型。在这一风云变幻的网络安全环境下&#xff0c;云主机安全不再仅仅是一个选项&#xff0c;它是信息系统安全的核心要素。云轴科技ZStack 云主机安全防护解决方案是为了满…

java项目之品牌银饰售卖平台(ssm+vue)

项目简介 主要功能包括首页、个人中心、用户管理、促销活动管理、饰品管理、我的收藏管理、系统管理、订单管理等。管理员模块: 管理员可以查询、编辑、管理每个用户的信息和系统管理员自己的信息&#xff0c;同时还可以编辑、修改、查询用户账户和密码&#xff0c;以及对系统…

项目管理套路:看这一篇绝对够用❤️

写论文必不可少的&#xff0c;就是创建代码并进行实验。好的项目管理可以让实验进行得更加顺利。本篇博客以一次项目实践为例&#xff0c;介绍项目管理的方法&#xff0c;以及可能遇到的问题&#xff0c;并提供一些可行的解决方案。 目录 项目管理工具开始第一步版本管理十分关…

微信小程序:This Mini Program cannot be opened as your Weixin version is out-of-date.

项目场景&#xff1a; 问题描述 升级基础库3.2.0&#xff0c;然后PC端整个小程序都打不开了&#xff0c;点击小程序提示”This Mini Program cannot be opened as your Weixin version is out-of-date. Update Weixin to the latest version.“&#xff0c;并且点击Update Wei…

成为AI产品经理——模型构建流程(下)

目录 1.模型训练 2.模型验证 3.模型融合 4.模型部署 上节课我们讲了模型设计、特征工程&#xff0c;这节课我们来讲模型构建剩下的三个部分&#xff1a;模型训练、模型验证和模型融合。 1.模型训练 模型训练就是要不断地训练、验证、调优直至让模型达到最优。 那么怎么达…

Linux系统装宝塔面板提示磁盘空间不足如何清理,检测到当前磁盘超过80%,空间不足如何清理

Linux系统装宝塔面板提示磁盘空间不足如何清理&#xff0c;检测到当前磁盘超过80%&#xff0c;空间不足如何清理 今天登陆服务器查看到磁盘空间满&#xff0c;如题这部分属于运维问题&#xff0c;如果直接点击 如何直接点立即清理会跳转企业版购买&#xff0c;1599CNY对普通用…

如何做一个简单的深度集成学习框架

使用同一个框架&#xff0c;独立在一个数据集上面&#xff0c;分别训练多次&#xff0c;每个单独模型训练超参数可以一样&#xff0c;也可以不一样&#xff0c;最后若干个训练好的独立模型在测试集数据上面做最后集中决策。 实例代码如下&#xff1a; class MyEnsemble(nn.Modu…

CVE-2023-6099:优卡特脸爱云一脸通智慧管理平台SystemMng.ashx接口未授权漏洞复现

文章目录 优卡特脸爱云一脸通智慧管理平台未授权SystemMng.ashx接口漏洞复现&#xff08;CVE-2023-6099&#xff09; [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 0x06 修复建议 优卡特脸爱云一脸通智慧管理平台未授权…

eNSP-远程登陆实验

实验拓扑&#xff1a; 实验需求&#xff1a; 1. 如图所示&#xff0c;配置设备名称和IP地址 2. 配置R2的远程登录vty接口0-4&#xff0c;完成以下需求&#xff1a; 1&#xff09;采用接口password的验证方式&#xff0c;口令为"spoto"&#xff08;不含引号&#xf…

为何越来越多的程序员纷纷转行网络安全?

目前&#xff0c;我国IT行业的人才结构不断升级&#xff0c;公司对程序员的要求越来越高&#xff0c;出现了大量的裁员现象&#xff0c;导致很多的程序员纷纷想转行的想法。 可能对于早期的程序员而言&#xff0c;学好编程语言就能找到比较好的工作。而现在伴随着互联网的不断发…

美国大学陪读签证申请条件是什么?

美国大学陪读签证是留学生的家庭成员在美国陪同留学生就读期间的签证类型。申请该签证需要满足一定的条件&#xff0c;这些条件通常包括&#xff1a; 1.申请人身份关系证明&#xff1a;申请人必须是留学生的家庭成员&#xff0c;如配偶或子女&#xff0c;需要提供证明文件&…

Lua脚本解决redis实现的分布式锁多条命令原子性问题

线程1现在持有锁之后&#xff0c;在执行业务逻辑过程中&#xff0c;他正准备删除锁&#xff0c;而且已经走到了条件判断的过程中&#xff0c;比如他已经拿到了当前这把锁确实是属于他自己的&#xff0c;正准备删除锁&#xff0c;但是此时他的锁到期了&#xff0c;那么此时线程2…

【深度学习实验】图像处理(一):Python Imaging Library(PIL)库:图像读取、写入、复制、粘贴、几何变换、图像增强、图像滤波

文章目录 一、实验介绍二、实验环境1. 配置虚拟环境2. 库版本介绍 三、实验内容0. 安装 PIL 库1. 图像读取和写入a. 图像读取b. 图像写入c. 构建新图像 2. 图像复制粘贴a. 图像复制b. 图像局部复制c. 图像粘贴 3. 几何变换a. 图像调整大小b. 图像旋转c. 图像翻转 4. 图像增强a.…

QGIS之二十五两个面图层数据中选择图形完全一致的数据

效果 步骤 1、准备数据 2、按位置选择 在Qgis工具箱中搜索"按位置选择"工具 选择要素和比较要素根据实际选择 运行 3、结果

FreeRTOS学习之路,以STM32F103C8T6为实验MCU(2-3:任务切换)

学习之路主要为FreeRTOS操作系统在STM32F103&#xff08;STM32F103C8T6&#xff09;上的运用&#xff0c;采用的是标准库编程的方式&#xff0c;使用的IDE为KEIL5。 注意&#xff01;&#xff01;&#xff01;本学习之路可以通过购买STM32最小系统板以及部分配件的方式进行学习…

WorkPlus解放企业应用开发,实现跨平台无缝切换

在移动应用的快速发展时代&#xff0c;企业对于自主开发和应用平台替换的需求日益增长。WorkPlus作为一个完整的移动数字化APP底座&#xff0c;为企业提供了独立自主的应用开发环境&#xff0c;实现了平台无关性和应用解耦。今天&#xff0c;我们将深入探讨WorkPlus在企业应用开…

基于 Flink SQL 和 Paimon 构建流式湖仓新方案

本文整理自阿里云智能开源表存储负责人&#xff0c;Founder of Paimon&#xff0c;Flink PMC 成员李劲松在云栖大会开源大数据专场的分享。本篇内容主要分为四部分&#xff1a; 数据分析架构演进介绍 Apache PaimonFlink Paimon 流式湖仓流式湖仓Demo演示 数据分析架构演进 …

【C语言:深入理解指针二】

文章目录 1. 二级指针2. 指针数组3. 字符指针变量4. 数组指针变量5. 二维数组传参的本质6. 函数指针变量7. 函数指针数组8. 转移表9. 回调函数10. qsort函数的使用与模拟实现 1. 二级指针 我们知道&#xff0c;指针变量也是变量&#xff0c;它也有自己的地址&#xff0c;使用什…