MySQL进阶之(三)InnoDB数据存储结构之数据页结构

news2024/11/16 7:46:36

三、InnoDB数据存储结构之数据页结构

  • 3.1 数据库的存储结构
    • 3.1.1 MySQL 数据存储目录
    • 3.1.2 页的引入
    • 3.1.3 页的概述
    • 3.1.4 页的上层结构
  • 3.2 数据页结构
    • 3.2.1 文件头和文件尾
      • 01、File Header(文件头部)
      • 02、File Trailer(文件尾部)
    • 3.2.2 空闲空间、用户记录、最大最小记录(上下确界)
      • 01、Free Space(空闲空间)
      • 02、User Record(用户记录)
      • 03、Infimum + Supremum(最小最大记录)
    • 3.2.3 页目录、页面头部
      • 01、Page Directory(页目录)
      • 02、Page Header(页面头部)
  • 3.3 从数据页的角度看 B+ 树如何查询
    • 3.3.1 B+ 树是如何进行记录检索的?
    • 3.3.2 普通索引和唯一索引在查询效率上有什么不同?

3.1 数据库的存储结构

前文中说到,索引结构给我们提供了高效的查询方式,而索引信息以及数据记录都是保存在文件上的,确切地说是保存在页结构中。

索引是在存储引擎中实现的,MySQL 服务器上的存储引擎负责对表中的数据进行读取和写入。但是不同的存储引擎对数据的存放格式一般是不同的,比如:InnoDB 是将数据存储到磁盘上、Memory 直接将数据存储在内存中…

注:MySQL 默认的存储引擎是 InnoDB,所以此文章以 InnoDB 存储引擎展开。

3.1.1 MySQL 数据存储目录

使用命令查看目录路径:

show variables like 'datadir';
# 或
select @@datadir

在这里插入图片描述
这个是修改后的目录路径,实际上 MySQL 默认的存储路径为:/var/lib/mysql

我们每创建一个数据库 database_name,这个目录下(包括自定义的)就会创建一个以数据库名为名的目录,然后里面存储表结构和表数据文件。

InnoDB 存储引擎创建的任何一张表的都会有两个文件:

  • test_table.frm:存储表结构的文件,保存表的原数据信息。
  • test_table.ibd:存储表数据的文件,表数据既可以存储在共享表空间文件中(ibdata1中),也可以存储在独占表空间中(后缀 .idb中)。是否存储在独占表中,可以通过参数 innodb_file_per_table控制,设置为 1,则会存储在独占表空间中,从 MySQL 5.6.6 版本之后,innodb_file_per_table 默认值就为 1 了,因此此后的版本,表数据都是存储在独占表中的。

3.1.2 页的引入

为什么会提到页呢?

InnoDB 是一个将表中的数据存储到磁盘上的存储引擎,即使我们关闭并重启服务器,数据还是存在。而真正处理数据的过程发生在内存中,所以需要把磁盘中的数据加载到内存中。如果是要处理写入或修改请求,还需要把内存中的内容刷新到磁盘上。而我们知道读写磁盘的速度是非常慢的,与读写内存差了几个数量级。当我们想从表中获取某些记录时,InnoDB 存储引擎需要一条一条地把记录从磁盘上读出来么?这样会慢死,InnoDB 采取的方式是,将数据划分为若干个页,以页作为磁盘和内存之间交互的基本单位。 —— 摘自《MySQL是怎样运行的》

InnoDB 中页的大小一般为 16KB,一般情况下,一次最少从磁盘中读取 16KB 的内容到内存中,一次最少把内存中 16KB 的内容刷新到磁盘中。也就是说,数据库 I/O 操作的最小单位是页每次刷盘时都必须是页的整数倍

SQL Server 中页的大小为 8KB。
Oracle 中用 “块” 来代表页,支持的块大小有 2KB、4KB、8KB(默认)、16KB、32KB、64KB。

