【JAVA核心知识】深度了解MySql的innodb引擎

  • 关键词
  • InnoDB架构图
  • 表空间
  • 数据页
    • 顺序下数据页的存储
    • 页分裂
    • 页合并
    • 高水位
    • 排序索引构建
    • img_v2_455d98d3-a67a-47ef-b15a-c1798de6f56g.jpg
  • 索引优化
    • 模糊查询打断最左匹配?-索引下推
    • 仅能使用一个索引?-索引合并
    • 自适应Hash索引
  • AUTO_INCREMENT
    • 计数器
    • 新增语句的两种形式
    • InnoDB的自增锁模式
  • 三大日志
    • binlog
    • redolog
    • undolog
  • 双写缓冲区
  • 缓冲池
    • 标准LRU
    • InnoDB的LRU
    • redis的LRU
    • 更改缓冲池

关键词

MySql默认引擎,支持事务,行级锁,支持外键。

InnoDB架构图

image.png

表空间

  • File-Per-Table Tablespaces: 此空间内每个表都有自己单独的文件,默认为开。关闭后表数据放到系统空间。
  • General Tablespaces:需要手动创建,并在建表时指定表空间。多个表共用一个空间, 一个idb文件可能含有多个表。
  • Temporary Tablespaces: 分为会话临时表空间和全局临时表空间。会话临时表空间存储用户创建的临时表和优化器在InnoDB为磁盘内部临时表的存储引擎时创建的内部临时表。磁盘上的内部临时表使用InnoDB存储引擎。全局临时表空间目前用来存储临时表的undolog日志。

删除在单表文件表空间中创建的表,磁盘空间将返回给操作系统。但是未使用空间仅能自己使用。删除存储在通用表空间中的表只是在通用表空间数据文件中创建可用空间,该空间只能用于数据 InnoDB并不会释放给操作系统。

数据页

InnoDB存储数据的物理文件是.ibd文件,每个表都有自己的.ibd文件。idb文件由多个段(segments)组成,段的下一级单位是区,区的下一级单位是页,数据行就在数据页里面。数据页默认16KB。
InnoDB操作的最小粒度是数据页,而不是数据行,即使只需要其中的一个行,也需要将整个页读取出来。然后再获取行记录。因此如果多行数据聚集在一个页内,那么仅需一次磁盘IO就能完成获取,如果分布在多个页内就需要多次磁盘IO。
因此如果相关数据存储在一起,就会操作时就会涉及更少的数据页,从而提升性能。
但是我们在日常场景设计中,除了特殊的场景(如数据相对固化,且查多写少的场景)外,都不会额外的设计聚簇索引,而是采用自增的。

顺序下数据页的存储

假设使用AUTO_INCREMENT作为聚簇索引,顺序插入聚簇索引列值为1,2,3的三行数据,数据页就是这样:
image.png
随着数据的顺序插入,数据页会慢慢变满,当达到页的最大填充因子时(InnoDB默认为最大页大小的15/16,留出空间用于增长),下一条记录就会写入新的页中。每个页中都会存着指向前一页和后一页的指针。

页分裂

假设聚簇索引列不是顺序插入的,而是随机的,这里假设顺序是:6,2,4,7,1,8…5…3:
插到8的过程是下图这样的,假设此时已达到最大填充因子,8到5之间的数据会新开页并插入到后续的页中:
img_v2_ad23c078-5d8f-4b6e-8d73-ce33a041ee9g.png
然后插入5,因为装载因子的预留空间,所以5还能插入,该页变成这样:
image.png
彻底填满了数据页,此时插入3,但是该页已经没有足够的空间了,为了保证聚簇索引的顺序性,就需要进行页分裂,也就是生成一个新页来承载该页的数据,新页也不再是仅仅迁移一个8了,会迁移更多数据保证均衡,整个过程会耗费更多的资源,同时也会造成空洞,这就会导致数据库占有空间比实际空间更大的情况。
这也是为什么大多数情况下我们使用自增主键的原因。
image.png

页合并

了解了页分裂,页合并也就更好理解的,当删除一行数据时,实际上记录并没有被物理删除,而是仅仅打上一个标记声明可以被其他记录使用,当页中剩余的记录不足MERGE_THRESHOLD(默认为页体积的50%)时,InnoDB会探查这个页前面或者后面的页是否有足够的空间,如果有,就会把这个页的数据迁移过去,完成页合并,这个动作同样相当耗费资源。

