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

三、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.mzph.cn/news/713744.shtml

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

相关文章

【JavaEE】_Spring Web MVC简介

目录 1. Spring Web MVC简介 2. MVC简介 3. Spring MVC 1. Spring Web MVC简介 官网对于Spring Web MVC的介绍如下&#xff1a; 链接如下&#xff1a; https://docs.spring.io/spring-framework/reference/web/webmvc.html#https://docs.spring.io/spring-framework/refer…

将SU模型导入ARCGIS,并获取高度信息,多面体转SHP文件(ARCMAP)

问题:将Sketchup中导出的su模型,导入arcgis并得到面shp文件,进而获取各建筑的高度、面积等信息。 思路: (1)导入arcgis得到多面体 (2)转为面shp文件 (3)计算高度/面积等 1、【3D Analyst工具】【转换】【由文件转出】【导入3D文件】(在此步骤之间,建议先建立一个…

栈和队列OJ题

文章目录 一、双队列实现栈二、双栈实现队列 一、双队列实现栈 题目链接&#xff1a; https://leetcode.cn/problems/implement-stack-using-queues/description/ 题目分析&#xff1a; 栈的结构是后进先出&#xff0c;而队列的结构是先进先出&#xff0c;我们利用这个性质&a…

AI Word Helper (Chorme Extentions) AI单词助手(谷歌浏览器插件)

AI Word Helper (Chorme Extentions) AI单词助手&#xff08;谷歌浏览器插件&#xff09; 英文网站&#xff0c;划词查单词&#xff0c;还是看不懂&#xff1f;因为单词意思那么多&#xff0c;词性搞不清&#xff0c;上下文搞不清&#xff0c;出来的意思就没法用&#xff0c;G…

一个基于轮询的广告系统

无论PC 客户端还是手机客户端&#xff0c;可能会遇到需要发布一些广告&#xff0c;这些广告可能是自己开发的&#xff0c;可能是三方的&#xff0c;而且希望是比较通用&#xff0c;能随时发布&#xff0c;随时就能看到效果。 本文提供了一种基于轮询的广告系统&#xff0c;主要…

【服务器数据恢复】昆腾存储中raid5磁盘阵列数据恢复案例

服务器数据恢复环境&故障&#xff1a; 10个磁盘柜&#xff0c;每个磁盘柜配24块硬盘。9个磁盘柜用于存储数据&#xff0c;1个磁盘柜用于存储元数据。 元数据存储中24块硬盘&#xff0c;组建了9组RAID1阵列1组RAID10阵列&#xff0c;4个全局热备硬盘。 数据存储中&#xff0…

Java基于springboot的个人理财系统

基于springboot的个人理财系统 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了个人理财系统的开发全过程。通过分析个人理财系统管理的不足&#xff0c;创建了一个计算机管理个人理财系统的方案。文章介绍了个…

考研机试C++题目精选

更多内容会在godownio.github.io更新 算法练习&#xff08;C代码&#xff09; 考研上机或C语言代码笔试准备&#xff0c;暨大机试原题letcode牛客中南大等高校机试 快速幂算法 题目&#xff1a;输入一个整数 n &#xff0c;求 n^n 的个位数是多少。 快速幂算法&#xff1a;…

【复试2.293.1】c语言——基础杂项

1.define定义常量类似全局变量&#xff0c;引用是直接拼到代码中去。 2.关于e 3.参数传递 形参直接接收的是数组的起始地址 4.数组越界乱码问题 5.scanf读字符串的时候会自动在末尾放0&#xff08;结束符 6.scanf是读取输入缓冲区的数据&#xff0c;是一种拿走操作。读取若有…

文本多分类

还在用BERT做文本分类&#xff1f;分享一套基于预训练模型ERNIR3.0的文本多分类全流程实例【文本分类】_ernir 文本分类-CSDN博客 /usr/bin/python3 -m pip install --upgrade pip python3-c"import platform;print(platform.architecture()[0]);print(platform.machine…

Linux第67步_linux字符设备驱动_注册和注销

1、字符设备注册与注销的函数原型” /*字符设备注册的函数原型*/ static inline int register_chrdev(unsigned int major,\ const char *name, \ const struct file_operations *fops) /* major:主设备号&#xff0c;Limnux下每个设备都有一个设备号&#xff0c;设备号分…

【六袆 - React】Next.js:React 开发框架;Next.js开发框架的特点

Next.js&#xff1a;React 开发框架 Next.js的特点 1.直观的、基于页面的路由系统&#xff08;并支持动态路由&#xff09; Next.js 提供了基于文件系统的路由&#xff0c;意味着你可以通过创建页面文件来定义路由。 伪代码示例&#xff1a; // pages/index.js export defa…

MYSQL--JDBC优化

一.JDBC优化: 优化前提: 有时候我们并不清楚某些表当中一共有多少列,以及这些列的数据类型,这个时候我们就需要提前通过一些方法提前了解到这些数据,从而更好的进行输出 具体语句: package cn.jdbc;import java.sql.*;public class JDBCDEmo1 {public static void main(String…

【数学建模获奖经验】2023第八届数维杯数学建模:华中科技大学本科组创新奖获奖分享

2024年第九届数维杯大学生数学建模挑战赛将于&#xff1a;2024年5月10日08:00-5月13日09:00举行&#xff0c;近期同学们都开始陆续进入了备赛阶段&#xff0c;今天我们就一起来看看上一届优秀的创新奖选手都有什么获奖感言吧~希望能帮到更多热爱数学建模的同学。据说点赞的大佬…

elment-ui table表格排序后 清除排序箭头/恢复默认排序 的高亮样式

问题描述&#xff1a; 1.默认排序是按照名称升序排列&#xff08;图一&#xff09; 2.在选择了筛选项以及其他排序方式之后&#xff0c;箭头高亮是这样的&#xff08;图二&#xff09; 3.当我点击清空按钮后&#xff0c;类型清空了&#xff0c;并且传给后端的排序方式是名称/升…

探索色彩搭配的奥秘:如何选择适合产品的理想配色方案

title: 探索色彩搭配的奥秘&#xff1a;如何选择适合产品的理想配色方案 date: 2024/3/1 20:47:45 updated: 2024/3/1 20:47:45 tags: 色彩搭配品牌形象用户体验情感连接信息传达视觉层次色调选择 引言 友善的色彩搭配和色调选择是现代产品设计中不可忽视的关键因素。通过正确…

Linux yum安装pgsql出现Bad GPG signature错误

官方文档&#xff1a;https://www.postgresql.org/download/linux/redhat/ sudo yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm sudo yum install -y postgresql12-server sudo /usr/pgsql-12/bin/…

Rust使用calamine读取excel文件,Rust使用rust_xlsxwriter写入excel文件

Rust使用calamine读取已存在的test.xlsx文件全部数据&#xff0c;还读取指定单元格数据&#xff1b;Rust使用rust_xlsxwriter创建新的output.xlsx文件&#xff0c;并写入数据到指定单元格&#xff0c;然后再保存工作簿。 Cargo.toml main.rs /*rust读取excel文件*/ use cala…

Tomcat 架构

一、Http工作原理 HTTP协议是浏览器与服务器之间的数据传送协议。作为应用层协议&#xff0c;HTTP是基于TCP/IP协议来传递数据的&#xff08;HTML文件、图片、查询结果等&#xff09;&#xff0c;HTTP协议不涉及数据包&#xff08;Packet&#xff09;传输&#xff0c;主要规定了…

本地快速部署谷歌开放模型Gemma教程(基于WasmEdge)

本地快速部署谷歌开放模型Gemma教程&#xff08;基于WasmEdge&#xff09; 一、介绍 Gemma二、部署 Gemma2.1 部署工具2.1 部署步骤 三、构建超轻量级 AI 代理四、总结 一、介绍 Gemma Gemma是一系列轻量级、最先进的开放式模型&#xff0c;采用与创建Gemini模型相同的研究和技…