HBase Shell 命令及示例

⚡ 智能摘要

HBase shell 命令允许您直接从交互式提示符管理表和数据,这些命令分为通用命令、表管理命令(如创建和删除)、数据操作命令(如上传和获取)以及集群复制命令。

  • 🖥️ 通用命令: status、version、table_help 和 whoami 报告集群状态和用户信息。
  • 🗂️ 表格管理: 创建、列出、描述、禁用、启用、删除和更改构建和重塑表。
  • ✏️ 数据操作: put、get、delete、deleteall、truncate、count 和 scan 读取和写入单元格值。
  • 🔁 Cluster 复制: add_peer、remove_peer、start_replication 和 stop_replication 用于管理数据复制。
  • TTL属性: “生存时间”功能会在设定的秒数后自动使列族行过期。
  • 🤖 人工智能助手: AI 助手能够根据纯文本提示生成并解释 HBase shell 命令。

HBase shell 命令示例,涵盖表、数据和集群复制。

如何使用 Shell 命令与 HBase 进行交互 Java API

在 Hadoop 上成功安装 HBase 后,我们会获得一个交互式 shell,用于执行各种命令和操作。利用这些命令,我​​们可以对数据表执行多种操作,从而提高数据存储效率,并为客户端提供灵活的交互方式。

我们可以通过两种方式与 HBase 进行交互:

  • HBase交互式shell模式,以及
  • 通过 Java API。

在 HBase 中,交互式 shell 模式用于与 HBase 进行交互,包括表操作、表管理和数据建模。 Java 通过 API 模型,我们可以对 HBase 执行所有类型的表和数据操作。我们可以使用这两种方法与 HBase 进行交互。两者之间的唯一区别在于…… Java API 使用 Java 使用代码连接 HBase,而 shell 模式使用 shell 命令连接 HBase。

在继续之前,我们先快速回顾一下HBase:

  • HBase 使用 Hadoop的 HBase 是一种用于存储大量数据的存储系统,它由主服务器和区域服务器组成。
  • 存储在 HBase 中的数据将以 Region 的形式存在。此外,这些 Region 将被拆分并存储在多个 Region 服务器上。
  • 这些 shell 命令允许程序员使用完整的 shell 模式交互来定义表结构和数据操作。
  • 无论我们使用哪个命令,它都会反映在 HBase 数据模型中。
  • 我们在操作系统脚本解释器(例如 Bash shell)中使用 HBase shell 命令。
  • Bash shell 是大多数操作系统的默认命令解释器。 Linux 以及 Unix 操作系统发行版。
  • HBase 高级版本提供带有 JRuby 风格面向对象表引用的 shell 命令。
  • 表引用变量可用于在 HBase shell 模式下执行数据操作。

例如:

  • 在本教程中,我们创建了一个表,其中“education”代表表名,对应于列名“guru99”。
  • 在某些命令中,“guru99”本身代表一个表名。

一般命令

在 HBase 中,通用命令分为以下几类:

  • 状态
  • 版本
  • Table_help(扫描、删除、获取、放置、禁用等)
  • 我是谁

要进入 HBase shell 命令,首先,我们必须执行下面提到的代码。

hbase Shell

进入 HBase shell 后,我们可以执行以下所有 shell 命令。借助这些命令,我​​们可以在 HBase shell 模式下执行所有类型的表操作。交互式 shell 提示符如下所示:

使用 hbase shell 命令打开了 HBase 交互式 shell 提示符。

让我们通过一个示例逐一研究所有这些命令及其用法。

状态

Syntax:status

此命令将提供系统状态的详细信息,例如集群中的服务器数量、活动服务器数量和平均负载值。您还可以根据想要了解的系统状态的详细程度传递特定参数。参数可以是“summary”、“simple”或“detailed”;默认参数为“summary”。

下面我们展示了如何向状态命令传递不同的参数。观察下面的屏幕截图,我们会更清楚地了解这一点。

hbase(main):001:0>status
hbase(main):002:0>status 'simple'
hbase(main):003:0>status 'summary'
hbase(main):004:0> status 'detailed'

状态输出如下所示:

HBase shell status 命令输出,显示运行中的服务器、已失效的服务器和平均负载