3.1.3 页的概述

将数据划分为若干个页,这些页可以不在物理结构上相连,只需要通过双向链表使其在逻辑结构上相连即可。每个数据页中的记录都会按照主键值从小到大的顺序组成一个单向链表,每个数据页都会为存储在它里边的记录生成一个页目录,再通过主键查找某条记录的时候可以在页目录中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录。

在 InnoDB 中,默认页的大小是 16KB,可以通过命令来查看:

show variables like '%innodb_page_size%'
# 或者
select @@innodb_page_size

在这里插入图片描述

3.1.4 页的上层结构

另外,在数据库中,还存在着区、段、表空间的概念。行、页、区、段、表空间的关系:
在这里插入图片描述

  • 区(Extent)是比页大一级的存储结构,在 InnoDB 存储引擎中,一个区会分配 64 个连锁的页,因为页默认是 16KB,所以一个区的大小是 64 * 16KB = 1MB。
  • 段(Segment)是由一个或多个区组成,区在文件系统是一个连续分配的空间,但是在段中不要求区与区之间是相邻的。段是数据库中的分配单位,不同类型的数据库对象以不同的段形式存在。当我们要创建数据表、索引的时候,就会相应地创建对应的段(索引段、表段…)。
  • 表空间(Tablespace)是一个逻辑容器,表空间存储的对象是段,在一个表空间中可以有一个或多个段,但是一个段只能属于一个表空间。数据库由一个或多个表空间组成,表空间从管理上可以分为系统表空间、用户表空间、撤销表空间、临时表空间等。

3.2 数据页结构

页如果按照类型划分的话,常见的有:数据页(保存 B+ 树节点)、系统页、Undo 页、事务数据页等。其中,数据页是我们最常使用的页。

前面也有提到,数据页的大小为 16KB,这 16KB 大小的存储空间被划分为七个部分:

  1. 文件头部(File Header):占用 38 字节,主要描述页的一些通用信息;
  2. 页面头部(Page Header):占用 56 字节,专门存储页的各种状态信息;
  3. 页面中的最小记录和最大记录(Infimum + Supremum):占用 26 字节,这是两个虚拟的行记录;
  4. 用户记录(User Records):占用空间由实际的记录数据决定,主要存储行记录内容;
  5. 空闲空间(Free Space):占用空间由用户记录决定,是页中还没有被使用的空间;
  6. 页目录(Page Directory):占用空间由用户记录有关,存储用户记录的相对位置,以便于查找;
  7. 文件尾部(File Trailer):占用 8 字节,校验页是否完整。

在这里插入图片描述
可以将以上 7 个结构归为以下 3 个部分。

3.2.1 文件头和文件尾

01、File Header(文件头部)

文件头部占用 38 字节,主要描述页的一些通用信息,比如:页的编号、其上一页页号、下一页页号…

File Header 的结构及描述如下:
在这里插入图片描述
其中,我们需要重点关注一下标黄的属性。

FIL_PAGE_OFFSET(4字节):页号,好比我们的身份证号,用来定位唯一的页

FIL_PAGE_TYPE(2字节):代表当前页的类型。前面说过,InnoDB 为了不同的目的而把页分为不同的类型,除了我们常用的数据页之外,还有很多其他类型的页。
在这里插入图片描述

FIL_PAGE_PREV(4字节)和 FIL_PAGE_NEXT(4字节):InnoDB 都是以页为单位存放数据的,如果数据分散到多个不连续的页中存储,就需要把这些页关联起来,FIL_PAGE_PREV 和 FIL_PAGE_NEXT 就分别代表本页的上一页和下一页的页号
通过建立一个双向链表,保证这些页与页之间在逻辑上连接而非物理连接。在这里插入图片描述

