深入剖析 Git 对象底层原理

一、引言

在我们日常使用 Git 时,通常的操作是:

  • 在写完一段代码后,执行 git add命令,将这段代码添加到暂存区中
  • 然后再执行 git commitgit push 命令,将 本地 Git 版本库中的提交同步到服务器中的版本库中

Git 在中间做了什么,它如何存储不同的文件和内容,以及如何区分不同分支下的文件版本呢?日常操作对这些自动的操作都是无感的。
但是如果哪天一旦上述操作中出现了错误,需要找回自己的代码时,如果不懂 Git 其内部存储原理,是没法找回的,因此为了避免这种情况,就有必要去了解其内部的存储——Git 对象的原理。

二、Git 对象

2.1. Git 对象概述

我们知道,Git 是一个内容寻址文件系统,其核心部分是一个键值对数据库。
当我们向 Git 仓库中插入任意类型的内容时,它会返回一个唯一的键。我们可以通过该键在任意时刻再次取回插入的内容。
比如我们初始化 GitDemo ,发现 Git 对 objects 目录进行了初始化,并创建了 pack 和 info 子目录,但均为空:

$ git init GitDemo
Initialized empty Git repository in D:/GitDemo/.git/
$ find .git/objects
.git/objects
.git/objects/info
.git/objects/pack
$ find .git/objects -type f

然后创建一个 readme.txt 文本,执行 git add后会发现在 .git/objects 中新增了一个文件夹 89 和文件 dab47ae90ebdfee4e6cb3d64708cd73e9c5472

$ echo 'read me please' > readme.txt
$ git add readme.txt
$ find .git/objects -type f
.git/objects/89/dab47ae90ebdfee4e6cb3d64708cd73e9c5472

查看其文件内容,类型和大小:

$ git cat-file -p 89dab47ae90ebdfee4e6cb3d64708cd73e9c5472
read me please
$ git cat-file -t 89dab47ae90ebdfee4e6cb3d64708cd73e9c5472
blob
$ git cat-file -s 89dab47ae90ebdfee4e6cb3d64708cd73e9c5472
15

这个键值为 89dab47ae90ebdfee4e6cb3d64708cd73e9c5472的对象就是 Git 对象中的 blob 对象。而且 Git 中所有的对象都存储在 .git/objects目录(也叫做对象库)中。
这个键值是一个 SHA-1 的哈希值,由 40 个十六进制的数字组成。它是通过一个将待存储的数据外加一个头部信息一起做 SHA 算法运算而得到的校验和。40 个十六进制数字就相当于 160 比特,当用 SHA-1 对不同对象进行区分和识别时,冲突的概率就会极低,不用存储文件的具体类型,用 blob 和 SHA-1 就足以分辨不同文件内容了。
下面来看看 Git 对象的类型:

2.2. Git 对象类型

2.2.1. Blob 对象

1. Blob 对象的定义和作用

Blob(Binary Large Object,二进制大对象)是Git中的一种对象类型,用来指代某些可以包含任意数据的变量或文件。它是Git对文件内容的一种抽象表示。每个文件在Git仓库中都被表示为一个独立的Blob对象。Blob对象保存了文件的原始二进制数据,无论文件是文本文件还是二进制文件,Git都以Blob对象的形式存储它们。
比如在上一节中的 readme.txt 文本,在 Git 中就是以 blob 对象存储的:

$ git cat-file -t 89dab47ae90ebdfee4e6cb3d64708cd73e9c5472
blob
$ git cat-file -p 89dab47ae90ebdfee4e6cb3d64708cd73e9c5472
read me please
  • **当在Git仓库中添加、修改或删除某个文件时,Git会创建一个新的Blob对象来存储这个文件的内容。**这样就可以跟踪文件的变化历史,并且可以在需要时恢复到特定的文件版本。

比如我们修改 readme.txt 文本,会发现有两个 blob 对象存储 readme.txt 的两个版本:

//新增一行文本: reading
$ vi readme.txt
$ git add readme.txt
//原来版本的readme.txt内容还存在:
$ git cat-file -p 89dab47ae90ebdfee4e6cb3d64708cd73e9c5472
//新版本的readme.txt内容
$ git cat-file -p b0530c9
read me please
reading
  • 因为在修改内容后创建了新的 Blob 对象,因此 Git 可以使用 Blob 对象来进行文件比较操作。通过比较两个Blob对象的哈希值,Git可以快速确定文件内容是否发生了变化,从而进行版本控制和合并操作。
2. Blob 对象的存储方式

Blob对象在Git中的存储方式是使用对象哈希值来进行索引和存储。具体的存储方式如下:

  1. 当在 Git 仓库中添加文件并执行 git add时,Git 就会提取该内容,然后将内容进行 SHA-1 哈希计算,得到一个40个十六进制字符的哈希值。这个哈希值就是Blob对象的唯一标识符,也就是我们上节提到的键。
  2. 而后如果Blob对象是新的,Git会将它以哈希值(上面由 SHA-1 哈希计算得到的标识符)为文件名存储在对象数据库中(也就是 .git/objects 目录下)。
  3. 存储时,Git将Blob对象的内容写入一个临时文件,并将该文件的路径与哈希值相关联。(一般来说取前两位作为文件目录,剩下的 38 位作为文件名。比如 readme.txt 文件:目录是 89,文件名为 dab47ae90ebdfee4e6cb3d64708cd73e9c5472。)
  4. 此外如果 blob 对象过大,Git会对存储的Blob对象进行压缩,并将压缩后的数据写入真正的对象文件中。这些压缩的文件存储在 .git/objects/pack

2.2.2. Tree 对象

1. Tree 对象的定义和作用

Tree 对象是Git中的一种对象类型,用于表示文件和目录的组织结构。每当向Git仓库中添加一个目录时,Git会创建一个新的Tree对象来表示该目录的结构。Tree对象包含了目录中的文件和子目录的元数据,以及它们对应的Blob或Tree对象的哈希值。
比如我们接着在 GitDemo 仓库中添加目录 lib和文件 readme2.txt 并提交后,当前目录为:

│  readme.txt
│
└─lib
      readme2.txt

在 git 中的存储如下:

$ git cat-file -p master^{tree}
040000 tree dbff68a947c7cc60653ff64260b372a405939ae2    lib
100644 blob b0530c9b7360a8cea0e4af86475cac70a2985138    readme.txt

master^{tree} 语法表示 master 分支上最新的提交所指向的树对象。lib 子目录(所对应的那条树对象记录)并不是一个数据对象,而是一个指针,其指向的是另一个树对象:
image.png

$ git cat-file -p dbff68a947c7cc
//模式  对象类型   对象的SHA-1值                          文件名
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    readme2.txt

从上面可以发现,一个 Tree 对象包含一条或多条树对象记录。每条记录含有一个指向数据对象或者子目录对象的 SHA-1 指针,以及相应的模式、类型、对象名(SHA-1 值)、文件名信息:
image.png

  1. 模式(Mode):模式表示文件或目录的类型和权限。它是一个八进制数字,通常以4位表示。常见的模式包括:
  • 100644:表示普通文件,即Blob对象。
  • 100755:表示可执行文件,即Blob对象。
  • 040000:表示目录,即Tree对象。
  1. 类型(Type):类型表示列表项所指向的对象类型,即是Blob对象还是Tree对象。
  2. 对象名(Object Name):对象名是对应的Blob或Tree对象的哈希值。它是一个40个十六进制字符的字符串,用于唯一标识对象。
  3. 文件名或目录名(File/Directory Name):文件名或目录名是列表项所代表的文件或目录的名称。

如果记录中的类型为 Blob,表示该项是一个文件;如果该类型为 Tree,表示该项是一个子目录。

2. Tree 对象的存储方式