执行此状态命令后,它会提供有关服务器数量、故障服务器数量以及服务器平均负载的信息。此处屏幕截图显示了诸如 1 台运行中的服务器、1 台故障服务器以及 7.0000 的平均负载等信息。

版本

Syntax: version

版本输出如下所示:

HBase shell 版本命令显示已安装的 HBase 版本

  • 此命令将在命令行模式下显示当前使用的 HBase 版本。
  • 运行版本控制命令后,将输出如上所示结果。

表格帮助

Syntax:table_help

表格帮助的输出如下所示:

HBase shell table_help 输出列表 create 和 get_table 命令用法

此命令指导:

  • 什么是表引用命令以及如何使用它们。
  • 它将提供不同的 HBase shell 命令用法及其语法。
  • 上面的截图展示了“create”和“get_table”命令的语法及其用法。一旦表在HBase中创建完成,我们就可以通过这些命令来操作该表。
  • 它将提供表格操作命令,如 put、get 以及所有其他命令信息。

WHOAMI

Syntax: Whoami

whoami 的输出结果如下所示:

HBase shell 的 whoami 命令返回当前 hduser 和组

命令“whoami”用于从HBase集群返回当前HBase用户信息。它将提供如下信息:

  • HBase 中存在的组。
  • 用户信息;例如,在本例中,“hduser”代表用户名,如屏幕截图所示。

TTL(生存时间)-属性

在 HBase 中,可以使用 TTL(生存时间)将列族设置为以秒为单位的时间值。HBase 会在达到过期时间后自动删除行。此属性适用于行的所有版本,包括当前版本。

HBase 中为行编码的 TTL 时间以 UTC 时间指定。此属性用于表管理命令。TTL 处理与列族 TTL 处理之间的重要区别如下:

  • 单元 TTL 以毫秒而不是秒为单位来表示。
  • 单元格 TTL 无法将单元格的有效寿命延长到超出列系列 TTL 设置的范围。

表管理命令

这些命令允许程序员创建包含行和列族的表和表结构。以下是表管理命令:

  • 创建
  • 列表
  • 描述
  • 关闭
  • 禁用所有
  • 启用
  • 启用全部
  • Drop
  • 全部删除
  • 显示过滤器
  • 年龄
  • 更改状态

让我们通过一个例子来了解 HBase 中各种命令的用法。

创建

Syntax: create <tablename>, <columnfamilyname>

例子:-

hbase(main):001:0> create 'education' ,'guru99'
0 rows(s) in 0.312 seconds
=>Hbase::Table – education

创建命令的执行结果如下所示:

使用 HBase shell create 命令创建包含 guru99 列族的 education 表

以上示例说明了如何在 HBase 中根据每个列族的规范,创建具有指定名称的表。此外,我们还可以向其中传递一些表级属性。为了检查表“education”是否已创建,我们使用以下“list”命令。另请参阅 使用以下方式创建 HBase 表 Java 壳.

列表

Syntax:list

列表输出结果如下所示:

HBase shell list 命令显示 HBase 中存在的八个表

  • “list”命令将显示HBase中存在或创建的所有表。
  • 以上截图中显示的输出结果目前显示的是 HBase 中现有的表。
  • 从这张截图中可以看出,HBase 中总共有 8 个表。
  • 我们可以通过传递可选的正则表达式参数来过滤表格中的输出值。

描述

Syntax:describe <table name>
hbase(main):010:0>describe 'education'

描述输出如下所示:

HBase shell describe 命令的输出结果详细列出了 education 表的列族。

该命令描述命名的表。

  • 它将提供有关上述表格中存在的列族的更多信息。
  • 在我们的例子中,它给出了“教育”表的描述。
  • 它将提供有关表名、列族、相关过滤器、版本以及其他一些详细信息的信息。

关闭

Syntax: disable <tablename>
hbase(main):011:0>disable 'education'

禁用后的输出如下所示:

HBase shell 禁用命令导致教育表离线

  • 此命令将开始禁用指定的表。
  • 如果需要删除或移除某个表,必须先将其禁用。

在上面的截图中,我们禁用了表格教育功能。

禁用所有

 Syntax: disable_all<"matching regex"
  • 此命令将禁用所有与给定正则表达式匹配的表。
  • 实现方式与 drop 命令相同(只是添加了用于匹配的正则表达式)。
  • 表被禁用后,用户可以从 HBase 中删除该表。
  • 删除或丢弃之前ping 如果是表格,应该先禁用它。