FIL_PAGE_SPACE_OR_CHKSUM(4字节):代表当前页面的校验和(checksum)
什么是校验和?
就类似于一个很长的字符串,通过某种算法计算出一个比较短的值来代表这个字符串,这个比较短的值就是校验和。
为什么要使用校验和?
在比较两个很长的字符串时,如果直接进行比较的话,肯定会比较慢的。但是,如果通过比较两个字符串的校验和(其中,生成校验和耗时可以忽略不计):校验和相同就代表两个字符串相同,反之则不同。这种方式很明显会缩短比较时的耗时。
校验和在页面上有什么作用?
校验和这个属性是存在于文件头部和文件尾部的。InnoDB 以页为单位把数据加载到内存中处理,如果该页中的数据在内存中被修改了,那么在修改后的某个时间就需要把数据同步刷新到磁盘中了。但是同步时可能会出现断电、磁盘损坏等一系列不可抗因素,从而造成数据页传输的不完整。
为了检测一个页是否刷盘成功,就可以通过文件头部的校验和与文件尾部的校验和做对比,如果两个值不相等则说明页刷盘失败,需要重新刷盘(数据重试或回滚操作);否则认为页刷盘成功。

FIL_PAGE_LSN(8字节):页面被最后修改时对应的日志序列位置(Log Sequence Number,简称:LSN)。

02、File Trailer(文件尾部)

  • 前 4 个字节:代表页的校验和,这个部分与 File Header 中的校验和相对应。
  • 后 4 个字节:代表页面被最后修改时对应的日志序列位置(LSN),这个部分也是为了校验页的完整性,如果首部和尾部的 LSN 值校验不成功的话,也说明同步过程出现了错了(刷盘失败)。

3.2.2 空闲空间、用户记录、最大最小记录(上下确界)

页的主要作用是存储记录,所以,用户记录最大最小记录占了页结构的主要空间。
在这里插入图片描述

01、Free Space(空闲空间)

当我们存储一条数据的时候,存储的记录会按照指定的行格式存储到 User Records 部分。但是在最开始生成页的时候,其实是没有 User Record 这个部分的。当每次插入一条记录时,都会从 Free Space(空闲空间)中申请一个记录大小的空间划分到 User Record 部分。当 Free Space 的空间被划分完之后,也就意味着这个页使用完了,如果还有新的记录插入的话,就需要去申请新的页了。
在这里插入图片描述

02、User Record(用户记录)

User Record 中的这些记录按照指定的行格式一条一条地摆在 User Record 部分,相互之间形成单链表。

至于这里的一条一条记录是如何存储的,可以查看:InnoDB 行格式。

03、Infimum + Supremum(最小最大记录)

对于一条完整的记录来说,比较记录的大小就是比较主键的大小,记录会按照主键值从小到大的顺序形成一个单向链表。

InnoDB 规定的最小记录和最大记录这两条记录的构造其实很简单,都是由 5 字节大小的记录头信息和 8 字节大小的一个固定的部分组成的:
在这里插入图片描述
这两条记录其实不是用户自己插入的,而是在生成页的时候默认自动创建的,并称为伪记录虚拟记录,它们并不存放在 User Record 部分,而是单独存放在 Infimum + Supremum 部分:
在这里插入图片描述
这里记录头信息中有一个属性:heap_no(记录在堆中的相对位置)。InnoDB 把记录一条一条亲密无间排列的结构称之为堆,其实此属性就是当前记录在本页中的位置,并把这两条伪记录的值分别设为 0 和 1。

3.2.3 页目录、页面头部

01、Page Directory(页目录)

为什么需要页目录?

在页中,记录是以单向链表的形式进行存储的。单向链表的特点就是插入、删除非常方便,但是检索效率不高,最差的情况下需要遍历链表上的所有节点才能完成检索。所以在页结构中专门设计了页目录这个模块,专门给记录做一个目录,通过二分查找法的方式进行检索,提升效率

假设现在有一条查询语句:

select * from page_demo where c1 = 3;

根据主键查找页中的某条记录,如何实现快速查找呢?