高水位

页分裂导致空洞以及记录被逻辑删除,而又没有达到页合并的阈值导致空洞的情况会使得数据库占有空间比实际空间更大,使得数据库处于高水位,此时可以通过alter table tableName engine = innodb;或者optimize table tableName(只对MyISAM, BDB和InnoDB表起作用) 来进行表重组,但是重组操作会锁表,这是需要重组时需要考虑到的。

排序索引构建

如果采用常规的B+树构建逻辑,在创建或者重建索引时,批量加载数据,使用插入 API 一次将一条记录插入到B+树,这就涉及打开 B+ 树 游标来查找插入位置,然后使用将条目插入到 B 树页面中。如果页面已满依然要进行页分裂。这种“自上而下”的索引方式弊端在于搜索插入位置的成本和B+树节点的不断分裂和合并。
InnoDB采用一种称为排序索引构建的方式自下向上的构建索引树。
构建共分为三个阶段。在第一阶段, 扫描索引记录,生成索引条目并将其添加到排序缓冲区中。当排序缓冲区已满时,条目将被排序并写入临时中间文件。这个过程也称为 “运行”。在第二阶段,将一次或多次运行写入临时中间文件,对文件中的所有条目执行合并排序。在第三个也是最后一个阶段,将排序后的条目按照先构建叶节点的方式自下而上的插入到 索引树中。

img_v2_455d98d3-a67a-47ef-b15a-c1798de6f56g.jpg

索引优化

模糊查询打断最左匹配?-索引下推

假设一个表包含有关人员及其地址的信息,并且该表的索引定义为 INDEX (zipcode, lastname, firstname)。如果知道一个人的zipcode但不确定姓氏,可以这样搜索:

SELECT * FROM people
  WHERE zipcode='95054'
  AND lastname LIKE '%jack%'
  AND address LIKE '%Main Street%';

按照正常索引的最左匹配原则,执行过程应该是:

  • 获取一行,回表查询读取整行数据。
  • WHERE条件测试此行的条件部分,根据测试结果决定接受还是抛弃该行。

索引条件下推下的扫描过程:

  • 获取一行的索引数据。
  • WHERE条件测试用于索引数据,如果不满足条件,则继续查找下一个索引数据。
  • 如果满足条件,回表查询读取整行。
  • WHERE条件测试此行的条件部分,根据测试结果决定接受还是抛弃该行。

通过这种方式,索引下推可以避免大量的回表。

仅能使用一个索引?-索引合并

多列索引是在多列建立一个索引,而不是为每一列建立单独的索引,在多个列建立独立索引并进行多列组合查询大多数情况下并不能提高性能。MySQL5.0之前最好的情况下也只能使用最左的一个索引。
MySQL5.0引入“索引合并(index merge)”策略,理念是通过取交集,并集的方式将同一个表多个索引的范围查询合并成一个索引。
如以下语句,分别在key1和key2上建立索引。

SELECT * FROM tbl_name WHERE key1 = 10 OR key2 = 20;

索引合并通过将key1和key2的结果取并集来获得结果。 索引合并仅支持单个表的索引扫描,不支持跨多个表的扫描。
索引合并默认开启,但是尽量避免在需要当前读的语句上采用索引合并,他可能会导致死锁的发生。

自适应Hash索引

Innodb存储引擎会监控对索引的查找,如果发现某个索引值被频繁访问,那么InnoDB会根据索引值自动构建一个Hash索引来提升性能。

AUTO_INCREMENT

计数器

如果为InnoDB表指定AUTO_INCREMENT列,则会在内存中为表生成一个称为自动增量计数器的特殊计数器,并使用该计数器为该列分配新值。
该计数器每次改变都会写入同步写入redolog,并在每个checkpoint刷入磁盘;这使得当前最大自动增量计数器值在服务器重新启动后保持不变。
MySql正常重启时,计数器会从磁盘读取值并初始化内存中的增量计数器。崩溃恢复时,计数器会以磁盘中读取到的值进行增量计数器初始化,并扫描redolog的值,取两者的大值进行恢复。
如果生成自增值的事务回滚,则自增值会丢失,并不会回滚。

新增语句的两种形式

  • 可预知插入,比如单行和多行的普通Insert。这种插入可以预知到具体要插入多少行。
  • 不可预知插入,比如INSERT … SELECT,这种语句在插入前具体要插入多少行是不可预知的。