和 blob 对象的存储方式类似,Tree对象在Git中的存储方式是使用对象哈希值来进行索引和存储。当执行 git add 时 Git 内部的操作有:

  1. 构建树对象:当你在Git仓库中添加、修改或删除文件时,Git会根据当前目录结构构建一个Tree对象。该Tree对象包含了目录中的文件和子目录的元数据,以及它们对应的Blob或Tree对象的哈希值。
  2. 哈希计算:Git会对Tree对象的内容进行哈希计算,生成一个40个十六进制字符的哈希值。这个哈希值就是Tree对象的唯一标识符。
  3. 检查存储:Git会检查对象数据库中是否已存在具有相同哈希值的Tree对象。如果存在,则直接引用已有的对象;如果不存在,则进行存储。
  4. 存储对象:如果Tree对象是新的,Git会将它以哈希值为文件名存储在对象数据库中。对象数据库通常位于".git/objects"目录下。存储时,Git将Tree对象的内容以特定的格式写入一个临时文件,并将该文件的路径与哈希值相关联。
  5. 压缩和索引:Git会对存储的Tree对象进行压缩,并将压缩后的数据写入真正的对象文件中。同时,Git会更新索引文件,将Tree对象的哈希值与文件路径进行映射。

2.2.3. Commit 对象

1. Commit 对象的定义和作用

Commit对象是Git中的一种对象类型,用于记录代码仓库中的提交操作(也就是执行 git commit命令)。每个Commit对象代表一个特定的提交操作,包含了提交的元数据和指向代码快照的引用。通过Commit对象,Git能够跟踪代码修改的历史,并实现版本控制和代码回溯等功能。
如下图所示,每个 Commit 对象就是一个 version 版本,Commit 对象通过指向代码快照(也就是一个 Tree 对象)的引用,记录了代码仓库在某个特定时间点的状态。

我们再次引用上面 Tree 对象中的 GitDemo 仓库案例,此时仓库中有如下文件和目录:

│  readme.txt
│
└─lib
      readme2.txt

可以通过 git cat-file master命令查看此时的 commit 对象:

//master就是一个指向commit对象的指针, 其内部存储Commit对象的SHA-1值
$ cat .git/refs/heads/master
2b2af66549827bd6a466fe43081f406c2a12900b
$ git cat-file -p 2b2af665498
tree 2503e9e0c4f774fc5ce298f4972f0e6d3a800d6f
parent 7b34a1e750918570ed610ee1f228e83b43a1192e
author wangJw <wangJw@163.com> 1705458723 +0800
committer wangJw <wangJw@163.com> 1705458723 +0800

second commit

从上面可知,一个 commit 对象由这样几个部分组成:

  1. 指向代码快照的引用(tree):Commit对象包含一个指向代码快照的引用,通常是指向一个Tree对象。该Tree对象记录了提交时代码仓库中文件和目录的状态。
  2. 父提交(parent):Commit对象可以有一个或多个父提交,指向前一个或多个Commit对象。这构成了提交历史的链式结构。通常,一个Commit对象的父提交是它之前的一个Commit对象,除非进行了分支合并等操作。
  3. 作者(author):Commit对象记录了提交的作者信息,包括姓名和电子邮件。作者是指实际进行代码更改的人。
  4. 提交者(Committer):Commit对象还包含了提交者信息,通常与作者相同。提交者是指将更改提交到代码仓库的人。
  5. 提交消息(Commit Message):也就是 second commit 中的内容, Commit对象包含了提交时附加的可选消息,用于描述提交的目的、更改的内容、修复的问题等。提交消息可以提供其他开发者和团队成员了解提交的背景和目的。
  6. Commit对象的哈希值(SHA-1 值):每个Commit对象都有一个唯一的哈希值,用于标识该对象。

加上 commit 对象和 master 指针,可以完善在 tree 对象中的图:
image.png

2. Commit 对象的存储方式