⭐ 方式一

顺序查找:从 Infimum 记录(最小记录)开始,沿着链表一直往后找,数据量非常大的时候,性能非常差。

⭐ 方式二

使用页目录,二分法查找。

  1. 将所有的记录分成若干个组,这些记录中包含最小记录和最大记录,但不包括被标记为删除的记录。
  2. 第 1 组只有一条记录,最小记录所在的组。最后一组,也就是最大记录所在的分组,会有 1-8 条记录。其他分组,会有 4-8 条记录。【这样做的好处是除了第 1 组外,其余组的记录数会尽量平分】。
  3. 每个组中的最后一条记录的头信息中会存储该组中一共有多少条记录,来作为 n_owned 字段的值。
  4. 页目录用来存储最后一条记录的地址偏移量,这些地址偏移量会按照顺序存储起来,每组的地址偏移量也被称为槽(Slot),每个槽相当于指针指向了不同组的最后一条记录。

假设现在的 page_demo 表中正常的记录共有 6 条,InnoDB 会把它们分成两组,第一组中只有一个最小记录,第二组中是剩余的 5 条记录。分组后:
在这里插入图片描述

上图的槽位:

  • 槽 0:指向的是最小记录的地址偏移量。
  • 槽 1:指向的是最大记录的地址偏移量。

用指针代替数字后:
在这里插入图片描述

再换个角度看,单纯从逻辑上看一下这些记录和页目录的关系:
在这里插入图片描述
对页目录有个大致的了解后,引申出两个问题:

问题一:页目录分组的个数是如何确定的?

在上述分组中,为什么最小记录的 n_owned 为 1,而最大记录的 n_owned 的值为 5 呢?

InnoDB 规定:对于最小记录所在的分组只能有 1 条记录,最大记录所在的分组拥有的记录条数只能在 1~8 条之间,剩下的分组中记录的条数范围只能是在 4~8 条之间。

分组的步骤是这样的:

  • 初始情况下只有两条记录(两个槽):最大记录和最小记录,它们分别属于两个组。
  • 每当插入一条记录,都会从页目录中找到主键值比本身记录的主键值大并且差值最小的槽(从本质上来说,槽是一个组内最大的那条记录在页面中的地址偏移量,通过槽可以快速找到对应的主键值),然后把该槽对应记录的 n_owned 值加一(表示本组内又添加了一条记录),直到该组中的记录数等于 8 个。
  • 当一个组中的记录数等于 8 后,再插入一条记录时,会将组中的记录拆分成两个组,其中一个组中 4 条记录,另一个 5 条记录。这个拆分的过程会在页目录中新增一个槽 ,记录这个新增分组中最大的那条记录的偏移量。

问题二:页目录结构下如何快速查找记录?

假设现在新增了 12 条数据(模拟大数据量下查找记录的过程):

INSERT INTO page_demo 
VALUES
(5, 500, 'zhou'), 
(6, 600, 'chen'), 
(7, 700, 'deng'), 
(8, 800, 'yang'), 
(9, 900, 'wang'), 
(10, 1000, 'zhao'), 
(11, 1100, 'qian'), 
(12, 1200, 'feng'), 
(13, 1300, 'tang'), 
(14, 1400, 'ding'), 
(15, 1500, 'jing'), 
(16, 1600, 'quan');

新添加 12 条记录后,页里共有 18 条记录了(包括最大记录和最小记录),这些记录被分成了 5 组:
在这里插入图片描述
这里为了方便展示,只保留了 16 条记录的记录头信息中的 n_owned 和 next_record 属性,省略了各个记录之间的箭头。