启用

Syntax: enable <tablename>
hbase(main):012:0>enable 'education'

启用输出如下所示:

HBase shell enable 命令重新激活已禁用的教育表

  • 此命令将开始启用指定的表。
  • 无论哪个表被禁用,要将其恢复到之前的状态,我们可以使用以下命令。
  • 如果一个表在最初被禁用而不是被删除或丢弃,并且如果我们想要重新使用已禁用的表,那么我们必须使用此命令启用它。
  • 在上面的截图中,我们启用了“教育”表。

显示过滤器

Syntax: show_filters

show_filters 的输出结果如下所示:

HBase shell 的 show_filters 输出列出了可用的 HBase 过滤器

此命令显示 HBase 中存在的所有过滤器,例如 ColumnPrefixFilter、TimestampsFilter、PageFilter、FamilyFilter 等。

下降

Syntax:drop <table name>
hbase(main):017:0>drop 'education'

输出结果如下所示:

使用 HBase shell 的 drop 命令删除已禁用的教育表

使用drop命令时,我们需要注意以下几点:

  • 要删除 HBase 中的表,首先需要禁用它。
  • 要删除 HBase 中存在的表,首先需要禁用它。
  • 因此,要删除表,首先应该使用 disable 命令禁用该表。
  • 在上面的截图中,我们可以看到掉落的画面。ping 表格“教育”。
  • 执行此命令之前,必须禁用“教育”表。

全部删除

Syntax: drop_all<"regex">
  • 此命令将删除所有与给定正则表达式匹配的表。
  • 必须先使用 disable_all 禁用表,然后才能执行此命令。
  • 包含正则表达式匹配的表将从 HBase 中删除。

已启用

Syntax: is_enabled 'education'

此命令将验证指定表是否已启用。通常,“enable”和“is_enabled”命令的操作容易混淆,我们在此进行澄清:

  • 假设一个表被禁用;要使用该表,我们必须使用 enable 命令启用它。
  • is_enabled 命令将检查表是否已启用。

改变

Syntax: alter <tablename>, NAME=><column familyname>, VERSIONS=>5

此命令会更改列族模式。为了让您了解它的具体功能,我们在此通过示例进行了解释。

示例:在这些示例中,我们将对表及其列执行 ALTER 命令操作。我们将执行如下操作:

  • 更改单列和多列族名称。
  • 从表中删除列族名称。
  • 其他一些使用作用域属性对表进行的操作。

要将表“education”中的列族“guru99_1”更改为最多保留 5 个单元格版本,请执行以下操作。此处,“education”是之前创建的表,其列名为“guru99”。我们尝试使用 ALTER 命令将列族架构从 guru99 更改为 guru99_1,如下所示:

使用 HBase shell alter 命令将 education 表的列族更改为 guru99_1

hbase> alter 'education', NAME='guru99_1', VERSIONS=>5

您还可以对多个列族执行 ALTER 命令。例如,我们将为现有表“education”定义两个新列,如下所示:

HBase shell alter 命令添加了两个新的列族 guru99_2 和 guru99_3

 hbase> alter 'edu', 'guru99_1', {NAME => 'guru99_2', IN_MEMORY => true}, {NAME => 'guru99_3', VERSIONS => 5}
  • 我们可以使用此命令一次性更改多个列架构。
  • 如上图所示,guru99_2 和 guru99_3 是我们为 education 表定义的两个新列名。
  • 我们可以从之前的截图中看到这个命令的使用方法。

在此步骤中,我们将学习如何从表中删除列族。要删除表“education”中的“f1”列族,请使用以下命令之一。结果如下图所示:

使用 HBase shell alter 命令从 education 表中删除列族

hbase> alter 'education', NAME => 'f1', METHOD => 'delete'
hbase> alter 'education', 'delete' =>' guru99_1'

在此命令中,我们尝试删除之前在第一步中创建的名为 guru99_1 的列空间。如下面的屏幕截图所示,它展示了两个步骤:如何更改表范围属性以及如何删除表范围属性。

使用 HBase shell alter 命令设置表范围属性 MAX_FILESIZE

Syntax: alter <'tablename'>, MAX_FILESIZE=>'132545224'

