Mysql第二篇---InnoDB数据存储结构

Mysql第二篇—InnoDB数据存储结构

数据库的存储结构: 页

索引结构给我们提供了高效的索引方式, 不过索引信息以及数据记录都是保存在文件上的(innodb的ibd文件, MyISAM的MyI和MyD文件), 确切的说是存储在页结构中. 另一方面, 索引是在存储引擎中实现的, MySQL服务器上的存储引擎负责对表中数据的读取和写入工作. 不同存储引擎中存放的格式一般是不同的, 甚至有的存储引擎比如Memory都不用磁盘来存储数据

由于InnoDB是MySQL的默认存储引擎, 所以本章剖析InnoDB存储引擎的数据结构

磁盘与内存交互的基本单位 : 页
InnoDB将数据划分为若干个页, InnoDB中页的大小默认为16KB。
以页作为磁盘和内存之间交互的基本单位, 也就是一次最少从磁盘中读取16KB的内容到内存中, 一次最少把内存中的16KB内容刷新到磁盘中. 也就是说, 在数据库中, 不论读一行还是读多行, 都是将这些行所在的页进行加载. 也就是说, 数据库管理存储空间的基本单位是页(Page), 数据库I/O操作的最小单位是页, 一个页中可以存储多行记录。
记录是按照行来存储的, 但是数据库的读取并不以行为单位, 否则一次读取(也就是一次I/O操作)只能处理一行数据, 效率会非常低。
在这里插入图片描述

页结构概述
页a, 页b, 页c… 页n这些页可以不在物理结构上相连, 只要通过双向链表相关联即可. 每个数据页中的记录会按照主键值从小到达的顺序组成一个单向链表, 每个数据页都会为存储在它里面的记录生成一个页目录, 在通过主键查找某条记录的时候可以在页目录中使用二分法快速定位到对应的槽位, 然后再遍历该槽对应分组中的记录即可快速找到指定的记录。

  • 页目录等我们下面详细介绍页结构的会讲解
  • 注意: 这些页是同一层的页, 可以不在物理结构上相连, 但是页分配的时候确实是相连的, 但是不是同一层相连。就是B+树比如有三层,而且是聚簇索引,那么第二层就是目录项页(里面不包含一行的全部记录),第二层有好多个页,但是这些页在物理存储上并不是连续的,比如可能有三个页,分别是23,12,33 ;但是呢,如果把B+树所有层的页都算上,它是一个段连续的页,比如说从1到100;
  • 但是注意: 数据库分配的单位是段, 段又分为了索引段和数据段, 数据段就是索引结构的叶子结点层, 所以说其实叶子结点层的页是相连的。

页的大小
不同的数据库管理系统(简称DBMS)的页大小不同. 比如在MySQL的InnoDB存储引擎中, 默认页的大小是16KB, 我们可以通过下面的命令来进行查看:

show variables like '%innodb_page_size';

SQL Server中页的大小为8KB, 而在Oracle中我们用术语"块"(Block)来代表页, Oracle支持的块大小为2KB, 4KB, 8KB, 16K, 32KB, 64KB。

页的上层结构
另外在数据库中, 还存在着区(Extent), 段(Segment)和表空间(Tablespace)的概念.。行, 页, 区, 段, 表空间的关系如下图所示:
在这里插入图片描述区(Extent)是比页大一级的存储结构, 在InnoDB存储引擎中, 一个区会分配64个连续的页。因为InnoDB中的页的大小默认是16KB, 所以一个区的大小是64*16KB = 1MB。