当执行 git commit命令提交代码时,commit 对象随之创建,Git 的内部操作有:

  1. 内容提取:执行git commit命令提交代码时,Git会提取提交的相关信息,包括作者、提交者、提交时间、提交消息和父提交等。
  2. 创建对象:Git会将提交信息和父提交的引用等数据组合成一个Commit对象。
  3. 哈希计算:Git会对Commit对象的内容进行SHA-1哈希计算,得到一个40个十六进制字符的哈希值。这个哈希值就是Commit对象的唯一标识符。
  4. 检查存储:Git会检查对象数据库中是否已存在具有相同哈希值的Commit对象。如果存在,则直接引用已有的对象;如果不存在,则进行存储。
  5. 存储对象:如果Commit对象是新的,Git会将它以哈希值为文件名存储在对象数据库中。对象数据库通常位于 .git/objects 目录下。存储时,Git将Commit对象的内容写入一个临时文件,并将该文件的路径与哈希值相关联。
  6. 压缩和索引:Git会对存储的Commit对象进行压缩,并将压缩后的数据写入真正的对象文件中。同时,Git会更新索引文件,将Commit对象的哈希值与文件路径进行映射。

2.2.4. Tag 对象

1. Tag 对象的定义和作用

Tag对象是Git中的一种对象类型,用于给特定的提交打上标记。Tag对象的主要作用是标记代码仓库中的特定提交或里程碑。它可以用于记录发布版本、重要的里程碑、稳定的代码快照等。git 标签分为两种类型:轻量标签和附注标签。

轻量标签

轻量标签是指向提交对象的引用:

//创建轻量级标签
$ git tag firstTag
//查看标签:
$ git tag
firstTag

当创建了 firstTag 后,会在.git/refs/tags 目录下创建一个名为 firstTag 的文件,其内容指向当前的 commit 对象的 SHA-1 值

$ cat .git/refs/tags/firstTag
2b2af66549827bd6a466fe43081f406c2a12900b
//轻量标签指向提交对象的引用
$ git cat-file -t firstTag
commit
$ git cat-file -p firstTag
tree 2503e9e0c4f774fc5ce298f4972f0e6d3a800d6f
parent 7b34a1e750918570ed610ee1f228e83b43a1192e
author wangJw <wangJw@163.com> 1705458723 +0800
committer wangJw <wangJw@163.com> 1705458723 +0800

second commit

我们发现,轻量标签 firstTag 中的内容只有一个指向提交对象的 SHA-1 值。没有其他内容,因此无法知道何人,什么时间创建的标签。在团队开发中很容易发生混淆,因此可以用另外一种打标签的方式:附注标签

附注标签

附注标签则是仓库中的一个独立对象,使用带参数 -a-m <msg>git tag 命令:

//创建一个空提交:
$ git commit --allow-empty -m "empty commit for tagTest"
[master 8a4678f] empty commit for tagTest
//创建一个附注标签
$ git tag -m "secondTag" secondTag
//查看所有标签
$ git tag
firstTag
secondTag

这个时候再来看看 .git/refs/tags中的 secondTag 标签内容:

//查看该标签的类型
$ git cat-file -t secondTag
tag
//再来看看secondTag标签的内容
$ git cat-file -p secondTag
object 8a4678fae181c16c6f4ff0e6a618991128d86da2
type commit
tag secondTag
tagger wangJw <wangJw@163.com> 1705480524 +0800

secondTag

主要由这样几个部分组成:

  1. 标签指向的提交对象(object):附注标签对象中包含一个指向特定提交的引用。此处 object 中的值为我上面的提交对象的 SHA-1 值
  2. 标签指向对象类型(type):指向的提交对象的类型
  3. 标签名称(tag):附注标签对象包含一个唯一的标签名称,用于标识和引用该标签。
  4. 标签作者(tagger):附注标签对象记录了标签的作者信息,包括姓名和电子邮件地址。作者是指创建该标签的人。
  5. 标签消息(Tag Message):其中的 "secondTag"内容,附注标签对象包含一个可选的标签消息,用于描述标签的目的、里程碑或其他相关信息。标签消息可以提供其他开发者和团队成员了解标签的背景和用途。

我们再来看看 Tag 对象的存储方式

2. Tag 对象的存储方式