InnoDB的自增锁模式

  • 0: 传统锁模式,此模式下,任何INSERT语句都会获得一个特殊的表级AUTO-INC锁,用于插入包含 AUTO_INCREMENT列的表。该锁通常保持到语句末尾(而不是事务结束),以保证能可预测且可重复为指定的语句序列分配自动递增值,并确保自动递增值对给定语句分配的值都是连续的。
  • 1: 连续锁模式,8.0之前的默认模式。此模式下,对于可预知的插入操作,会一次申请足够的数量,AUTO-INC仅在申请期间持有。对于不可预知的插入,则和传统模式一样。此中模式同一个语句生产的值都是连续的。
  • 2:交错锁模式,8.0之后的默认模式。此模式下,没有了AUTO-INC的概念。速度大大提升,代价却是对于不可预知插入生成的值存在间隙。对于可预知的,依然是一次申请,不存在这这种问题,而不可预知插入因为是一个个申请的,这就意味着,同一个SQL语句申请到的值不是连续的。除此之外还带来另外一个问题,那便是数据不一致,同一批插入语句,即使执行顺序相同,但是因为调度的关系,依然可能生成不同的结果。这种问题在主从同步时就会受到影响。这也是为什么8.0将binlog默认格式设置为ROW的原因。

三大日志

binlog

binlog用于记录数据库执行的修改性操作,以二进制的形式保存在磁盘中。binlog是MySQL的逻辑日志,由服务层进行记录,也就是说无论使用任何存储引擎的MySQL都会记录,binlog主要应用于数据的备份(包括主从复制)与数据恢复。
binlog日志的记录形式有三种:STATEMENT,ROW,MIXED。通过binlog-format指定。

  • STATEMENT:基于语句,直接记录执行SQL。优点是占用空间少,缺点是可能导致数据不一致。
  • ROW:基于行,记录对行的影响。 可以简单的理解为变为对目标行的set值操作。 8.0后的默认格式。 优点是不会导致数据不一致,缺点是可能会占用较多的空间。
  • MIXED:混合模式,MySql识别那些可能导致数据不一致的语句使用ROW。否则使用STATEMENT。

redolog

redolog归属于InnoDB存储引擎,属于物理日志。事务有持久化的特性,而要实现持久化,仅仅录入内存是不够的,势必要与磁盘进行交互,而InnoDB与磁盘的交互是以数据页为单位的,如果一个事务只修改了一个数据行内的某几个字段,此时就进行一次完整的数据页刷入的话,不仅影响性能,还导致资源浪费。而如果一个事务修改了了多个数据页,如果对每个数据页都进行磁盘操作,那么随机IO的情况下,这个事务的性能也会变得很差。redolog是InnoDB在保证性能的情况下实现持久性的关键,它记录事务对数据页进行了哪些修改,然后异步合并刷入磁盘,不仅减小了与磁盘的交互数据量大小,而且是顺序IO。
redolog不像binlog,无需长久保存,毕竟数据页持久化后他就没有存在的意义了,因此redolog采用的是大小固定,循环写入的方式,日志写到结尾是,会回到开头循环写,如下图:

redo文件记录有两个LSN(逻辑序列号)位置,分别是write pos和check point, check point表示当前数据页刷盘进度,write pos表示当前redolog 文件写入的位置。
如果应用故障或宕机,MySQL重启后就会先探查check point的位置,从check point开始进行恢复(当然实际不是这一句话这么简单)。
redolog和binlog各司其职,binlog进行数据备份, redolog的存在使得InnoDB一定程度上具有crash-safe的能力。为什么说一定程度,那是因为只有redolog是不够的。

undolog

undolog记录着数据的逻辑变化历程,属于InnoDB逻辑日志。 可以简单的理解为行数据的每次修改,undolog都会记录下修改前的快照,并根据事务执行顺序组成一个链表。 这样一个事务需要查看原始数据达成一致读取操作时就可以从undolog中检索未修改的数据。
undolog清除时机。 数据执行delete语句时逻辑删除,在undolog日志清除时执行物理清除。

  • 插入undolog: 事务提交后清除
  • 更新undolog: 不存在其作为快照的事务时间

双写缓冲区