在mysql中一个表中的数据量过大的时候,有一个优化方式是分区,就是创建表的时候使用partition by关键字,从外部来看分区就是给原表分成好多个子表。比如原表中有一个时间time字段,那么我们可以把时间是1月份的记录放到month1分区,把时间是2月份的记录放到month2分区,。。。把时间是12月份记录的放到month12分区。首先需要知道一个知识点,就是一个页中的记录行会根据我们的索引列比如就先当成主键吧去进行从小到大的排序,比如页38的主键排序是333到444,然后我们把这个页38放到区里面,现在问题来了,区里面有多个页,这些页在物理上是连续的,那么下一个页我们找谁?把谁放到区里面?需要找一个页,它里面的主键排序要从445到556,这个页就是物理上相邻的页39,那么我们一个区里面的前两个页就是页38和页39。注意聚簇索引中叶子节点中列出的所有页物理上都是相邻的;但是非叶子结点的层,页与页之间不是物理空间上相邻,仅仅只是逻辑上相邻。之前在做上面的数据库优化的时候遇到了一个问题,就是分区字段time必须要是主键或者联合主键里面的字段,这是因为这样的话能保证页里面的分区字段是连续递增的,比如说页11里面全是时间为2月份的记录,这样我们才能把页11放到month2分区里面,不然的话,假设分区字段不是联合主键或者是主键,那么就会出现页11里面的前几条时间都是6月份,而后面时间就变成了1月份,再往后又变成了10月份,这样我们这个页11就不能假如到month2分区里面了,因为month2分区里面假如的页要求里面的时间对应的月份全都是2月份。因此只要根据主键或者联合主键里面的字段分区的时候,我们的页里面的数据才会根据分区字段递增,因为页里面的记录是根据主键递增的。这样我们才能把页假如到区里面,不然的话我们的页是不配加入到区里面的。

段(Segment) 由一个或者多个区组成, 区在文件系统是一个连续分配的空间(在InnoDB中是连续的64个页), 不过在段中不要求区与区是相邻的. 段是数据库中的分配单位, 不同类型的数据库对象以不同的段形式存在, 当我们创建数据表, 索引的时候, 就会相应创建对应的段, 比如创建一张表时会创建一个表段, 创建一个索引时会创建一个索引段。

表空间(Tablespace) 是一个逻辑容器, 表空间存储的对象是段, 在一个表空间中可以有一个或者多个段, 但是一个段只能属于一个表空间。数据库由一个或者多个表空间组成, 表空间从管理上可以划分为系统表空间, 用户表空间, 撤销表空间, 临时表空间等。

页的内部结构

页如果按照类型划分的话, 常见的有数据页(保存B+树结点), 系统页, Unod页和事物数据页等, 数据页是我们最常使用的页。我们这里说的数据页包括了B+树叶子结点和非叶子结点, 但是其实也可以把非叶子结点分出去称之为目录页

数据页的16KB大小的存储空间被划分为了7个部分, 分别是文件头(File Header), 页头(Page Header), 最大最小记录(Infimum+supremum), 用户记录(User Records), 空闲空间(Free Space), 页目录(Page Directory)和文件尾(File Tailer)。
页结构的示意图如下所示:
在这里插入图片描述在这里插入图片描述
这7个部分作用分别如下: 我们简单的梳理如下表所示:
在这里插入图片描述

我们可以把这7个结构分成3个部分:
第一部分: File Header(文件头部)和File Trailer(文件尾部)
首先是文件通用部分, 也就是文件头文件尾

1. 文件头:
作用 : 描述各种页的通用信息(比如页的编号, 其上一页, 下一页是谁等等)
文件头的构成如下图:
在这里插入图片描述
我们说几个比较重要的组成:
1. fil_page_offset(4个字节) :
每一个页都有一个单独的页号, 就和你的身份证号码一样, InnoDB通过页号可以唯一定位一个页。

2. fil_page_type
这个代表当前页的类型
在这里插入图片描述
可以看到页其实是不区分数据页和索引页的, 我们说索引页也好, 说数据页也好都是代表的是索引 或者 数据。段是分为索引段和数据段的, 因为我们要区分索引和数据进行分开存储, 至于好处就是为了能多一点顺序IO, 减少随机IO。

