数据库中索引的底层原理和SQL优化

文章目录

  • 关于索引
    • B+ 树的特点
    • MySQL 为什么使用 B+ 树?
  • 索引分类
    • 聚簇索引 和 非聚簇索引
    • 覆盖索引
    • 索引的最左匹配原则
    • 索引与NULL
    • 索引的代价
    • 大表结构修改
  • SQL优化
    • EXPLAIN命令
    • 选择索引列
    • 其它细节

关于索引

索引是一种用来加快查找效率的数据结构,可以简单粗暴的把索引比喻为字典或书本的目录,通过索引可以快速的查找到想要的数据,避免了逐一遍历效率低下的问题。绝大多数跟存储和查找有关的中间件都有索引功能,但是它们的原理不尽相同。

从数据结构上来说,在 MySQL 里面索引主要是 B+ 树索引。它的查询性能更好,适合范围查询,也适合放在内存里。

MySQL 的索引又可以从不同的角度进一步划分。比如说根据叶子节点是否包含数据分成聚簇索引和非聚簇索引,还有包含某个查询的所有列的覆盖索引等等。数据库使用索引遵循最左匹配原则。但是最终数据库会不会用索引,也是一个比较难说的事情,跟查询有关,也跟数据量有关。在实践中,是否使用索引以及使用什么索引,都要以 EXPLAIN 为准。

B+ 树的特点

B+ 树是一颗多叉树,一棵m阶的B+树定义如下:

  • 每个节点最多有 m 个子节点。
  • 除根节点外,每个节点至少有 [m/2] 个子节点,根节点至少有两个子节点。
  • 有 k 个子节点的节点必有 k 个关键字。这里的关键字可以直观地理解为就是索引全部列的值。

B+ 树的两个主要特征:

  • 叶子节点存放了数据,而非叶子节点只是存放了关键字。
  • 叶子节点被链表串联起来了。

在这里插入图片描述

一棵 B+ 树就像是一个部门,部门分成管理人员(非叶子节点)和基层员工(叶子节点),管理人员只负责传达命令(只放索引关键字),具体的事务(存放数据)都是由基层员工来执行的。基层员工之间为了合作顺畅,互相都有联系,对应到B+树上就是叶子节点被链表连在一起。

B+ 树用在数据库索引上有3大优势。

  • B+ 树的高度和二叉树比起来更低,树的高度代表了查询的耗时,所以 查询性能更好
  • B+ 树的叶子节点都被串联起来了, 所以 适合范围查询
  • B+ 树的非叶子节点没有存放数据,所以 适合放入内存中

使用索引提高查询性能的时候,一个默认的前提就是 索引本身会全部装进内存中,只有真实的数据行会放在磁盘上。 如果索引放在磁盘上的话,使用索引的效果就不明显了。

MySQL 为什么使用 B+ 树?

对比 B+ 树、B 树、二叉树、红黑树、跳表,在用作索引的时候,其他数据结构都有哪些缺陷?

  • 与B+ 树相比,平衡二叉树、红黑树在同等数据量下, 高度更高,性能更差,而且它们会频繁执行再平衡过程,来保证树形结构平衡。
    在这里插入图片描述
  • 与B+ 树相比,跳表在极端情况下会退化为链表, 平衡性差,而数据库查询需要一个可预期的查询时间,并且跳表需要更多的内存。
  • 与B+ 树相比,B 树的数据存储在全部节点中, 对范围查询不友好。 非叶子节点存储了数据, 导致内存中难以放下全部非叶子节点。如果内存放不下非叶子节点,那么就意味着查询非叶子节点的时候都需要磁盘 IO。

结论:一个数据结构是否适合做数据库索引,取决于这种数据结构的增删改查性能。并且在关系型数据库里面,还额外要求对范围查询友好,减少内存消耗。

参考:数据结构与算法中的B+树

索引分类

关于索引的分类,如果站在不同的角度取看待,就会有不同的说法。

  • 根据叶子节点是否存储数据来划分,可以分成 聚簇索引非聚簇索引
  • 如果某个索引包含某个查询的所有列,那么这个索引就是 覆盖索引
  • 如果索引的值必须是唯一的,不能重复,那么这个索引就是 唯一索引。
  • 如果索引的某个列,只包含该列值的前一部分,那么这个索引就是 前缀索引。比如说在一个类型是 varchar(128) 的列上,选择前 64 个字符作为索引。
  • 如果某个索引由多个列组成,那么这个索引就是 组合索引 也叫做联合索引。
  • 全文索引 是指用于支持文本模糊查询的索引。
  • 哈希索引 是指使用哈希算法的索引,但是 MySQL 的InnoDB 引擎并不支持这种索引。

一个索引可以同时是 「覆盖索引、唯一索引、前缀索引、组合索引」,你站在不同的角度去看待索引就会有不同的说法。

聚簇索引 和 非聚簇索引

如果索引叶子节点存储的是数据行,那么它就是聚簇索引,否则就是非聚簇索引。

简单来说,某个数据表本身你就可以看作是一棵使用主键搭建起来 B+ 树,这棵树的叶子节点放着表的所有行。而其他索引也是 B+ 树,不同的是它们的叶子节点存放的是主键。聚簇索引和非聚簇索引的唯一区别就是叶子节点放的是数据行还是索引。

回表 :如果查询一张表用到了索引,那么数据库就会先在索引里面找到主键,然后再根据主键去聚簇索引中查找,最终找出数据。
在这里插入图片描述

如图所示,查询的时候先沿着绿色的线条在非聚簇索引中找到主键。然后拿着主键再去下面沿着黄色的线条找到数据行。这个数据行存放在磁盘里,所以触发磁盘 IO 之后能够读取出来。磁盘 IO 是非常慢的,因此回表性能极差,你在实践中要尽可能避免回表。

覆盖索引

如果查询的列全部都在某个索引里面,那么数据库可以直接把索引存储的这些列的值返回,不需要回表。

比如说有一个用户表 user_tab,在字段 idname 两列上创建了一个组合索引 <id, name>,那么对于这个查询来说 SELECT id, name FROM user_tab WHERE id = 123, SELECT 关键字后面的 idname 两列都在这个索引里,那么就可以直接用索引的数据,不必回表查询了。
在这里插入图片描述
这个索引在这个查询中就是一个覆盖索引。所以覆盖索引并不是一个独立的索引,而是某个索引相对于某个查询而言的。

针对这个特性,优化 SQL 性能里面有两种常见的说法。

  1. 只查询需要的列。
  2. 针对最频繁的查询来设计覆盖索引。

这两种说法本质上都是为了避免回表。

覆盖索引是最为常见的SQL优化方案。假设现在有一个执行非常频繁的 SQL,这个 SQL 查询全部的列,但是业务只会用到其中的三个列 A B C(SELECT 语句是 SELECT A, B, C 三个列),而且 WHERE 里面也只有这三个列的条件,那么就可以考虑直接创建一个 <A, B, C> 组合索引。

对于这个高频 SQL 来说,新的组合索引就是一个覆盖索引。将 SQL 由 SELECT * 改成了 SELECT A, B, C,完全避免了回表。

索引的最左匹配原则

索引在查询中是按照最左匹配原则来使用的。比如一张表的示例数据如下:

ABC
123141
123151
144122
144132
231109

假如你创建了一个在 A、B、C 三个列上的组合索引 <A, B, C>,此时:A 是绝对有序的;在 A 确定的情况下,B 是有序的;在 A 和 B 都确定的情况下,C 是有序的。

反过来说:

  • 如果 A 的值不确定,那么 B 和 C 都是无序的。例如当 A 取值可能为 1 或者 2 的时候,B 的取值可能是 (23, 44, 31)
  • 如果 A 的值确定,但是 B 的值不确定,那么 C 是无序的。例如当 A=1 而 B 可能是 44 或者 31 的时候,C 的值可能是 (122, 132, 109)

执行一个 WHERE A=a1 AND B=b1 AND C=c1 的查询就类似于:

for a in A {if a == a1 {for b in B {if b == b1 {for c in C {if c == c1 {// 这就是你要的数据,拿到主键之后去磁盘里面加载出来}}}}}
}

从这个角度出发,就能理解其他最左匹配原则的情况了。

  • 如果查询条件是 WHERE A = a1 AND B = b1,那么你可以推断出来,数据库只会应用外层的两重循环,不会对 C 进行过滤。
for a in A {if a == a1 {for b in B {if b == b1 {// 这就是你要的结果,去磁盘里面读取}}}
}
  • 如果查询条件是 WHERE A = a1 OR B = b1,那么这个查询并不会使用这个索引。
for a in A {if a == a1 {as = append(as, a)}
}
for b in B {if b == b1 {bs = append(bs, b)}
}
// as 和 bs 的并集就是你要的结果
  • 如果查询条件是 WHERE A=a1 AND B > b1 AND C = c1,那么这个查询只会使用索引的 A 和 B 两列。
for a in A {if a == a1 {for b in B {if b > b1 {// C 是无序的,所以用不了。你可以从前面的表格里面看出来// 比如说 b > 23 之后,对应的 C 是乱序的// 这就是你要的结果,去磁盘里面读取}}}
}
  • 如果查询条件是 WHERE A !=a1,那么这个查询也不会使用索引。

可以用一个简单的口诀判断会不会使用索引。按照组成索引的列的顺序,从左往右: AND 用 OR 不用,正用反不用,范围则中断

这个口诀是一个简化之后的版本,还有一些意外情况是不符合这个口诀描述的规律的。比如说 M = 1 OR N = 2,如果单列 M 上有一个索引,并且单列 N 上也有一个索引,那么还是可能会使用 M 和 N 上的两个索引。

在实践中,用索引还是不用索引,就一个原则: 看 EXPLAIN 命令的输出

【问题】为什么明明设置了索引,但是查询的时候数据库却没用上索引?
【分析】数据库在一些特殊的情况下可能并不会使用任何索引。例如在前面的索引 <A, B, C> 的例子中,假设 A 的所有值都是正数,然后查询条件 WHERE A > -1,那么这个时候数据库会觉得还不如直接全表扫描,那么虽然有一个索引看起来能用,但是最终并不会用。
【回答】数据库可能不使用索引的原因有以下几种:

  • 使用了 !=LIKE 之类的查询。(如果只是 LIKE abc% 这种前缀查询,那么还是可能用索引的)
  • 字段区分度不大。比如说你的 status 列只有 0 和 1 两个值,那么数据库也有可能不用。
  • 使用了特殊表达式,包括数学运算和函数调用。
  • 数据量太小,或者 MySQL 觉得全表扫描反而更快的时候。

索引与NULL

首先,MySQL 的索引对 NULL 类型的字段,会尽可能使用索引,即便索引的某个列里面有零值,并且 IS NULL IS NOT NULL 都可以使用索引。

其次, MySQL 的唯一索引允许有多行的值都是 NULL。也就是说你可以有很多行唯一索引的列的值都是 NULL。因为对于 NULL 的定义 ,是指未知值。 唯一索引中多个 NULL都是未知的,不能说它们是相等的,也不能说是不等,就是未知的。所以多个NULL的存在是不违反唯一约束的。

但是不管怎么说,使用 NULL 都是一个比较差的实践,应该尽可能避免使用NULL

索引的代价

索引并不是完全没有代价,它会消耗很多的系统资源。

  1. 索引本身需要存储起来,消耗磁盘空间。
  2. 在运行的时候,索引会被加载到内存里面,消耗内存空间。
  3. 修改数据也要同步修改索引。在增删改的时候,数据库还需要同步维护索引,引入额外的消耗。

大表结构修改

在修改表结构或者修改索引(ALTER语句)的时候,数据量大的表修改索引和数据量小的表修改索引,实施方案是不一样的。

修改索引的时候, 数据库会加表锁,直到修改完成。所以当你准备新加一个索引的时候,如果这个表的数据很多,那么在你执行加索引的命令的时候,整张表可能都会被锁住几分钟甚至几个小时。

针对这个问题,一般可以考虑以下方案:

  • 停机变更,就是把业务停下来,然后更新表结构。如果做得更加精细一点,那么就可以说只把和这个表有关的功能下线,但不需要将整个服务或者系统下线。
  • 在业务低谷变更,比停机更新好一点,但是业务依旧受到了影响。而且万一并没有在低谷能完成变更,那么就会有其他风险。
  • 创建新表,这是不停机又不想业务受到影响的方案。具体来说就是创建一张新表,这张新表就是你准备用的新的表定义。然后将旧表的数据迁移过去。

SQL优化

了解了索引的基本原理,在实际业务中,有个很常用的场景就是数据库优化。数据库优化主要包含以下内容:

  • 硬件资源优化:换更大更强的机器。
  • 操作系统优化:调整操作系统的某些设置。
  • 服务器/引擎优化:也就是针对数据库软件本体进行优化,比如说调整事务隔离级别。在 MySQL 里面还可以针对不同的引擎做优化,比如说调整 InnoDB 引擎的日志刷盘时机。
  • SQL 优化:针对的就是 SQL 本身了。

其中,SQL 优化就是为了达到两个目标:

  1. 减少磁盘 IO,也可以说是尽量避免全表扫描、尽量使用索引以及尽量使用覆盖索引。
  2. 减少内存 CPU 消耗,尽可能减少排序、分组、去重之类的操作。

EXPLAIN命令

EXPALIN 命令的用法是 EXPLAIN SQL语句,然后数据库就会返回一个执行计划。expain出来的信息有10列,分别是:id、select_type、table、type、possible_keys、key、key_len、ref、rows、Extra。
在这里插入图片描述
其中几个需要关注的字段如下:

  1. type:指的是查询到所需行的方式,从好到坏依次是 system > const > eq_ref > ref > range > index > ALL

    • system 和 const 都可以理解为数据库只会返回一行数据,所以查询时间是固定的。
    • eq_ref 和 ref 字面意思是根据索引的值来查找。
    • range:索引范围扫描。
    • index:索引全表扫描,也就是扫描整棵索引。
    • ALL:全表扫描,发起磁盘 IO 的那种全表扫描。
  2. possible_keys:候选的索引。

  3. key:实际使用的索引。

  4. rows:扫描的行数。数据库可能扫描了很多行之后才找到你需要的数据。

  5. filtered:查找到你所需的数据占 rows 的比例。

实践中,没有必要将这些东西都死记硬背下来。等你要解读 EXPLAIN 的返回结果的时候,再去查看对比就可以了。

选择索引列

设计索引的时候,列的选择非常关键,可以参考以下规则:

  • 外键,一般都会用于关联、过滤数据,所以正常来说都会为表的外键创建索引。
  • 频繁出现在 WHERE 中的列,主要是为了避免全表扫描。
  • 频繁出现在 ORDER BY 的列,这是为了避免数据库在查询出来结果之后再次排序。
  • 频繁出现在关联查询的关联条件中的列。不过一般我们都不建议使用关联查询,所以几乎可以忽略这个。
  • 区分度很高的列。比如每一行的数据都不同的列,并且在创建组合索引的时候,区分度很高的列应该尽可能放到左边。

【问题】你是怎么优化你的 SQL 查询的?
【回答】通过 SQL 的慢查询监控,当发现接口响应时间比较差的时候,就会去排查 SQL 的问题。主要可以使用 EXPLAIN 命令来查看 SQL 的执行计划,看看有没有走索引、走了什么索引、是否有内存排序、去重之类的操作。初步判定了问题所在之后,可以尝试如下优化方案:包括改写 SQL 或者修改、创建索引,然后再次运行 SQL 看看效果。如果效果不好,就继续使用 EXPLAIN 命令,再尝试修改。如此循环往复,直到 SQL 性能达到预期。

其它细节

【用 WHERE 替换 HAVING】
一般来说,数据库都是先根据 WHERE 条件找到候选的列,再根据 HAVING 条件进行二次过滤。如果能够将 HAVING 中部分条件提前到 WHERE里,那么就可以提前将不符合条件的数据过滤掉了。如果不是使用聚合函数来作为过滤条件,最好还是将过滤条件优先写到 WHERE 里面。

【优化分页中的偏移量】
比如列表分页,一页50条数据,那么当你要拿第 101 页的数据,需要写成 LIMIT 50000, 50,其中 50000 就是偏移量。实际执行中数据库就需要读出 50050 条数据,然后将前面的 50000 都丢掉,只保留 50 条。

针对这类情况,优化的思路就是 使用小偏移量。可以在原本的查询语句的 WHERE 里面加上了一个 WHERE id > max_id 的条件,这个 max_id 就是上一页的最大 ID,这样就可以保证 LIMIT 的偏移量永远是 0。

更多参考:《MySQL优化方案和explain详解》

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

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

相关文章

卸载、安装、配置快捷mysql

卸载mysql 1、筛选过滤出mysql相关组件 rpm -qa | grep mysql2、关闭MySQL服务 systemctl stop mysql.service 3、卸载对应组件命令如下&#xff1a; rpm -ev --nodeps [显示的组件名称] 4、查找MySQL对应的所有文件夹 find / -name mysql rm -rf [显示的文件夹路径] 检查…

基于若依框架搭建网站的开发日志(一):若依框架搭建、启动、部署

RuoYi&#xff08;基于SpringBoot开发的轻量级Java快速开发框架&#xff09; 链接&#xff1a;开源地址 若依是一款开源的基于VueSpringCloud的微服务后台管理系统&#xff08;也有SpringBoot版本&#xff09;&#xff0c;集成了用户管理、权限管理、定时任务、前端表单生成等…

linux的基础入门(2)

环境变量 在Shell中&#xff0c;正确的赋值语法是没有空格的&#xff0c;即变量名数值。所以&#xff0c;正确的方式是&#xff1a; tmpshy 这样就将变量tmp赋值为"shy"了。 注意&#xff1a;并不是任何形式的变量名都是可用的&#xff0c;变量名只能是英文字母、…

【neteq】tgcall的调用、neteq的创建及接收侧ReceiveStatisticsImpl统计

G:\CDN\P2P-DEV\Libraries\tg_owt\src\call\call.cc基本是按照原生webrtc的来的:G:\CDN\P2P-DEV\tdesktop-offical\Telegram\ThirdParty\tgcalls\tgcalls\group\GroupInstanceCustomImpl.cpptg对neteq的使用 worker 线程创建call Call的config需要neteqfactory Call::CreateAu…

Java中使用RediSearch进行高效数据检索

RediSearch是一款构建在Redis上的搜索引擎&#xff0c;它为Redis数据库提供了全文搜索、排序、过滤和聚合等高级查询功能。通过RediSearch&#xff0c;开发者能够在Redis中实现复杂的数据搜索需求&#xff0c;而无需依赖外部搜索引擎。本文将介绍如何在Java应用中集成并使用Red…

300V直流充电桩测试有哪些实验项目

300V直流充电桩测试的实验项目主要包括以下几个方面&#xff1a; 1. 电气性能测试&#xff1a; - 输入电压范围测试&#xff1a;检查充电桩在不同输入电压下的正常工作情况。 - 输出电压范围测试&#xff1a;检查充电桩在不同输出电压下的正常工作情况。 - 输出电流范围测试…

MySQL#MySql数据库的操作

目录 一、创建数据库 二、字符集和校验规则 1.查看系统默认字符集以及校验规则 2.查看数据库支持的字符集 3.查看数据库支持的字符集校验规则 4.校验规则对数据库的影响 1.以UTF-8格式创建数据库 2.不区分大小写 3.区分大小写 4 大小写对数据库的影响 三、操纵数据…

流程:采集1688店铺内有成交的商品列表||1688商品订单列表+订单详情API接口

此API目前支持以下基本接口&#xff1a; item_get 获得1688商品详情item_search 按关键字搜索商品item_search_img 按图搜索1688商品&#xff08;拍立淘&#xff09;item_search_suggest 获得搜索词推荐item_fee 获得商品快递费用seller_info 获得店铺详情item_search_shop 获得…

沃伦·巴菲特将AI比做原子弹:“瓶中精灵”使诈骗成为最快增长产业|TodayAI

在伯克希尔哈撒韦公司的年度股东大会上&#xff0c;投资大师沃伦巴菲特对人工智能的未来提出了严重警告。巴菲特对这项可以模拟现实并产生误导性内容的技术表示担忧&#xff0c;他认为这将成为史上增长最快的行业之一。 巴菲特在会上说&#xff1a;“当你思考诈骗人们的潜力时…

NCBI GEO王炸:GEO2R直接分析RNA-seq数据,几家欢喜几家愁?

GEO2R是NCBI GEO团队针对上传到GEO的芯片数据开发的一款在线差异分析、可视化作图工具&#xff0c;是广大数据分析人员的福音。然而&#xff0c;一直以来GEO2R仅针对芯片数据&#xff0c;对于越来越多的测序数据&#xff0c;只能下载所上传的matrix矩阵&#xff0c;进行分析&am…

ENZO:Insulin(胰岛素)ELISA kit

胰岛素是一种由胰岛β细胞合成分泌的肽类激素。它促进葡萄糖从血液中吸收到组织中&#xff0c;并以糖原和脂肪的形式储存起来&#xff1b;此外胰岛素还抑制肝脏产生葡萄糖。通过控制葡萄糖水平&#xff0c;胰岛素成为脂肪和碳水化合物代谢的调节器。当由于缺乏胰岛素分泌而失去…

成都最大视听产业园更新数字影像创作空间,影像技术打造沉浸式体验

国际数字影像产业园&#xff0c;位于成都金牛区的心脏地带&#xff0c;借助数字影视制作技术的力量&#xff0c;努力“破墙而出”&#xff0c;耕耘数字影像技术研发和创新创作的热土。 随着科技的飞速发展&#xff0c;数字影像已经成为成都文创产业的重要组成部分。为了满足国际…

航天系统工程介绍

01 什么是系统工程 系统工程&#xff08;SE&#xff09;是一个视角、一个流程、一门专业&#xff0c;正如以下三种代表性定义所阐明&#xff1a; 系统工程是一种使系统能成功实现的跨学科的方法和手段。系统工程专注于&#xff1a;在开发周期的早期阶段定义客观需要与所要求的…

【考研数学】只做《1800》和真题,能不能考90分?

网上的经验贴推荐的习题册&#xff0c;说自己刷完以后最后考了130 你以为自己也能和大佬一样&#xff0c;刷完数学也能考那么高 但实际上很多人是1800、660、880刷题刷下来最后考了四五十的 他们也不会在网上说 刷题多少&#xff0c;或者是刷哪一本习题册并不能衡量你最后能…

实力登榜!蓝卓入选2024中国未来独角兽TOP100企业

日前&#xff0c;由民建中央、中国科协指导&#xff0c;民建浙江省委会、中国投资发展促进会联合主办的第八届万物生长大会中国未来独角兽大会在杭州举办。 会上&#xff0c;《2024中国未来独角兽TOP100榜单》隆重揭晓。其中&#xff0c;蓝卓在全国参评企业中脱颖而出&#xff…

RK3568 学习笔记 : 精简 u-boot env 默认复杂的多种引导启动设置

前言 环境&#xff1a; 正点原子 Atompi-CA1 RK3568 开发板、正点原子 DLRK3568 开发板&#xff0c;&#xff08;一时脑热买了两块 RK3568 开发板&#xff09;&#xff0c;Atompi-CA1 RK3568 开发板比较小巧&#xff0c;利于一些前期的嵌入式 Linux 开发学习与实践。 RK3568 开…

VGA项目:联合精简帧+双fifo+sobel算法 实现VGA显示(未完)

前言&#xff1a;该项目实际上是在很多基础的小练习上合成起来的&#xff0c;例如涉及到uart&#xff08;rs232&#xff09;的数据传输、双fifo流水线操作、VGA图像显示&#xff0c;本次内容在此基础上又增添了sobel算法&#xff0c;能实现图像的边沿监测并VGA显示。 文章目录…

假期的最后一天...

今天是假期最后一天&#xff0c;这个假期我也跑了不少高速&#xff0c;说实话&#xff0c;看到了梅大高速塌方事故&#xff0c;心情一直不是滋味... 我就在思考&#xff0c;我们究竟在追求什么&#xff1f; 有人说生命是脆弱的&#xff0c;也有人说生命是顽强的&#xff0c;是呀…

Unity MeshRenderer 入门

概述 在项目制作过程中&#xff0c;肯定缺少不了模型的使用&#xff0c;那就一定接触过MeshRenderer&#xff0c;也许还有你不理解的地方&#xff0c;接下来让我们来学习一下这部分的内容吧。 Mesh Filter&#xff08;网格过滤器&#xff09; Mesh:提供一个网格的参考&#xf…

医学图像处理:nii格式转换(3D切片为2D)

目录 NIFTI文件结构 读取NII文件 ITK-SNAP安装 使用方法 NII转PNG NIFTI文件结构 NIFTI 格式&#xff0c;是一种用于存储和交换医学成像数据的文件格式&#xff0c;特别适用于神经影像学领域。NIFTI文件通常有两个扩展名&#xff1a;.nii&#xff08;用于图像数据&#xf…