MySQL的数据页大小默认是16KB, 但是OS数据页大小却不一定相同,例如Linux文件系统页(OS Page)的大小默认是4KB。 一般情况下应用程序的页都是OS Page的整数倍。
这就意味着要将一个InnoDB数据页写入磁盘,要写4个OS Page,但是这个操作并非原子操作,比如InnoDB在写到第二个页的时候,Linux机器断电了,这时候就会出现问题。造成”页数据损坏“。
这种损坏redolog日志是无法解决的,因为redolog记录的修改数据的物理操作,但是如果发生部分页写入,损坏的可能是未修改的数据。
为了解决这种问题,InnoDB设计了Doublewrite Buffer,
image.png
当有页数据刷盘时,页数据先刷入双写缓冲区,然后在刷入数据文件中。如果OS在将数据页写入磁盘的过程中发生了崩溃,那么恢复时,InnoDB存储引擎可以从Doublewrite Buffer中找到数据页的一个完好的副本,将其复制到表空间数据文件,再执行redlog重放进行恢复。
Doublewrite Buffer虽然要写到磁盘,但其和redolog 一样采用顺序写的方式,因此性能上不会有太大的损耗。

缓冲池

数据是要落盘的,而磁盘的读写操作相较于内存慢的多,因此为了避免每次都操作磁盘,InnoDB设计了基于内存的缓冲池来提高执行效率。
一条数据执行时,如果是读操作,要查找的数据所在的数据页在内存中时,则将结果返回,否则会先将其所在的数据页加载到内存中,然后再返回结果。如果是写操作,要修改的数据所在的数据页在内存中时,则修改后返回对应的结果(后续再刷入磁盘中),否则会先将其所在的数据页加载到内存中,再进行修改。
基于内存的操作会让执行效率大大提升,因此为了尽可能的让数据所在的数据页都在内存中,InnoDB具有预读的特性,预读简单的来说就是读取数据页时,会将相邻的数个数据页也提前读入内存。
因为缓冲池大小的限制,缓冲池会采用LRU算法来淘汰不常用的数据页以避免溢出。

标准LRU

LRU(Least Recently Used),即最近最少使用,选择最近最久未使用的页面予以淘汰。
对于InnoDB来说,标准LRU会带来两个问题:

  • 预读失效:因为InnoDB的预读机制,可能会一次加载多个数据页,而这些数据页并不一定是全都有用的,如果此时采用标准LRU,将这些数据页都放到链表头部,淘汰已有的尾部,那就可能出现无用的数据页被放入缓冲池,而常用的数据被移除。这就属于预读失效。
  • 缓冲池污染:如果执行一条需要全表扫描或者扫描了大量数据的SQL,那么缓冲池会加载大量的数据页,如果采用标准LRU算法,会使得常用数据页都被替换出去,导致MySQL性能下降。这就是缓冲池污染。

InnoDB的LRU

InnoDB采用了类似GC的分代策略,将缓冲池分为Old和New两部分。
image.png

  • 缓冲池的 3/8 用于存储Old页面。
  • 列表的中间点是New页面列表的尾部与Old页面列表的头部相交的边界。
  • 当InnoDB将页面读入缓冲池时,因为页面被读取要么是因为这个页面上用户所需要的,要么是预读的一部分。因此会将其先插入到中点即Old页面子列表的头部。 New列表不受影响,一定程度的解决了预读失效的问题。
  • 如果1S后缓冲页再次被访问,会将其移动到New列表的头部。1S的延迟是为了尽量保证这个数据页不是一个临时读取。另外,对于New区域来说,可能内部的数据页会被频繁读取,因此为了避免每次读取都维护链表,同时又能保证LRU算法的诉求,MySQL对New数据区的维护做了优化,只有后3/4的数据页被访问时才移动到头部,前1/4的数据页被访问了也不会移动。
  • 缓冲池中未访问的页面会通过向列表尾部移动而“老化” 。最终,未使用的页面到达列表的尾部并被驱逐。

redis的LRU

为了避免标准LRU需要付出的大量内存,Redis一样采用一种近似LRU算法。
Redis为每个key增加了一个24bit的额外小字段用来存储最近访问的时间戳。当内存不足需要执行LRU时,redis会根据策略从设置了过期时间的key或者所有key中随机采样5(可配置)个key,然后淘汰掉最旧的key,如果内存依然不足,那么就继续采样淘汰。设置的采样样本量越大,就越接近严格的LRU算法。
在Redis3.0中还新增了淘汰池,进一步提升近似LRU算法,淘汰池是一个数组,大小和采样大小一样,每一次采样淘汰时,先将选出来的key与淘汰池中的key结合,淘汰掉最旧的的一个key,然后将剩下的key整合到淘汰池等待下一个循环。
image.png

更改缓冲池

对于非唯一的二级索引,InnoDB设计了专门的缓冲池-更改缓冲池以避免执行变更操作时可能出现的大量磁盘交互。
上面提到,内存和磁盘交互的最小单位是数据页,当执行变更操作时,如果数据页不存在于Buffer Pool中,需要先将数据页读入Buffer Pool更新数据页之后再刷入磁盘。
而二级索引与一级索引存在一些差别,不像自增场景下聚簇索引的相邻性,连续插入的两条数据在二级索引上可能在两个数据页。同样的,删除和更新操作所影响的二级索引可能也不是相邻的。而且我们可能会建立多个二级索引。悲观场景下,两次相邻的insert操作会产生1(聚簇索引的数据页IO)+2*N(N为二级索引数量)磁盘交互,不仅交互变慢,同时还会占据bufferPool的空间。但是二级索引并没有使用。
对于数据页存在于BufferPool的,ChangeBuffer不会发生作用。但对于非唯一二级索引页不存在BufferPool的,则会将对二级索引的更改先缓存到ChangeBuffer中,并在未来特定条件下其合并到Buffer Pool中。

  • 二级索引被使用时,会先将其读出,并将Change Buffer中的内容进行合并,然后放入Buffer Pool中进行使用。
  • 系统空闲或关闭时
  • Change Buffer空间耗尽时
  • Redo Log日志空间耗尽时

Change Buffer仅针对非唯一二级索引生效,这是因为对于唯一索引,总是要读出来进行唯一性判定的。
Change Buffer默认占据Buffer Pool空间到25%, 当数据库有较多的非唯一二级索引,同时有大量的写入操作而又不会立即使用这些写入数据的场景下,可以适当的调大Change Buffer来提升性能。

参考资料:
MySQL官方文档
MySQL各种“Buffer”之Change Buffer

PS:
【JAVA核心知识】系列导航 [持续更新中…]
关联导航:MySQL架构基础
关联导航:MySQL数据类型选择与设计
关联导航:创建高性能的索引
关联导航:查询性能优化
关联导航:EXPLAIN的使用
欢迎关注…

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

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

相关文章

正点原子嵌入式linux驱动开发——Linux LCD驱动

LCD是很常用的一个外设,通过LCD可以显示绚丽的图片、界面等,提交人机交互的效率。STM32MP1提供了一个LTDC接口用于连接RGB接口的液晶屏。本章就来学校一下如何在Linux下驱动LCD屏。 LCD和LTDC简介 LCD简介 这里在当时学习stm32裸机开发的时候就学过了…

C++文件和流

到目前为止,我们已经使用了 iostream 标准库,它提供了 cin 和 cout 方法分别用于从标准输入读取流和向标准输出写入流。 本教程介绍如何从文件读取流和向文件写入流。这就需要用到 C 中另一个标准库 fstream,它定义了三个新的数据类型&#x…

JavaWeb——IDEA相关配置(Maven配置以及创建自己的第一个Maven项目)

写在前面: 笔者根据狂神说的javaweb视频,一步一步跟着配置IDEA中的Maven,在后面,笔者将讲述自己如何从0配置Maven以及创建自己的第一个Maven项目,笔者将自己的心路历程,包括配置的过程,都以文字…

【TGRS 2023】RingMo: A Remote Sensing Foundation ModelWith Masked Image Modeling

RingMo: A Remote Sensing Foundation Model With Masked Image Modeling, TGRS 2023 论文:https://ieeexplore.ieee.org/stamp/stamp.jsp?tp&arnumber9844015 代码:https://github.com/comeony/RingMo MindSpore/RingMo-Framework (gitee.com) …

汽车4S店如何在数字化管理下,提高市场竞争力

在所有人都认为疫情过后,经济形势会一路向阳,但是,实际情况出乎所有人的意料,各行各业举步维艰。 新闻爆出的各大房地产,恒大的2.4万亿让人瞠目结舌,还有碧桂园和融创,也是债台高筑了&#xff…

嵌入式 Tomcat 调校

SpringBoot 嵌入了 Web 容器如 Tomcat/Jetty/Undertow,——这是怎么做到的?我们以 Tomcat 为例子,尝试调用嵌入式 Tomcat。 调用嵌入式 Tomcat,如果按照默认去启动,一个 main 函数就可以了。 简单的例子 下面是启动…