当执行带有 -a-m <msg>git tag命令时,Git 就会由如下操作:

  1. 创建对象:当你执行创建标签的操作(如git tag命令)时,Git会创建一个Tag对象。
  2. 内容提取:Tag对象包含标签的名称、类型、标签指向的提交、标签作者、标签创建时间、标签消息等信息。
  3. 哈希计算:Git会对Tag对象的内容进行SHA-1哈希计算,得到一个40个十六进制字符的哈希值。这个哈希值就是Tag对象的唯一标识符。
  4. 检查存储:Git会检查对象数据库中是否已存在具有相同哈希值的Tag对象。如果存在,则直接引用已有的对象;如果不存在,则进行存储。
  5. 存储对象:如果Tag对象是新的,Git会将它以哈希值为文件名存储在对象数据库中。对象数据库通常位于 .git/objects 目录下。存储时,Git将Tag对象的内容写入一个临时文件,并将该文件的路径与哈希值相关联。
  6. 压缩和索引:Git会对存储的Tag对象进行压缩,并将压缩后的数据写入真正的对象文件中。同时,Git会更新索引文件,将Tag对象的哈希值与文件路径进行映射。

三. Git 对象的存储

3.1 SHA-1 算法如何生成哈希值

SHA-1(Secure Hash Algorithm 1)是一种用于生成哈希值的加密算法。该算法将任意长度的输入经过散列运算转换为固定长度的输出。这个固定长度的输出就叫做对应输入内容的数字摘要或者哈希值。
那么对于 Git 对象中的 SHA-1 哈希值是如何生成的?
在 《Pro Git 2nd》这本书提到,SHA-1 哈希值是通过将待存储的数据+一个头部信息(header)一起做 SHA-1 校验运算而得到的。
而在头部信息由这些部分组成:

  • 对象类型字符串,比如"blob", “tree”, “commit”, “tag”
  • 空格
  • 数组内容的字节数
  • 空字节(null byte)

Git 会将上述的头部信息和文件原始数据拼接,来计算出 SHA-1 校验和。在 Linux 中有 sha1sum 命令可以生成 SHA1 哈希值,下面来验证一下我们生成的 SHA1 哈希值和 Git 是不是相同的:

//当前目录结构
│  a.txt
│
└─b
     c.txt

3.1.1. blob 对象的 SHA1 哈希值

先来看看 blob 对象,也就是 a.txt 对应的文件内容的 SHA1 哈希值生成过程,

//a.txt中的内容为:
$ cat a.txt
123
//字符数为3
$ git cat-file blob HEAD:a.txt | wc -c
3

其头部信息为 blob 3\000
在文件内容上加上头部信息,然后对新文件内容执行 SHA-1 哈希算法:

$ (printf "blob 3\000"; git cat-file blob HEAD:a.txt) | sha1sum
d800886d9c86731ae5c4a62b0b77c437015e00d2 *-

查看在 Git 仓库中是否找到该 SHA-1 值对应的 blob 对象

$ git cat-file -p d80088
123

说明执行 sha1 算法和 Git 操作算法得到的结果一致,验证了 Git 中 SHA-1 哈希值的生成过程

3.1.2. commit 对象的 SHA1 哈希值

此时在提交链最末端的 commit 对象内容是:

$ git cat-file commit master
tree 46bda27c4834d428a388841808fdaa7ca15a7bc1
parent 61b04b17412e1d9639db2a6b1b4e83319473a14a
author wangJW <1w@163.com> 1679818066 +0800
committer wangJW <1w@163.com> 1679818066 +0800

second commit

根据头部信息的组成,需要知道 commit 中的字符数:

$ git cat-file commit HEAD | wc -c
218

然后加上空格以及空字符串:commit 218\000,然后与 commit 对象内容拼接,将拼接后的内容计算 SHA1 校验和:

$ (printf "commit 218\000"; git cat-file commit HEAD) | sha1sum
2514fb61430ad5beea4f80e2548f1fbdfd97d74d *-

再来看看 HEAD 文件中对应的 Commit 对象以及其内容是不是与上面的 SHA1 相符:

$ cat .git/HEAD
2514fb61430ad5beea4f80e2548f1fbdfd97d74d
$ git cat-file -p 2514fb6
tree b79d07773ea2d47125f1e7078bbc8113a74a2fa7
parent 61b04b17412e1d9639db2a6b1b4e83319473a14a
author wangJW <1w@163.com> 1705493204 +0800
committer wangJW <1w@163.com> 1705493204 +0800

second commit

从结果可知,说明 Git 内部就是采用头部信息+内容利用 SHA1 算法得到的哈希值。
再来看看 tree 对象

3.1.3 tree 对象的 SHA1 哈希值

直接拿上面 commit 对象中的 tree 对象来做实验,首先查看 tree 对象中的内容和其中的字节数

$ git cat-file -p b79d0777
100644 blob d800886d9c86731ae5c4a62b0b77c437015e00d2    a.txt
040000 tree ceb3bfbba0a2f151a88628549113aa5c1be65bf5    b
//此时就是对应HEAD指针指向的树
$ git cat-file tree HEAD^{tree} | wc -c
61

然后根据头部信息+tree 对象内容信息计算 SHA-1 值:

$ (printf "tree 61\000"; git cat-file tree HEAD^{tree}) | sha1sum
b79d07773ea2d47125f1e7078bbc8113a74a2fa7 *-

发现此时计算出的 SHA-1 值和 commit 对象所指向的值完全相同,再次验证 SHA-1 生成方式。最后再来看看 tag 对象

3.1.4 tag 对象的 SHA1 哈希值

首先创建一个 tag 对象:

$ git tag -m "firstTag" firstTag
//创建成功
$ git tag
firstTag

获取这个 tag 对象的字节数,并执行 SHA1 哈希算法

$ git cat-file tag firstTag | wc -c
136

$ (printf "tag 136\000"; git cat-file tag firstTag) | sha1sum
d0c8f7e57f23b368152094bf3e57e70b3569cb13 *-

从 tag 对象的执行结果说明,SHA1 哈希值生成方式正确。

3.2 Git 对象的存储位置

从前面查看 blob 对象内容时提到过,在 Git 中的对象存储在 Git 仓库的 .git/objects 目录下。
在下列情况中,会触发 Git 存储对象的操作:

  1. git add:在执行 git add 命令暂存某个文件 时,Git 将会将文件的内容转换为一个 Blob 对象,并将该对象存储在本地对象数据库中。这个操作将文件添加到暂存区(Staging Area),为接下来的提交做准备。
  2. git commit:执行 git commit 命令时,Git 首先会创建一个新的 Commit 对象。这个 Commit 对象包含了提交的元数据信息,如作者、提交时间、提交信息等。同时,Git 会创建一个对应的根目录的 Tree 对象,记录了当前提交时仓库中所有文件的快照。最后,Git 将这个 Commit 对象存储在本地对象数据库中,并将当前分支指向该 Commit 对象,表示当前的工作状态。
  3. git tag:执行创建附注标注命令时,Git 会创建一个 Tag 对象,该对象包含标签的元数据信息,并指向一个特定的 Commit 对象。这个 Tag 对象会被存储在本地对象数据库中,以便后续引用。
  4. git merge:执行 git merge 命令时,Git 会创建一个新的 Commit 对象,该对象包含合并的元数据信息,并引用两个或多个合并的分支的 Commit 对象。Git 会将这个新的 Commit 对象存储在本地对象数据库中,并将当前分支指向该新的 Commit 对象。

四、 总结

本文通过 .git 目录角度解析 Git对象

  1. Git 对象主要有以下四种类型:Blob存储文件内容,Tree记录文件结构,Commit记录历史,Tag添加标签。
  2. Git 通过提取对象内容加头信息,使用 SHA-1 算法生成哈希值作为唯一 ID
  3. Git 对象存储于 .git/objects目录下,其中对象 ID 值前两位作为目录名,后 38 位作为文件名
  4. 在在执行暂存(add)、提交(commit)、合并(merge)、打标签(tag)等操作时都会触发 Git 对象的存储

参考资料

《Git 权威指南》

《Git Pro》

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

10分钟快速上手LLM大模型Python前端开发(三)之显示模块(二)

【计划昵称全网统一&#xff0c;代码随想随记&#xff0c;知乎无法立即修改&#xff0c;&#xff0c;】 微信公众号&#xff1a;leetcode_algos_life&#xff0c;代码随想随记 小红书&#xff1a;412408155 CSDN&#xff1a;https://blog.csdn.net/woai8339?typeblog &#xf…

服务器管理平台(5)- 数据展示

数据展示 Grafana导入MySQL数据源进行定制化数据展示&#xff0c;包括品牌分类饼图&#xff0c;详细数据列表等LayUI为开源前端框架&#xff0c;对系统概览、登录日志等信息&#xff0c;划分不同页面使用表格展示详细数据 1、Grafana 对品牌、CPU型号、内存等数据使用饼图展示…

力扣hot100 分割等和子集 变形01背包 滚动数组优化

Problem: 416. 分割等和子集 文章目录 思路&#x1f496; 01背包复杂度Code &#x1f496; 滚动数组优化复杂度Code 思路 &#x1f468;‍&#x1f3eb; 参考地址 &#x1f496; 01背包 复杂度 时间复杂度: O ( n m ) O(nm) O(nm)&#xff1a; m m m为数组元素和的一半 空…

支付宝:多线程事务怎么回滚?说用 @Transactional 可以回去等通知了!

1&#xff0c;最近有一个大数据量插入的操作入库的业务场景&#xff0c;需要先做一些其他修改操作&#xff0c;然后在执行插入操作&#xff0c;由于插入数据可能会很多&#xff0c;用到多线程去拆分数据并行处理来提高响应时间&#xff0c;如果有一个线程执行失败&#xff0c;则…

【Android】TypedArray的使用

介绍 看电池电量组件BatteryMeterView的时候看到的。 Array是个数组&#xff0c;所有TypedArray也是个容器&#xff0c;基本是用于自定义View里面的&#xff08;至少我目前见过的全部都在自定义View里面&#xff09;。 使用 1.自定义View public class RoundSeekbarView e…

vivado 预设文件、IP设置(_P)、用户参数、以太网时钟处理、GT位置限制、当前可识别板的IP列表

了解预设文件 预设文件有助于在特定配置中自定义IP核心。PS7、axi_emc和当linear_flash或DDR3_SDRAM 界面是在Vivado IP集成商的Board选项卡中选择的。预设文件使用XML格式。preset_file是为特定的Board文件定义的&#xff0c;可以是用于将预设应用于多个IP。 <ip_presets…

模拟器单窗口ip有问题?试试关闭IPV6来解决

目前应该不止雷电9有这个问题了&#xff0c;最早是看到无忧群里在说有这个问题&#xff0c;后面发现很多其他的ip软件也有同样的问题&#xff0c;很多人都遇到&#xff0c;所以做个图文教程在这里&#xff0c;没出问题的也可以设置一下&#xff0c;目前ipv6也还没普及&#xff…

数据库(银行数据库表构建)

题目&#xff1a; 通过所提供的E-R图和数据库模型图完成库表的创建&#xff0c;并插入适量的数据.要求必须使用SQL命令进行构建。 表1 UserInfo **建表** CREATE TABLE USERINFO (customerID INT AUTO_INCREMENT COMMENT 客户编号,customerName CHAR(50) CHARACTER SET utf8mb…

Unity -简单键鼠事件和虚拟轴

简单键鼠事件 — “Test_03” KeyTest 键鼠事件每帧都要监听&#xff0c;要放在Update()中处理 public class KeyTest : MonoBehaviour {// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){// 【鼠标点击事件…

【论文代码】基于隐蔽带宽的汽车控制网路鲁棒认证-到达时间间隔通道的Java实现(一)

文章目录 一、USBtin 基类1.1 CANSender 类1.1.1 SimpleSender类 1.2 CANReceiver类1.2.1 SimpleReceiver类 1.3 Noise_node类 二、CANMessageListener 接口2.1 IAT_Monitor2.2 BasicListener2.3 DLC_Monitor 三、IATBitConverter 抽象类3.1 OneBitConverter类3.2 TwoBitConver…

Bit Extraction and Bootstrapping for BGV/BFV

参考文献&#xff1a; [GHS12] Gentry C, Halevi S, Smart N P. Better bootstrapping in fully homomorphic encryption[C]//International Workshop on Public Key Cryptography. Berlin, Heidelberg: Springer Berlin Heidelberg, 2012: 1-16.[AP13] Alperin-Sheriff J, Pe…

常见的嵌入式面试问题解答!

1.关键字static的作用是什么&#xff1f;为什么static变量只初始化一次&#xff1f; ​1&#xff09;修饰局部变量&#xff1a;使得变量变成静态变量&#xff0c;存储在静态区&#xff0c;存储在静态区的数据周期和程序相同&#xff0c; 在main函数开始前初始化&#xff0c;在…

【车载开发系列】AutoSar当中的诊断会话控制

【车载开发系列】AutoSar当中的诊断会话控制 【车载开发系列】AutoSar当中的诊断会话控制 【车载开发系列】AutoSar当中的诊断会话控制一. 什么是诊断会话控制服务二. 会话模式分类三. 会话的接口1&#xff09;获取当前会话状态2&#xff09;设置会话状态3&#xff09;返回默认…

分布式锁4 :数据库DB实现分布式锁的悲观锁和乐观锁,unique实现方式

一 方案1 使用悲观锁解决冲突 1.1 使用悲观锁原理 1.1.1 使用悲观锁的原理 1.悲观锁&#xff1a;在select的时候就会加锁&#xff0c;采用先加锁后处理的模式&#xff0c;虽然保证了数据处理的安全性&#xff0c;但也会阻塞其他线程的写操作。在读取数据时锁住那几行&…

UVT音乐证书考试时间确定,学习氛围渐浓

美国职业资格与人才管理中心&#xff08;UVT&#xff09;音乐证书考试时间正式确定&#xff0c;学习氛围逐渐浓厚。众多热爱音乐的从业者和学生开始积极备考&#xff0c;希望通过这一考试获得音乐领域的宝贵证书。音乐证书被认为是音乐人才展示个人专业水平的重要机会&#xff…

re:从0开始的HTML学习之路 2. HTML的标准结构说明

1. <DOCTYPE html> 文档声明&#xff0c;用于告诉浏览器&#xff0c;当前HTML文档采用的是什么版本。 必须写在当前HTML文档的首行&#xff08;可执行代码的首行&#xff09; HTML4的此标签与HTML5不同。 2. <html lang“en”> 根标签&#xff0c;整个HTML文档中…

虚拟机设置固定IP地址以及访问外网

一、虚拟机固定IP地址设置 1、IP地址查看命令 &#xff08;1&#xff09;ip a [rootlocalhost ~]# ip a • inet 192.168.93.129/24这表示该网络接口&#xff08;ens33&#xff09;被分配了一个IPv4地址是192.168.93.129&#xff0c;并且其子网掩码为 24位&#xff08;即/24…

Java多线程并发篇----第二十六篇

系列文章目录 文章目录 系列文章目录前言一、什么是 Executors 框架?二、什么是阻塞队列?阻塞队列的实现原理是什么?如何使用阻塞队列来实现生产者-消费者模型?三、什么是 Callable 和 Future?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分…

MySQL---多表等级查询综合练习

创建emp表 CREATE TABLE emp( empno INT(4) NOT NULL COMMENT 员工编号, ename VARCHAR(10) COMMENT 员工名字, job VARCHAR(10) COMMENT 职位, mgr INT(4) COMMENT 上司, hiredate DATE COMMENT 入职时间, sal INT(7) COMMENT 基本工资, comm INT(7) COMMENT 补贴, deptno INT…