3. fil_page_prev(4字节)和fil_page_next(4字节)
InnoDB都是以页为单位来存放数据的, 如果数据分散到多个不连续的页中存储的话需要把这些页关联起来, fil_page_prev和fil_page_next就分别代表本页的上一个和下一个页的页号. 这样通过建立一个双向链表把许许多多的页就串联起来了, 保证这些页之间不需要是物理上连续, 而是逻辑上连续 (区上的页的分配是连续的, 但是经过很多操作后, 可能逻辑连续的页物理位置就不连续了)。

fil_page_space_or_chksum(4字节)
代表当前页面的校验和(checksum)。文件头部和文件尾部都有属性fil_page_space_or_chksum。

什么是校验和?
就是对于很长的字节串来说, 我们会通过某种算法来计算一个比较短的值来代表这个很长的字符串, 这个比较短的值就称之为校验和。
在比较两个很长的字节串之前, 先比较这两个长字节串的校验和, 如果校验和都不一样, 则两个长字节串肯定是不同的, 所以省去了直接比较两个比较长的字节串的时间损耗。

校验和的作用?
InnoDB存储引擎以页为单位把数据加载到内存中处理, 如果该页中的数据在内存中被修改了, 那么在修改后的某个时间需要把数据同步到磁盘中. 但是在同步了一半的时候断电了, 造成该页传输的不完整。
为了检测一个页是否完整(也就是在同步的时候有没有发生只同步一半的尴尬情况), 这时可以通过文件尾的校验和与文件头的校验和做对比, 如果两个值不相等则证明页的传输有问题, 需要重新进行传输, 否则认为页的传输已经完成。
后面我们讲到redo日志的时候就会讲到刷盘, 以及刷盘策略, 我们开始的时候将数据加载到内存中, 对内存中数据操作之后要以页为单位将数据写回到磁盘中, 那么如何判断我们磁盘中的文件是刷新完的, 如何确保刷新的过程是正确的, 我们就要用到检验和, 修改内存中页的数据之后我们就会同时改变校验和, 那么如果刷盘的过程中刷盘一般停机了, 这个时候我们就可以判断磁盘中的页的文件头和文件尾的校验和是否相等, 如果不相等, 那不就直接说明是失败的? 就节省了性能。

具体的?
每当一个页面在内存中修改了, 在同步之前就要把它的校验和算出来. 因为File Headeer在页面的前面, 所以校验和会被首先同步到磁盘, 当完全写完时, 校验和也会被写到页的尾部, 如果完全同步成功, 则页的首部和尾部的校验和应该是一致的. 如果写一般断电了, 那么在File Header中的校验和就代表着已经修改过的页, 而在File Trailer中的校验和代表着原先的页, 二者不同则意味着同步中间出了错. 这里, 校验方式就是采用Hash算法进行校验。

5. fil_page_lsn(8字节)
页面被最后修改时对应的日志序列位置(英文名是: Log Sequence Number)。日志序列位置也是为了校验页的完整性的, 如果首部和尾部LSN值校验不成功的话, 就说明同步过程出现了问题。

文件尾(8字节)
1. 前4个字节代表页的校验和
这个部分是和文件头(File Header)中的校验和相对应的。
2.后4个字节代表页面被最后修改时对应的日志序列位置(LSN):
这个部分也是为了校验页的完整性的, 如果首部和尾部的LSN值校验不成功的话, 就说明同步过程出现了问题。

第二部分: 记录部分
第二个部分是记录部分, 页的主要作用是存储记录, 所以"最大和最小记录"和"用户记录"部分占了页结构的主要空间。记录部分分为: 1.空闲空间, 2.用户记录, 3.最小最大记录。

1. Free Space(空闲空间)
我们自己存储的记录会按照指定的行格式存储到User Records(用户记录)部分, 但是在一开始生成页的时候, 其实并没有User Records这个部分, 每当我们插入一条记录, 都会从Free Space部分, 也就是尚未使用的存储空间申请一个记录大小的空间划分到User Records部分, 当Free Space部分的空间全部被User Records部分代替掉之后, 也就意味着这个页使用完了, 如果还有新的记录插入的话, 就需要去申请新的页了。