Power BI 傻瓜入门 9. 设计和部署数据模型

本章内容包含: 详细说明设计数据模型的技术要求Power BI Desktop中基本数据模型的设计将数据模型从Power BI Desktop发布到Power BI Services 在数据进入Power BI后对其进行操作既是一门艺术,也是一门科学。导入到任何应用程序中的数据不仅需要注意数据…

从力扣[203]理解递归思想

本文旨在通过使用递归方法的使用来进一步了解递归思想 class Solution {public ListNode removeElements(ListNode head, int val) {if (head null) {return head;}head.next removeElements(head.next, val);return head.val val ? head.next : head;} }既然要使用递归算法…

【golang】Go中的切片slice和操作笔记,垃圾回收机制,重组 reslice ,复制和追加,内存结构

切片 文章目录 切片将切片传递给函数make() 创建一个切片new() 和 make()的区别多维切片bytes包for-range切片重组 reslice切片的复制和追加 字符串、数组和切片的应用获取字符串的某一部分字符串和切片的内存结构修改字符串中的某个字符字节数组对比函数搜索及排序切片和数组a…

一、PHP环境搭建[phpstorm]

一、安装 1.php编写工具 地址:https://www.jetbrains.com/phpstorm/download/#sectionwindows 图示: 2.php环境 解释:建议使用phpstudy进行安装,安装较为简单 链接:https://www.xp.cn/ 图示: 二、第…

微信小程序投票管理系统:打造智能、便捷的投票体验

前言 随着社交网络的兴起和移动互联网的普及,人们对于参与和表达意见的需求越来越强烈。在这个背景下,微信小程序投票管理系统应运而生。它为用户提供了一个智能、便捷的投票平台,使用户可以轻松创建和参与各种类型的投票活动。本文将详细介…

【德哥说库系列】-PostgreSQL跨版本升级

📢📢📢📣📣📣 哈喽!大家好,我是【IT邦德】,江湖人称jeames007,10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】!😜&am…

STM32-LCD中英文显示及应用

目录 字符编码 ASCII码(8位) 中文编码(16位) GB2312标准 GBK编码 GB18030标准(32位) Big5编码 Unicode字符集和编码 UTF-32(32位) UTF-16(16位/32位&#xff0…

[AutoSar NVM] 存储架构

依AutoSAR及公开知识辛苦整理,禁止转载。 专栏 《深入浅出AutoSAR》, 全文 2900 字. 图片来源: 知乎 汽车的ECU内存中有很多不同类型的变量,这些变量包括了车辆各个系统和功能所需的数据。大部分变量在ECU掉电后就会丢失&#x…

AI的Prompt是什么

一.AI的Prompt的作用 在人工智能(AI)中,"Prompt"通常指的是向AI系统提供的输入或指令,用于引导AI进行特定的操作或生成特定的输出。例如,在一个对话型AI系统中,用户输入的问题就是一个prompt&…

高校教务系统登录页面JS分析——西安科技大学

高校教务系统密码加密逻辑及JS逆向 本文将介绍高校教务系统的密码加密逻辑以及使用JavaScript进行逆向分析的过程。通过本文,你将了解到密码加密的基本概念、常用加密算法以及如何通过逆向分析来破解密码。 本文仅供交流学习,勿用于非法用途。 一、密码加…

《数字图像处理》作业一:题目+学习笔记

Hi,学习进步,共同加油💪 1、选择题 (每小题3分,总计24分) (1)下列哪种图像属于数字图像( ) A、电影胶片;B、普通照片;C、手机拍照;D、眼前看到的景物 数字图像是由离散的像素点组成的图像,可以通过数值表示。根据选项,可以排除A(电影胶片)和B(普通…

Unity中Shader的ShaderLOD

文章目录 前言一、ShaderLOD的使用步骤1、ShaderLOD使用在不同的SubShader中,用于区分SubShader所对应的配置2、在 C# 中使用 Shader.globalMaximumLOD 赋值来选择不同的 SubShader,以达到修改配置对应Shader的效果3、在设置LOD时,是需要和程序讨论统一 …

Git简明教程

1.Git的定位 在我们自己开发项目的过程中,经常会遇到这样的情况,为了防止代码丢失,或者新变更的代码影响到原有的代码功能,为了在失误后能恢复到原来的版本,不得不复制出一个副本,比如:“坦克大战1.0”“坦…
最新文章