步骤 1)您可以更改表范围属性,例如 MAX_FILESIZE、READONLY、MEMSTORE_FLUSHSIZE、DEFERRED_LOG_FLUSH 等。这些属性可以放在末尾;例如,要将区域的最大大小更改为 128MB 或任何其他内存值,我们可以使用以下命令。

用法:

  • 我们可以像上面那样,将 MAX_FILESIZE 用作表的作用域属性。
  • MAX_FILESIZE 表示的数值是以字节为单位的内存大小。

注意:MAX_FILESIZE 表的作用域由 HBase 中的某些属性决定。MAX_FILESIZE 也属于表作用域属性。

步骤 2)您还可以使用 table_att_unset 方法删除表级属性。请参见以下命令:

alter 'education', METHOD => 'table_att_unset', NAME => 'MAX_FILESIZE'
  • 上面的截图显示了修改后的表名及其作用域属性。
  • table_att_unset 方法用于取消设置表中存在的属性。
  • 在第二种情况下,我们将取消设置属性 MAX_FILESIZE。
  • 执行该命令后,它将从“education”表中取消设置 MAX_FILESIZE 属性。

更改状态

 Syntax: alter_status 'education'

alter_status 的输出结果如下所示:

HBase shell alter_status 命令显示其中一个区域已更新

  • 通过此命令,您可以获取 alter 命令的状态。
  • 它表示表中已应用更新架构的区域数量。您需要传入表名。
  • 上面的截图中显示已更新 1/1 个区域。这意味着已更新一个区域。如果更新成功,之后会显示“完成”的提示。

数据操作命令

这些命令用于对表进行数据操作,例如向表中插入数据、从表中检索数据以及删除模式等。这些命令包括:

  • 计数
  • Put
  • 积极
  • 删除
  • 删除所有
  • 截短
  • Scan 扫描

让我们通过一个例子来了解这些命令的用法。另请参阅 读取和写入 HBase 数据.

计数

Syntax: count <'tablename'>, CACHE =>1000
  • 该命令将检索表中的行数。此命令返回的值即为行数。
  • 默认情况下,当前计数每 1000 行显示一次。
  • 计数间隔可选择性指定。
  • 默认缓存大小为 10 行。
  • 如果配置了正确的缓存,计数命令将运行得很快。

计费示例:

hbase> count 'guru99', CACHE=>1000

统计结果如下所示:

HBase shell count 命令返回 guru99 表的行数

此示例每次从“guru99”表中获取 1000 行数据。如果表中的行数更多,我们可以降低缓存值。但默认情况下,它每次获取一行数据。

hbase>count 'guru99', INTERVAL => 100000
hbase> count 'guru99', INTERVAL =>10, CACHE=> 1000

假设表“guru99”包含一个表引用,例如 g。我们也可以对该表引用运行 count 命令,如下所示:

hbase>g.count INTERVAL=>100000
hbase>g.count INTERVAL=>10, CACHE=>1000

Put

Syntax:  put <'tablename'>,<'rowname'>,<'columnvalue'>,<'value'>

此命令用于以下用途:

  • 它会将单元格“值”放置在已定义或指定的表、行或列中。
  • 它还可以选择性地添加时间戳。

例如:在这里,我们将值放入表“guru99”的 r1 行 c1 列中。

hbase> put 'guru99', 'r1', 'c1', 'value', 10

我们在表“guru99”中放入了三个值,分别为 10、15 和 30,如下截图所示。

HBase shell put 命令将值 10、15 和 30 插入到 guru99 表中

假设表“guru99”有一个表引用,例如 g。我们也可以对该表引用运行命令,例如:

hbase> g.put 'guru99', 'r1', 'c1', 'value', 10

将值输入到“guru99”后,输出结果将如上图所示。为了检查输入值是否已正确插入到表中,我们使用“scan”命令。在下面的截图中,我们可以看到值已正确插入。

HBase shell 扫描输出确认已将值插入到 guru99 表中

Code 片段:供练习

create 'guru99', {NAME=>'Edu', VERSIONS=>213423443}
put 'guru99', 'r1', 'Edu:c1', 'value', 10
put 'guru99', 'r1', 'Edu:c1', 'value', 15
put 'guru99', 'r1', 'Edu:c1', 'value', 30

从这段代码片段可以看出,我们正在做以下事情:

  • 在这里,我们创建一个名为“guru99”的表,列名为“Edu”。
  • 通过使用“put”命令,我们将值放入“guru99”表“Edu”列的行名r1中。