这五个槽位的编号分别为:0、1、2、3、4,所以最初情况下最低的槽就是 low = 0,最高槽就是 high = 4。由于各个槽代表的记录的主键值都是从小到大排序的,所以可以采用二分法来进行快速查找主键值为 6 的记录:

  1. 计算中间槽的位置:(0 + 4)/ 2 = 2,所以查看槽 2 对应记录的主键值为 8,而 8 > 6,所以设置 high = 2,low 保持不变。
  2. 重新计算中间槽的位置:(0 + 2)/ 2 = 1,所以查看槽 1 对应记录的主键值为 4,而 4 < 6,所以设置 low = 1,high 保持不变。
  3. 因为 high - low 的值为 1,所以确定主键值为 6 的记录在槽 2 对应的组中。沿着槽 2 中主键值所在的单链表中遍历即可得到主键值为 6 的记录。

由于每个组中包含的记录的条数只能是 1~8 条,所以遍历一个组中的记录的代价是很小的。

在一个数据页中查找指定主键值的记录的过程分为两步:

  1. 通过二分法在页目录中确定要查找的主键所在槽位的上一个槽位,并找到该槽所在分组中主键值最大的记录;
  2. 从当前主键值最大的记录开始,通过 next_record 属性往后遍历,直到找到要查找的记录为止。

02、Page Header(页面头部)

为了能得到一个数据页中存储的记录的状态信息,比如:

  • 本页中已经存储了多少条记录?
  • 第一条记录的地址是什么?
  • 页目录中存储了多少个槽?…

特意在页中定义了一个叫 Page Header 的部分,这个部分占用固定的 56 个字节,专门存储各种状态信息。

有这些属性:
在这里插入图片描述

PAGE_DIRECTION:记录插入的方向。
比如:新插入的一条记录的主键值比上一条记录的主键值大,就说这条记录的插入方向是右边;反之则是左边。

PAGE_N_DIRECTION:一个方向连续插入的记录数量。
假设连续几次插入新纪录的方向都是一致的,InnoDB 会把沿着同一个方向插入记录的条数记录下来,这个条数就使用此状态表示。但如果最后一条记录的插入方向改变了的话,这个状态的值是会被清零重新统计的。

3.3 从数据页的角度看 B+ 树如何查询

一颗 B+ 树按照节点类型可以分成两部分:

  1. 叶子节点:B+ 树最底层的节点,节点的高度为 0,存储行记录。
  2. 非叶子节点:节点的高度大于 0,存储索引键和页面指针,并不存储行记录本身。

在这里插入图片描述

3.3.1 B+ 树是如何进行记录检索的?

通过 B+Tree 的索引查询记录,首先是从根节点开始逐层检索,直到找到记录所在的叶子节点,然后将整个数据页从磁盘中加载到内存中,页目录中的槽(slot)可以通过 二分查找的方式定位到记录所在的槽(分组),通过链表遍历的方式查找到记录。

3.3.2 普通索引和唯一索引在查询效率上有什么不同?

唯一索引其实就是在普通索引上增加了约束,也就是关键字唯一,找到关键字之后就停止检索。

普通索引存在用户记录中的关键字相同的情况。根据页结构的原理,当我们读取一条记录的时候,不是单独将这条记录从磁盘中读出去,而是将这个记录所在的页加载到内存中进行读取,而一个页中可能存储着上千个记录。因为普通索引可能存在关键字重复的情况,所以查找到关键字后仍需往后再多几次 “判断下一条记录” 的操作,而此时页已经被加载到内存中去了,所以不会涉及 I/O 操作了,而在内存中判断所消耗的时间是可以忽略不计的。

所以,对一个索引字段进行检索时,采用普通索引还是唯一索引在检索效率上基本是没有区别的

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

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

相关文章

麒麟银河操作系统V10部署ffmpeg(也能用于Linux系统)

麒麟银河操作系统V10部署ffmpeg(也能用于Linux系统) 部署ffmpeg用来处理视频的各种操作 想使用ffmpeg&#xff0c;要先安装nasm&#xff0c;yasm&#xff0c;x264之后&#xff0c;否则会报错 nkvers 查看麒麟操作系统版本 cat /proc/version #查看linux版本信息 uname -a …

C++:菱形继承问题

目录 1、什么是菱形继承 2、虚拟继承 3、一些常见问题 1. 什么是菱形继承&#xff1f;菱形继承的问题是什么&#xff1f; 2. 什么是菱形虚拟继承&#xff1f;如何解决数据冗余和二义性的 3. 继承和组合的区别&#xff1f;什么时候用继承&#xff1f;什么时候用组合&#…

模拟栈(数组实现)

题目描述&#xff1a; 代码模板&#xff1a; //push插入操作 void push(int x) {//让栈顶元素从0开始stk[ tt] x; }//pop弹出操作 void pop() {//直接把这个元素跳过&#xff08;相当于弹出&#xff09;tt--; }//empty判断是否为空操作 bool empty() {if(tt > 0) return …

程序员是如何看待“祖传代码”的?

目录 ​编辑 程序员是如何看待“祖传代码”的&#xff1f; 一、什么是“祖传代码”&#xff1f; 二、“祖传代码”的利弊 1. 可以节省开发成本 2. 可能引入安全隐患 3. 可能增加系统的维护难度 三、祖传代对程序员的影响 1. 丰富程序员的技能和知识 2. 提高程序员的创…

在两台CentOS 7服务器上部署MinIO集群---准确

环境说明&#xff1a; 2台Centos7服务器 IP地址分别为172.16.1.9和172.16.1.10 1. 创建minio用户和目录 在两台服务器上执行以下命令&#xff1a; sudo useradd -m -d /app/minio minio sudo mkdir -p /app/minioData sudo mkdir -p /app/minio/logs sudo chown -R mini…

范伟:你们怎么老提1,200呢,有什么典故啊?赵本山:没有啊!

范伟&#xff1a;你们怎么老提1,200呢,有什么典故啊?赵本山&#xff1a;没有啊&#xff01; --小品《面子》&#xff08;中3&#xff09;的台词 表演者&#xff1a;赵本山 高秀敏 范伟 &#xff08;接上&#xff09; 范伟&#xff1a;哎吃啊 赵&#xff1a;哎呀这电视看的挺…

什么是端点安全以及如何保护端点

什么是端点安全 端点是指可以接收信号的任何设备&#xff0c;是员工使用的一种计算设备&#xff0c;用于保存公司数据或可以访问 Internet。端点的几个示例包括&#xff1a;服务器、工作站&#xff08;台式机和笔记本电脑&#xff09;、移动设备、虚拟机、平板电脑、物联网、可…

Vue3_2024_1天【Vue3创建和响应式,对比Vue2】

前言&#xff1a; Vue3对比Vue2版本&#xff0c;它在性能、功能、易用性和可维护性方面都有显著的提升和改进。 性能优化&#xff1a;模板编译器的优化、对Proxy的支持以及使用了更加高效的Virtual DOM算法等。这使得Vue3的打包大小减少了41%&#xff0c;初次渲染提速55%&#…

【基础训练 || Test-1】

总言 主要内容&#xff1a;一些习题。       文章目录 总言一、选择1、for循环、操作符&#xff08;逗号表达式&#xff09;2、格式化输出&#xff08;转换说明符&#xff09;3、for循环、操作符&#xff08;逗号表达式、赋值和判等&#xff09;4、if语句、操作符&#xff…

如何根据PalWorldSettings.ini重新生成定制的WorldOption.sav文件?

这个过程涉及到将PalWorldSettings.ini 文件中的设置与WorldOption.sav 文件进行匹配和替换。具体的操作步骤可能包括检查PalWorldSettings.ini 文件中的设置是否与WorldOption.sav 文件中的设置相匹配&#xff0c;然后根据这些设置重新生成或修改WorldOption.sav 文件&#xf…

腾讯云学生云服务器_学生云主机_学生云数据库_云+校园特惠套餐

2024年腾讯云学生服务器优惠活动「云校园」&#xff0c;学生服务器优惠价格&#xff1a;轻量应用服务器2核2G学生价30元3个月、58元6个月、112元一年&#xff0c;轻量应用服务器4核8G配置191.1元3个月、352.8元6个月、646.8元一年&#xff0c;CVM云服务器2核4G配置842.4元一年&…

论文里点击如图?-?如何跳转到图片的题注

写论文&#xff0c;如何点击如图?-?然后光标自己能跳转到指定图片的题注之前呢&#xff1f; 首先&#xff0c;你要确定自己已经列好了标题&#xff0c;如几点几&#xff0c;几点几&#xff0c;比如我写到第三个章节的标题为 3.2 XXXXXXXXX 那么接下来后面的操作会出现图3-&…

Python中学习调试requests模块时出现的大坑(1)

为防止迷路: 学习机械相关,请关注公众号:南大盛联 学习软件,硬件,请关注公众号号:一训微课 cmd模式下 不知道上面这行的话,需要补课。 pip install requests 这个不知道的话,也要补课 pip是python的安装工具。 install是安装的意思 requests是我们需要安装的模…

腾讯云优惠券领取入口_先领取再下单_2024腾讯云优惠攻略

腾讯云优惠代金券领取入口共三个渠道&#xff0c;腾讯云新用户和老用户均可领取8888元代金券&#xff0c;可用于云服务器等产品购买、续费和升级使用&#xff0c;阿腾云atengyun.com整理腾讯云优惠券&#xff08;代金券&#xff09;领取入口、代金券查询、优惠券兑换码使用方法…

【C++庖丁解牛】类与对象

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 目录 1.面向过程和面向对象…

AJAX 学习笔记(Day1)

「写在前面」 本文为黑马程序员 AJAX 教程的学习笔记。本着自己学习、分享他人的态度&#xff0c;分享学习笔记&#xff0c;希望能对大家有所帮助。 目录 0 课程介绍 1 AJAX 入门 1.1 AJAX 概念和 axios 使用 1.2 认识 URL 1.3 URL 查询参数 1.4 常用请求方法和数据提交 1.5 HT…

arduino uno R3驱动直流减速电机(蓝牙控制)

此篇博客用于记录使用arduino驱动直流减速电机的过程&#xff0c;仅实现简单的功能&#xff1a;PID调速、蓝牙控制 1、直流减速电机简介2、DRV8833电机驱动模块简介3、HC-05蓝牙模块简介电机转动测试4、PID控制5、蓝牙控制电机 1、直流减速电机简介 我在淘宝购买的电机&#x…

VMware安装Centos7详细过程

1.硬件软件准备 软件&#xff1a;VMware16 硬件&#xff1a;因为是在宿主机上运行虚拟化软件安装centos&#xff0c;所以对宿主机的配置有一定的要求。最起码I5CPU双核、硬盘500G、内存4G以上。 镜像&#xff1a;centos7,镜像下载地址centos安装包下载_开源镜像站-阿里云 2…

华为数通方向HCIP-DataCom H12-821题库(多选题:21-40)

第21题 管理员在配置 VRRP 时,下面哪些不是必须配置的? A.抢占模式 B.抢占延时 C.虚拟IP 地址 D.虚拟路由器的优先级 【参考答案】ABD 【答案解析】 VRRP的作用之一是提供一个虚拟的IP地址,用作默认网关,用来实现冗余和故障转移。因此,配置虚拟IP地址是必须的。华为设备vr…

力扣1892 页面推荐Ⅱ

力扣1892&#xff0c;页面推荐Ⅱ&#xff0c;为一个社交媒体网站实施一个页面推荐系统。如果页面被user_id的 至少一个朋友喜欢 &#xff0c;而 不被user_id喜欢 &#xff0c;你的系统将 推荐 一个页面到user_id。 目录 题目描述 解题思路 完整代码 优化 题目描述 表&…