2. User Records(用户记录)
User Records中的这些记录按照指定的行格式一条一条摆在User Records部分, 相互之间形成单链表。那么如何证明记录之间是使用的单链表? —> 这里我们要先讲解一下记录的行格式的记录头信息。

3.Infimum+Supremum(最小最大记录)
就是上阙界和下阙界。
最大记录和最小记录是一个固定的字符串信息, 所以并不是我们理解上的最大和最小记录, 只是一个链表中值最小的记录的前一个记录(最小记录), 一个是链表中值最大的记录的后一个记录(最大记录)。
记录可以比较大小吗?
是的, 记录可以比较大小, 对于一条完整的记录来说, 比较记录的大小就是比较主键的大小. 比方说我们插入的4行记录的主键值分别是: 1, 2, 3, 4, 这也就意味着这4条记录了是从小到达依次递增
InnoDB规定了最小记录与最大记录这两条记录的构造十分简单, 都是由5字节大小的记录头信息和8字节大小的一个固定的部分组成的, 如图所示:
在这里插入图片描述
这两条记录不是我们自己定义的记录, 所以它们并不存放在页的User Records部分, 它们被单独放在了一个称为Infimum+Supremum的部分, 如图所示:
在这里插入图片描述
补充:
Infimum 和 Supremum
有人可能会说了,你在 User Records 中还不是通过遍历来解决的,你就是简单的把数据分了个组而已。如果我的数据根本不在当前这个页中,那我难道还是得把之前的页中的每一条数据全部遍历完?这效率也太低了。
当然,MySQL 也考虑到了这个问题,所以实际上在页中还存在一块区域叫做 The Infimum and Supremum Records ,代表了当前页中最大和最小的记录。
有了 Infimum Record 和 Supremum Record ,现在查询不需要将某一页的 User Records 全部遍历完,只需要将这两个记录和待查询的目标记录进行比较。比如我要查询的数据 id = 101 ,那很明显不在当前页。接下来就可以通过下一页指针跳到下页进行检索。

使用Page Directory
可能有人又会说了,你这 User Records 里不也全是单链表吗?即使我知道我要找的数据在当前页,那最坏的情况下,不还是得挨个挨个的遍历100次才能找到我要找的数据?你管这也叫效率高?

不得不说,这的确是个问题,不过是一个 MySQL 已经考虑到的问题。不错,挨个遍历确实效率很低。为了解决这个问题,MySQL 又在页中加入了另一个区域 Page Directory
顾名思义,Page Directory 是个目录,里面有很多个槽位(Slots),每一个槽位都指向了一条 User Records 中的记录。大家可以看到,每隔几条数据,就会创建一个槽位。其实我图中给出的数据是非常严格按照其设定来的,在一个完整的页中,每隔6条数据就会有一个 Slot。

MySQL 会在新增数据的时候就将对应的 Slot 创建好,有了 Page Directory ,就可以对一张页的数据进行粗略的二分查找。至于为什么是粗略,毕竟 Page Directory 中不是完整的数据,二分查找出来的结果只能是个大概的位置,找到了这个大概的位置之后,还需要回到 User Records 中继续的进行挨个遍历匹配。

不过这样的效率已经比我们刚开始聊的原始版本高了很多了。

第三部分: 页部分
1. 页目录(Page Directory):
为什么需要页目录?
在页中, 记录是以单向链表的形式进行存储的, 单向链表的特点就是插入, 删除非常方便, 但是检索效率不高, 最差的情况下需要遍历链表上的所有结点才能完成检索. 因此在页结构中专门设计了页目录这个模块, 专门给记录做一个目录, 通过二分查找法的方式进行检索, 提升效率。页目录包含所有槽, 其实就是一个页中所有的槽加载一起构成页目录。

需求:根据主键值查找页中的某条记录, 如何实现快速查找呐?

select * from page_demo where c1 = 3;

方式1 : 顺序查找
从Infimum记录(最小记录)开始, 沿着链表一直往后找, 总有一天会找到(或者最终也找不到就说明是没有这条记录), 在找的时候还能投机取巧, 因为链表中各个记录的值是按照从小到达的顺序排列的, 所以当链表的某个结点代表的记录的主键值大于你想要查找的主键值时, 你就可以停止查找了, 因为该结点以后的结点的值主键依次递增。

如果一个页中存储了非常多的记录, 那么查找性能很差。

方式2 : 使用页目录, 二分法查找

页目录是一个数组结构, 我们可以在页目录中使用二分查找的方式进行搜索:

  1. 将所有的记录分成几个组, 这些记录包括最小记录和最大记录, 但是不包括标记为已删除的记录
  2. 第1组, 也就是最小记录所在的分组( 第一组就只有一条记录就是最小记录自己 )。最后一组, 就是最大记录所在的分组, 会有1-8条记录;其余的组记录数量在4-8条之间;这样做的好处是, 除了第一组(最小记录所在组)以外, 其余组的记录数会尽量平分;
  3. 在每个组中最后一条记录的头信息会存储该组一共有多少条记录, 作为n_owned字段值
  4. 页目录用来存储每组最后一条记录的地址偏移量, 这些地址偏移量会按照先后顺序存储起来, 每组的地址偏移量也被称之为槽(slot), 每个槽相当于指针指向了不同组的最后一个记录在这里插入图片描述
    那么我们如何通过页目录, 通过槽来定位到二分位置? 由于槽指向的是一组中最大的值, 所以如果我们判断到某个值比我们的上一个槽值大, 比下一个槽值小的时候, 那么我们就应该到上一个槽的位置, 上一个槽指向的就是上一个页目录中最大的, next_record指针指向的就是下一个槽中的最小值了, 因为页内是单向指针, 所以我们必须要从前往后找。

页目录分组的个数如何确定?
InnoDB规定: 对于最小记录所在的分组只能有一条记录, 最大记录所在的分组拥有的记录了条数只能在1-8条之间, 剩下的分组中记录的条数范围只能在4-8条之间。
分组是按照下边的步骤进行的:

  • 初始情况下一个数据页里只会有最小记录和最大记录这两条记录, 它们属于两个分组。
  • 之后每插入一条记录, 都会从页目录中找到主键值比本记录的主键值大并且差值最小的槽, 然后把该槽对应的记录的n_owned值加1, 表示本组内又添加了一条记录, 知道该组中的记录数等于8个。
  • 在一个组中的记录数等于8个后再插入一条记录时, 会将组中的记录拆分成两个组, 一个组中4条记录, 另一个5条记录, 这个过程会在页目录中新增一个槽位来记录这个新增分组中最大的那条记录的偏移量。

2. 页面头部(Page Header):
为了能得到一个数据页中存储的状态信息, 比如本页中已经存储了多少条记录, 第一条记录的地址值是什么, 页目录中存储了多少个槽等等, 特意在页中定义了一个叫做Page Header的部分, 这部分占用固定的56个字节, 专门存储各种状态信息
在这里插入图片描述
关于记录插入方向:
page_direction :
假如新插入的一条记录的主键值比上一条记录的主键值大, 我们说这条记录的插入方向是右边, 反之就是左边, 用来表示最后一条记录插入方向的状态就是page_direction。

page_n_direction
假设连续几次插入新记录的方向都是一致的, InnnoDB会把沿着同一个方向插入记录的条数记下来, 这个条数就用page_n_direction这个字段表示. 当然, 如果最后一条记录的插入方向改变了的话, 这个状态的值会被清零重新统计。

记录行格式的记录头:
先给出一个表:
在这里插入图片描述
给出该表中记录的行格式示意图:
在这里插入图片描述
由于表中是指明了主键的, 所以隐藏字段只有两个 : 1.transaction_id(事物id)和2. roll_pointer(回滚指针), 如果没有指明主键, 也没有指明非空且唯一的字段, 那么就会有一个row_id隐藏字段作为聚簇索引列。

记录头组成如下图:
在这里插入图片描述
预留位是没有使用的空间, 所以我们直接去掉预留位, 得到简化后的行格式示意图:

在这里插入图片描述
然后我们插入4条记录:
在这里插入图片描述
然后我们来讲解记录头组成(上面图中我们可以看到记录头一共是分为了6个部分: ):

1. delete_mask
这个属性标记着当前记录是否被删除, 占用1个二进制位:

  • 值为0 : 代表记录并没有被删除
  • 值为1 : 代表记录被删除掉了

被删除的记录为什么还在页中存储呐?
你以为它删除了, 可它还在真实的磁盘上. 这些被删除的记录之所以不立即从磁盘上移除, 是因为移除它们之后其他的记录在磁盘上需要重新排列, 导致性能消耗. 所以只是打一个删除标记而已, 所有被删除掉的记录都会组成一个所谓的垃圾链表, 在这个链表中的记录占用的空间称之为可重用空间, 之后如果有新记录插入到表中的话, 可能把这些被删除的记录占用的存储空间覆盖掉。

2. min_rec_mask
B+树的每层非叶子结点中最小记录都会添加该标记, min_rec_mask值为1。
我们自己插入的四条记录的min_rec_mask值都是0, 意味着他们都不是B+树的非叶子结点中的最小记录(我们添加的这四条记录都是叶子结点)。

我们之前在讲数据库索引(B+树索引)设计的时候就提到过, 数据记录(叶子结点)和目录项(索引)记录(非叶子结点)在记录头上有两个不同 : 1. record_type不同, 一个是数据记录, 一个是索引记录, 2. min_rec_mask不同, 非叶子结点记录的min_rec_mask值可能为1。

3. record_type
这个属性表示当前记录的类型, 一共有4种类型的记录:

  • 0 : 表示普通记录。
  • 1 : 表示B+树非叶子结点记录。
  • 表示最小记录。
  • 表示最大记录。

从图中我们也可以看出来, 我们自己插入的记录就是普通记录, 它们的record_type值都是0, 而最小记录和最大记录的record_type值分别为2和3。

注意: 最小记录和最大记录是MySQL为我们自动生成的两个记录, 最小记录和最大记录由于是MySQL自动生成的, 所以我们常常将之称之为: 虚拟记录。

4. heap_no
这个属性表示当前记录在本页中的位置。从上面(插入的数据的行记录格式图)图中可以看出, 我们插入的4条记录在本页中的位置分别是: 2,3,4,5。

怎么不见heap_no值为0和1的记录呐?
MySQL会自动给每个叶里加了两个记录, 由于这两个记录并不是我们自己插入的, 所以有时候也称之为伪记录或者虚拟记录. 这两个伪记录一个代表最小记录, 一个代表最大记录. 最小记录和最大记录了的位置最靠前, 所以最小记录和最大记录的heap_no值分别是0和1。

5. n_owned
页目录中每个组最后一条记录的头信息中会存储该组一共多少条记录, 作为n_owned字段。可以看到n_owned和页目录是密切相关的。

6. next_record
记录头信息里该属性非常重要, 它表示从当前记录的真实数据到下一条记录的真实数据的地址偏移量。

比如: 第一条记录的next_record值为32, 意味着从第一条记录的真实数据的地址处向后找32个字节便是下一条记录的真实数据。

注意 : 下一条记录指的并不是按照我们插入顺序的下一条记录, 而是按照主键值从小到大的顺序的下一条记录, 而本页中主键值最大的用户记录的下一条记录就是Supremum记录(也就是最大记录)。也即是说: 我们的最大记录虽然位置是页中的第二个位置, 第一个位置是最小记录, 但是在逻辑上却是页中的最后一条记录(也就是值最大的记录的下一条记录), 也即是物理地址靠前但是逻辑地址靠后。

删除操作举例:
在这里插入图片描述
从图中可以看出来, 删除第二条记录前后主要发生了这些变化:

  • 第2条记录并没有从存储空间中移除, 而是把该条记录的delete_mask值设置为1
  • 第2条记录的next_record的值变为了0, 意味着该记录没有下一条记录了
  • 第1条记录的next_record指向了第3条记录
  • 最大记录的n_owned值从5变成了4(原本删除之前页目录中有5条记录)

所以, 不论我们怎么对页中的记录做增删改操作, InnoDB始终会维护一条记录的单链表, 链表中的各个节点是按照主键值由小到大的顺序连接起来的.

添加操作:
在这里插入图片描述
直接复用了原来被删除记录的存储空间。

说明:
当数据页中存在多条被删除掉的记录时, 这些记录了的next_record属性将会把这些被删除掉的记录组成一个垃圾链表, 以备之后重用这部分存储空间。

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

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

相关文章

工业自动化编程与数字图像处理技术

工业自动化编程与数字图像处理技术 编程是计算机领域的基础技能,对于从事软件开发和工程的人来说至关重要。在工业自动化领域,C/C仍然是主流的编程语言,特别是用于工业界面(GUI)编程。工业界面是供车间操作员使用的,使用诸如Hal…

二叉树,堆排序及TopK问题

要讲二叉树的概念,就要先讲树的概念。 树是什么呢? 树其实是一种储存数据的结构,因为他的结构倒过来和生活中的树很相似所以才被称之为树。 这是一颗多叉树,从最顶端的节点可以找到下边的几个节点,下边的节点又可以找…

【力扣刷题】回文链表、环形链表、合并两个有序链表

🐌个人主页: 🐌 叶落闲庭 💨我的专栏:💨 c语言 数据结构 javaEE 操作系统 Redis 石可破也,而不可夺坚;丹可磨也,而不可夺赤。 刷题篇 一、回文链表1.1 题目描述1.2 思路分…

1.16.C++项目:仿muduo库实现并发服务器之HttpContext以及HttpServer模块的设计

文章目录 一、HttpContext模块二、HttpServer模块三、HttpContext模块实现思想(一)功能(二)意义(三)接口 四、HttpServer模块实现思想(一)功能(二)意义&#…

经典网络模型

Alexnet VGG VGG的启示 VGGNet采用了多次堆叠3x3的卷积核,这样做的目的是减少参数的数量。 例如,2个3x3的卷积核效果相当于1个5x5的卷积核效果,因为它们的感受野(输入图像上映射区域的大小)相同。但2个3x3卷积核的参数…

1024程序员狂欢节 | IT前沿技术、人工智能、数据挖掘、网络空间安全技术

一年一度的1024程序员狂欢节又到啦!成为更卓越的自己,坚持阅读和学习,别给自己留遗憾,行动起来吧! 那么,都有哪些好书值得入手呢?小编为大家整理了前沿技术、人工智能、集成电路科学与芯片技术、…

YOLOv8改进实战 | 更换主干网络Backbone之轻量化模型Efficientvit

前言 轻量化网络设计是一种针对移动设备等资源受限环境的深度学习模型设计方法。下面是一些常见的轻量化网络设计方法: 网络剪枝:移除神经网络中冗余的连接和参数,以达到模型压缩和加速的目的。分组卷积:将卷积操作分解为若干个较小的卷积操作,并将它们分别作用于输入的不…

PHP的学习入门建议

学习入门PHP的步骤如下: 确定学习PHP的目的和需求,例如是为了开发网站还是为了与数据库交互等。学习PHP的基础语法和程序结构,包括变量、数据类型、循环、条件等。学习PHP的面向对象编程(OOP)概念和技术。学习与MySQL…

BIO、NIO、IO多路复用模型详细介绍Java NIO 网络编程

文章目录 前言基本概念BIO过程NIO过程IO多路复用过程Java NIO编程Java NIO 核心概念Java NIO 示例 总结 前言 上文介绍了网络编程的基础知识,并基于 Java 编写了 BIO 的网络编程。我们知道 BIO 模型是存在巨大问题的,比如 C10K 问题,其本质就…

TCP/IP模型五层协议

TCP/IP模型五层协议 认识协议 约定双方进行的一种约定 协议分层 降低了学习和维护的成本(封装)灵活的针对这里的某一层协议进行替换 四/五层协议 五层协议的作用 应用层 应用层常见协议 应用层常见协议概览 基于TCP的协议 HTTP(超…

腾讯云 CODING 快速应用中心,让您 10 分钟轻松玩转 AIGC

点击链接了解详情 前言 AI 时代已经到来,与其说这是一个技术变革,不如说这是对我们工作和生活方式的全面升级。很多人已经听说过 Stable Diffusion AI 绘图和 Meta 公司推出的免费大语言模型 Llama 2,它们代表了当今最前沿的技术水平。但对于…

1.5状态压缩DP

1.小国王 在 n n nn nn的棋盘上放 k k k个国王,国王可攻击相邻的 8 8 8个格子,求使它们无法互相攻击的方案总数。 输入格式 共一行,包含两个整数 n n n和 k k k。 输出格式 共一行,表示方案总数,若不能够放置则输出…

全国产EtherCAT运动控制边缘控制器(五):IO配置与回零运动的Python+Qt开发

今天,正运动小助手给大家分享一下全国产EtherCAT运动控制边缘控制器ZMC432H如何使用PythonQT实现单轴回零运动控制开发。 01 功能简介 全国产EtherCAT运动控制边缘控制器ZMC432H是正运动的一款软硬件全国产自主可控,运动控制接口兼容EtherCAT总线和脉冲…

Elasticsearch使用——结合MybatisPlus使用ES es和MySQL数据一致性 结合RabbitMQ实现解耦

前言 本篇博客是一篇elasticsearch的使用案例,包括结合MybatisPlus使用ES,如何保证MySQL和es的数据一致性,另外使用了RabbitMQ进行解耦,自定义了发消息的方法。 其他相关的Elasticsearch的文章列表如下: Elasticsear…

【无人机】太阳能伪卫星VoLTE无人机设计(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

MySQL数据库简单安装

MySQL介绍 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下公司。MySQL 最流行的关系型数据库管理系统,在 WEB 应用方面MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管…

acme.sh: 未找到命令解决办法丨acme命令安装ssl证书

在Freessl申请的ssl证书现在都是需要acme命令了,服务器没有自带所以会出现这个报错,首先 1、安装并下载: curl https://get.acme.sh | sh -s emailmyexample.com2、进入到安装目录,创建指令别名: cd /root/.acme.sh/ alias acm…

电脑删除的视频怎么恢复?可尝试着3钟恢复办法!

无论是为了工作还是生活,我们都有可能在电脑上保存重要的视频,如宣传视频、回忆录视频等。这些视频通常包含了制作者的心血,要是被我们误删除了,很难重新拍摄,那么电脑删除的视频怎么恢复? 能。通常&#…

大托,如何站上天心南部的价值高地?

作者 | 魏启扬 陈宇航 来源 | 洞见新研社 陈飞 摄 “商贾云集于四方,市井数盈于万户”,长沙南城古往今来生生不息的热辣与烟火,每隔一段时间,都会有璀璨的迸发。 才在“加长版”黄金周释放了“不夜南城”的魅力,第…

正点原子嵌入式linux驱动开发——pinctrl和gpio子系统

在上一篇笔记中,学习编写了基于设备树的LED驱动,但是驱动的本质还是没变,都是配置LED灯 所使用的GPIO寄存器,驱动开发方式和裸机基本没区别。Linux是一个庞大而完善的系统,尤其是驱动框架,像GPIO这种最基本…