积极

Syntax: get <'tablename'>, <'rowname'>, {< Additional parameters>}

这里,包含 TIMESTAMP、TIMERANGE、VERSIONS 和 FILTERS 等参数。使用此命令,您将获取表格中某一行或某一单元格的内容。此外,您还可以添加其他参数,例如 TIMESTAMP、TIMERANGE、VERSIONS 和 FILTERS 等,以获取特定行或单元格的内容。

以下获取输出结果返回第 r1 行和第 c1 列的值:

HBase shell get 命令返回 guru99 表中的行 r1 和列 c1

例子:-

hbase> get 'guru99', 'r1', {COLUMN => 'c1'}

对于表“guru99”,使用此命令将显示第 r1 行和第 c1 列的值,如上面的屏幕截图所示。

hbase> get 'guru99', 'r1'

对于表“guru99”,使用此命令将显示第 r1 行的值。

hbase> get 'guru99', 'r1', {TIMERANGE => [ts1, ts2]}

对于表“guru99”,使用此命令将显示时间范围 ts1 到 ts2 内的行 r1 值。

hbase> get 'guru99', 'r1', {COLUMN => ['c1', 'c2', 'c3']}

对于表“guru99”,使用此命令将显示行 r1 和列族 c1、c2 和 c3 的值。

删除

Syntax:delete <'tablename'>,<'row name'>,<'column name'>
  • 此命令将删除指定表格中指定行或列的单元格值。
  • 删除操作必须且应该与被删除单元格的坐标完全一致。
  • 扫描时,删除单元格会抑制旧版本的值。

例如:以下删除命令从 guru99 表中删除单元格 r1 和 c1:

使用 HBase shell 删除命令从 guru99 表中删除单元格 r1 c1

hbase(main):)020:0> delete 'guru99', 'r1', 'c1''.
  • 上述执行将删除表“guru99”中列族 c1 的行 r1。
  • 假设表“guru99”有一个表引用,例如g。
  • 我们也可以在表引用上运行该命令,例如 hbase> g.delete 'guru99', 'r1', 'c1'”。

删除所有

Syntax: deleteall <'tablename'>, <'rowname'>
  • 此命令将删除指定行中的所有单元格。
  • 我们可以在语法中选择性地定义列名和时间戳。

例如:下面的 deleteall 命令会删除第 r1 行中的所有单元格:

使用 HBase shell 的 deleteall 命令删除 guru99 表中第 r1 行的所有单元格

hbase>deleteall 'guru99', 'r1', 'c1'

这将删除表中的所有行和列。您也可以选择指定列名。

截短

Syntax:  truncate <tablename>

下面的 truncate 命令清空表格,同时保留表格内容。ping 其架构:

HBase shell 截断命令禁用,删除ping并重新创建 guru99 表

截断 HBase 表后,表结构仍然存在,但记录将被删除。此命令执行以下 3 个功能:

  • 如果表格已存在,则禁用该表格。
  • 如果表已存在,则将其删除。
  • 重新创建上述表格。

Scan 扫描

Syntax: scan <'tablename'>, {Optional parameters}

该命令会扫描整个表格并显示表格内容。

  • 我们可以将几个可选规范传递给这个扫描命令,以获取有关系统中存在的表的更多信息。
  • 扫描仪规格可能包括下列一个或多个属性。
  • 这些参数包括 TIMERANGE、FILTER、TIMESTAMP、LIMIT、MAXLENGTH、COLUMNS、CACHE、STARTROW 和 STOPROW。
scan 'guru99'

扫描结果如下所示:

HBase shell scan 命令显示 guru99 表中的 r1、r2 和 r3 行

在上面的截图中:

  • 它显示了“guru99”表,包括列名和值。
  • 它由三个行值 r1、r2 和 r3 组成,对应单个列值 c1。
  • 它显示与行关联的值。

示例:- scan 命令的不同用法:

命令 用法
扫描 '.META.', {COLUMNS => 'info:regioninfo'} 它显示与 HBase 表中存在的列相关的所有元数据信息。
扫描 'guru99',{COLUMNS => ['c1', 'c2'], LIMIT => 10, STARTROW => 'xyz'} 它显示表 guru99 的内容及其列族 c1 和 c2,并将值限制为 10。
扫描 'guru99', {COLUMNS => 'c1', TIMERANGE => [1303668804, 1303668904]} 它显示 guru99 的内容,列名为 c1,其值介于所述时间范围属性值之间。
扫描'guru99',{RAW => true,版本 => 10} 在此命令中,RAW=> true 提供了一个高级功能,例如显示表格 guru99 中存在的所有单元格值。

Code 示例:首先,创建一个表格并将值放入表格中。输入内容如下图所示:

HBase shell 输入,用于创建 guru99 表并在原始扫描之前插入版本化值。

create 'guru99', {NAME=>'e', VERSIONS=>2147483647}
put 'guru99', 'r1', 'e:c1', 'value', 10
put 'guru99', 'r1', 'e:c1', 'value', 12
put 'guru99', 'r1', 'e:c1', 'value', 14
delete 'guru99', 'r1', 'e:c1', 11

如果我们运行扫描命令:

Query: scan 'guru99', {RAW=>true, VERSIONS=>1000}

它将显示如下所示的输出:

HBase shell 原始扫描输出显示多个单元格版本,其中包括一个已删除的值。

以上截图显示的输出结果包含以下信息:

  • 扫描 guru99 表,属性为 RAW=>true,VERSIONS=>1000。
  • 显示包含列族和值的行。
  • 第三行显示的值是该列中已删除的值。
  • 它显示的输出是随机的;它不可能与我们在表中插入的值的顺序相同。

Cluster 复制命令

  • 这些命令适用于 HBase 的集群设置模式。
  • 这些命令通常用于向集群添加和删除对等节点,以及启动和停止复制。
命令 Functionality
添加对等点 向集群添加要复制的节点。
hbase> add_peer '3', zk1,zk2,zk3:2182:/hbase-prod
移除对等体 停止已定义的复制流并删除有关对等节点的所有元数据信息。
删除对等体'1'
启动复制 重启所有复制功能。
hbase> 启动复制
停止复制 停止所有复制功能。
hbase>停止复制

常见问题

输入 exit 或 quit 并按 Enter 键即可退出交互式 HBase shell 并返回操作系统提示符。您可以使用 hbase shell 命令打开 shell。exit 和 quit 命令都不会停止 HBase 服务;它们只会结束 shell 会话。

是的。可以通过管道以非交互方式输入命令,例如 `echo “list” | hbase shell`,或者运行已保存的脚本文件 `hbase shell script.txt`。由于该 shell 基于 JRuby,您还可以将 Ruby 逻辑和循环嵌入到 `.rb` 脚本文件中。

shell 是一个交互式的 JRuby 提示符,用于快速手动执行命令和管理操作。 Java API(使用 Connection、Admin 和 Table)适用于创建表并以编程方式读取或写入数据的应用程序。两者都访问同一个 HBase 集群和数据。

当创建表时使用与现有表相同的名称时,会出现“表已存在”错误;当删除或修改表时未先禁用表,会出现“表未禁用”错误。请在删除或修改表之前运行 `disable 'table'` 命令,并在创建表之前使用 `exists 'table'` 命令检查表名是否存在。

HBase 列族每个单元格保存的带时间戳的版本数量有限,历史上通常为三个,较新的版本则为一个。在创建或修改列时使用 `VERSIONS` 设置版本数量,并在使用 `get` 或 `scan` 命令时使用 `{VERSIONS => n}` 请求旧版本。

表引用变量是 `g = get_table 'guru99'` 返回的 shell 对象。之后,您可以直接调用该对象的方法,例如 `g.put`、`g.get` 或 `g.count`,而无需重复输入表名。这是 HBase shell 中一种便捷的 JRuby 式快捷方式。

AI 编码助手和大型语言模型能够根据纯文本请求生成 HBase shell 命令,解释诸如扫描过滤器或 TTL 等不熟悉的选项,并帮助调试错误。机器学习还会分析集群指标以指导复制和区域调优,但工程师仍应验证每个命令。

是的。 GitHub 副驾驶 根据简短的注释建议 HBase shell 命令和 JRuby 脚本,包括 create、put、scan 和 alter 示例。 Rev在实际集群上运行之前,请查看其输出,确认表名、列族和语法(例如 VERSIONS)是否正确。

总结一下这篇文章: