HBase 工具|HydraQL: Easy to use HBase

作者:微信小助手

发布时间:2023-10-09T16:36:25

目录导读

HydraQL 简介

HydraQL [ˈhaɪdrəQL],是基于 HBase 原生客户端 API 设计的一款 SQL 查询器,通过使用类 SQL 语法或更精简的 API,用户就可以轻松读写 HBase 表中的数据,而无需深入了解和编写复杂的方法调用。
与 Phoenix 相比, HydraQL中的 SQL 语法更轻量,无需引入额外的组件和配置即可使用,且对 HBase 无倾入性,但目前还不支持聚合查询、表关联、以及二级索引等高阶功能。

HBase 简介和应用场景

在开始隆重介绍 HydraQL 之前,可以先简单认识下什么是 HBase,以及 HBase 的使用场景。
HBase 是 Apache 的顶级项目之一,是一款开源的、分布式的、面向列的 NoSQL 数据库系统。它基于 Hadoop 的分布式文件系统(HDFS)构建,并且在 Hadoop 生态系统中充当高可靠、高性能和可伸缩性的实时数据存储和处理解决方案。
HBase 适用于需要大规模数据存储和实时访问的场景。其主要应用场景包括:
  • 存储海量实时数据,如社交网络的消息、实时日志数据、在线游戏的玩家数据等。
  • 适用于需要高可靠性的应用,HBase 的设计使得数据可以以冗余的方式存储在分布式环境中,从而保证数据的持久性和可靠性。
  • 用于在线分析处理(OLAP)和实时查询分析(OLTP)的场景。唔,OLAP 还是不太适合
  • HBase 提供对大规模数据的低延迟随机读写操作,使得它成为处理实时分析和查询的理想选择。
HBase 具有以下架构特点:
  • 分布式架构:HBase 以集群的方式部署,数据分布在多个节点上,从而实现数据的高可用性和负载均衡。
  • 列式存储:HBase 将数据存储在列族中,每个列族可以包含单独定义的列,并将数据按照列进行存储,从而提高查询效率。
  • 强一致性:HBase 通过强一致性保证数据的正确性,即在写入和读取操作过程中,始终保证数据的一致性。
  • 高可扩展性:HBase 支持水平扩展,即可以通过添加更多的节点来增加存储容量和处理能力。关于数据量规模,HBase 可以存储海量级别的数据。它可以通过添加更多的 RegionServer 节点来水平扩展,以存储和处理更大规模的数据。
HBase 被许多大型公司广泛应用,其中一些典型的大型公司包括:
  • Facebook:Facebook 使用 HBase 来存储和处理用户的消息、通知、实时新闻源等实时数据,提供实时的社交网络服务。

  • Twitter:Twitter 使用 HBase 作为其实时分析和查询引擎的后端存储,用于存储和处理大规模的社交网络数据、实时推文和用户行为数据等。

  • Airbnb:Airbnb 使用 HBase 来存储和处理其大规模的实时用户数据,包括订房信息、用户行为数据和实时推荐。

  • Yahoo:Yahoo 使用 HBase 作为其广告平台的后端存储,用于存储和处理广告投放相关的海量数据。

  • Spotify:Spotify 使用 HBase 来存储和处理音乐流媒体服务中的海量用户数据,包括播放历史、音乐推荐和用户互动数据等。

这些大型公司选择使用 HBase 的原因主要是因为 HBase 提供了可靠性、可伸缩性和高性能的实时数据存储和查询能力,适用于处理大规模数据的场景。同时,HBase 具有强一致性和分布式架构的优势,能够应对企业级应用的高要求。
以上内容摘抄自 ChatGPT。

HydraQL 尝鲜示例

针对如下数据集:
数据集合
现有一查询场景:
筛选 rowKey 范围在('a1000', 'g1005'],年龄>=18 岁,得有工作或者薪酬大于 1 万,有交通工具,而且交通工具不能是公交、自行车和地铁的人员信息。
在没有 HydraQL 之前,我们需要这样做,才能过滤出来我们需要的结果
原生Scan
上述代码的输出结果
原生scan查询结果
在有了 HydraQL 之后,我们只需一条 SQL 即可。
HQL-select
SQL 的执行结果如下
HQL-select结果展示

HydraQL 名称由来

HydraQL由 Hydra + SQL 拼接而来,其名称 Hydra引用了“九头蛇”,象征着在处理 HBase 数据时的灵活性和多功能性。 hql 是其简称,一种类 SQL 的语言,其在执行时,会被“翻译”成 hbase-client的原生 API 来读写表中的数据。

HydraQL 设计理念

HydraQL 是 HBase 的衍生工具集,它融合了 hql、ORM、HBatis、hbase-shell、hbase-thrift 等 API 的功能,你可以按需引用,也可以拷贝和修改源码使用,这并不会有什么限制,只希望能及时反馈 bug,或提供更好的建议。

HydraQL 项目结构与核心模块介绍

项目结构

 
.
├── LICENSE
├── README-EN.md
├── README.md
├── antlr
│   ├── HydraQLLexer.g4
│   ├── HydraQLParser.g4
│   ├── antlr.sh

├── bin
│   ├── build-hql-console.sh
│   ├── build.sh
│   ├── change-model-version.sh
│   ├── deploy.sh
│   └── mvn-doc.sh
├── build
│   ├── checkstyle
│   └── findbugs
├── docs
├── hydraql-adapter
│   ├── hydraql-adapter-common
│   ├── hydraql-adapter_1.2
│   ├── hydraql-adapter_1.4
│   ├── hydraql-adapter_2.2
│   ├── hydraql-adapter_2.5
│   └── pom.xml
├── hydraql-common

├── hydraql-console

├── hydraql-dsl

├── hydraql-examples
│   ├── hydraql-example
│   ├── hydraql-shell-example
│   ├── pom.xml
│   └── spring-boot-starter-hydraql-example
├── hydraql-shell
│   ├── hydraql-shell-core
│   ├── hydraql-shell_1.2
│   ├── hydraql-shell_1.4
│   ├── hydraql-shell_2.2
│   ├── hydraql-shell_2.5
│   └── pom.xml
├── hydraql-template

├── hydraql-tests
│   ├── hydraql-tests-adapter_1.2
│   ├── hydraql-tests-adapter_1.4
│   ├── hydraql-tests-adapter_2.2
│   ├── hydraql-tests-adapter_2.5
│   ├── hydraql-tests-common
│   ├── hydraql-tests-main
│   └── pom.xml
├── hydraql-thrift

├── pom.xml
└── spring-boot-starter-hydraql
核心模块介绍
模块
介绍


hydraql-common
对一些公共方法的封装
hydraql-dsl
hql 的定义,以及使用 antr4 解析 hql,并转换 hbase-client 的调用
hydraql-adapter
统一 HBase 数据读写的接口,并针对不同版本的 hbase-client api 进行适配和增强,屏蔽了多版本下 hbase-client api 不兼容
hydraql-template
依赖 hydraql-adapter,对外统一暴露为模版类和模版方法,是用户需要引入使用
hydraql-tests
利用 HBaseMiniCluster 来做单元测试
spring-boot-starter-hydraql
可以利用 spring-boot-starter-hydraql 与 Spring Boot 轻松集成
hydraql-thrift
对 HBase thrift API 的池化和封装
hydraql-shell
对 HBase Shell 的封装,支持直接在 java 进程中执行 hbase-shell 的 JRuby 环境,可以利用该模块,封装 web-hbase-shell
hydraql-console
hql 的命令行交互程序,支持多集群切换,支持 HQL 和 HBase Shell 双语法,可以直接替换掉原生 hbase-shell

HydraQL 功能特性

hbase-client原生 API 进行了统一的接口定义,屏蔽了底层 API 的复杂调用方式,消除了跨版本升级过程中 API 不兼容的问题。在保证原有功能的同时,额外扩展了其他有用功能,列举如下:
  • [ ] 定义了统一的接口规范,消除了不同版本 hbase-clientAPI 之间的差异
  • [ ] HQL,以类 SQL 的形式读写 HBase 的表中数据
  • [ ] ORM特性,支持以注解的方式快速定义表、列簇、字段的数据模型,在保存和查询数据时,底层自动做数据类型转换
  • [ ] 对 HBase 的原生 thrift API 进行池化封装,提供了 HBaseThriftPool 的功能
  • [ ] 利用 spring-boot-starter-hydraql模块可与 SpringBoot 无缝集成
  • [ ] 支持 kerberos 认证,支持 kerberos 认证的超级用户代理普通用户
  • [ ] 支持类似 hdfs 的 hbase.client.hedged.read 功能,在读主集群达到超时阈值或异常时,自动降级读备集群数据,此功能要求 HBase 主备集群互相 Replication,否则会有数据不一致风险
  • [ ] hydraql-shell,把 hbase-shell 封装到一个 jar 包中,可被其他项目通过 maven 等方式依赖,这在你想开发 hbase-web-shell 的功能时非常有用
  • [ ] hydraql-console,命令行交互工具,可以同时执行 hql 和 hbase-shell 的指令,可完全替代 hbase-shell 来使用
  • [ ] HBatis,类似于 myBatis,提供配置文件管理 HQL 的功能(规划中)
  • [ ] hbase-thrift 连接池中连接数的动态扩缩容能力(规划中)

HydraQL 编译及使用

编译


 
cd ~/HydraQL
sh bin/build.sh 1.2|1.4|2.2|2.5

#
 或者
mvn clean install -Dmaven.test.skip=true
mvn clean install -Dmaven.test.skip=true -Dhbase.profile=1.4
mvn clean install -Dmaven.test.skip=true -Dhbase.profile=2.2
mvn clean install -Dmaven.test.skip=true -Dhbase.profile=2.5
单独编译 hydraql-console

 
cd ~/HydraQL
sh bin/build-hql-console.sh 1.2|1.4|2.2|2.5

#
 或者

mvn  clean package  -Dmaven.test.skip=true  -pl hydraql-console -am
mvn  clean package  -Dmaven.test.skip=true  -Dhbase.profile=1.4  -pl hydraql-console -am
mvn  clean package  -Dmaven.test.skip=true  -Dhbase.profile=2.2  -pl hydraql-console -am
mvn  clean package  -Dmaven.test.skip=true  -Dhbase.profile=2.5  -pl hydraql-console -am
成功编译后,获取压缩包:hydraql-console/target/hydraql-console_1.2-1.0.1-SNAPSHOT-release.tar.gz 后解压缩。

普通项目中使用 hydraql-template

编译成功后,在项目 pom.xml 文件中增加如下 hydraql-template的依赖

 
<properties>
   <hydraql.hbase.adapter.version>1.2 </hydraql.hbase.adapter.version>
   <!--    <hydraql.hbase.adapter.version>1.4</hydraql.hbase.adapter.version>-->
   <!--    <hydraql.hbase.adapter.version>2.2</hydraql.hbase.adapter.version>-->
   <!--    <hydraql.hbase.adapter.version>2.5</hydraql.hbase.adapter.version>-->
</properties>

<dependency>
   <groupId>com.hydraql </groupId>
   <artifactId>hydraql-template_${hydraql.hbase.adapter.version} </artifactId>
   <version>1.0.0 </version>
</dependency>
目前 hydraql 的最新版本是: 1.0.1-SNAPSHOT,可以关注:https://github.com/CCweixiao/HydraQL,在master分支代码中获取最新的版本号。
创建模版操作类

 
// 创建连接HBase的Configuration
Configuration conf = HBaseConfiguration.create();
conf.set( "hbase.zookeeper.quorum""myhbase");
conf.set( "hbase.zookeeper.property.clientPort""2181");

// HBase表数据读写的操作模版类
BaseHBaseTableTemplate tableTemplate = HBaseTableTemplate.of(conf);
// Admin操作模版类
BaseHBaseAdminTemplate adminTemplate = HBaseAdminTemplate.of(conf);
// HQL操作模版类
BaseHBaseSqlTemplate sqlTemplate = HBaseSqlTemplate.of(conf);
集成 Kerberos 认证,Configuration 的构造如下:

 
Configuration conf = HBaseConfiguration.create();
conf.set( "hbase.zookeeper.quorum""myhbase");
conf.set( "hbase.zookeeper.property.clientPort""2181");
conf.set( "hbase.security.authentication""kerberos");
// 下面配置是kerberos认证方式所需
conf.set( "kerberos.principal""hbase@HADOOP.LEO.COM");
conf.set( "keytab.file""/etc/hbase/conf/hbase.keytab");
// 设置kerberos用户代理,需保证hbase有代理普通用户的权限
conf.set( "kerberos.proxy.user""proxy_user");
conf.set( "hbase.regionserver.kerberos.principal""hbase/_HOST@HADOOP.LEO.COM");
conf.set( "hbase.master.kerberos.principal""hbase/_HOST@HADOOP.LEO.COM");
// 指定kdc服务相关的配置方式有如下两种:
// 方式一:指定krb5.conf路径
conf.set( "java.security.krb5.conf""/etc/krb5.conf");
// 方式二:指定java.security.krb5.realm和java.security.krb5.kdc
conf.set( "java.security.krb5.realm""HADOOP.LEO.COM");
conf.set( "java.security.krb5.kdc""你自己的kdc服务地址");
// 一些额外的客户端参数
conf.set( "hbase.client.retries.number""3");
// HBase客户端的hedged read功能
conf.set( "hbase.client.hedged.read.open""true");
// 主集群读超时触发hedged.read的阈值
conf.set( "hbase.client.hedged.read.timeout""100");
// 可以与rpc handler count一致
conf.set( "hbase.client.hedged.thread.pool.size""100");
// 设置hedged.read集群的配置,其他配置,默认复用主集群的配置
conf.set( "hbase.zookeeper.quorum.hedged.read""myhbase");
conf.set( "hbase.zookeeper.property.clientPort.hedged.read""2181");

BaseHBaseTableTemplate tableTemplate = HBaseTableTemplate.of(conf);
模版操作类中支持的方法请参考源码: IHBaseTableOpAdapter.java或测试模块中的测试用例
ORM 特性使用
  1. 定义数据模型类

 
public  class CityTag {
   private String tagName;

   public CityTag(String tagName) {
       this.tagName = tagName;
  }
   // 省略Getter/Setter/toString
}

 
@HBaseTable(namespaceName =  "default", tableName =  "test_table", defaultFamilyName =  "f1")
public  class CityModel {
   @HBaseRowKey
   private String cityId;
   private String cityName;
   private String cityAddress;
   @HBaseColumn(familyName =  "detail")
   private Integer cityArea;
   @HBaseColumn(familyName =  "detail", toUpperCase =  true)
   private Integer totalPopulation;
   @HBaseColumn(familyName =  "detail", columnName =  "cityTagList")
   private List<CityTag> cityTagList;
   // 省略Getter/Setter/toString
}
@HBaseTable注解用于定义 HBase 的表信息

 
@HBaseTable(namespaceName =  "default", tableName =  "t2", defaultFamilyName =  "info")
  • namespaceName:用于设置 HBase 表的命名空间,默认:default
  • tableName:用于设置 HBase 表的名称,如果不指定,表名则为类名的组合单词拆分转小写加'_'拼接,如:CityModel 对应的表名为:city_model。
  • defaultFamilyName:用于定义如果有字段不特别配置(@HBaseRowKey 注解中的 familyName)列簇名,则使用此处配置的列簇名。
@HBaseRowKey注解用于定义某一个属性字段是用作存储 rowKey 数据的,是必须要设置的,如:

 
@HBaseRowKey
private String cityId;
该注解表示 cityId 字段为 rowKey,每一个模型类有且只能有一个字段被标识为 rowKey。
@HBaseColumn注解用于定义 HBase 的列簇和列名信息,如:

 
@HBaseColumn(familyName =  "detail", columnName =  "TOTAL_POPULATION",  toUpperCase =  true)
private Integer totalPopulation;
  • familyName:指定列簇名,不指定则使用 defaultFamilyName 配置的列簇名。
  • columnName:指定列名,不指定则默认使用字段名的组合单词拆分转小写加'_'拼接,如:isVip,对应的字段名是:is_vip
  • toUpperCase:定义字段名是否转大写,如:isVip -> IS_VIP,默认值:false,不做转换。
  1. 存取数据

 
@Test
public void testSave() {
   // 构造数据对象
  CityModel cityModel = CityModelUtil.createDefaultCityModel();
   // 调用save存储数据
  tableTemplate.save(cityModel);
   // 构造 GetRowParam 查询参数
  GetRowParam getRowParam = GetRowParam.of(cityModel.getCityId()).build();
   // 调用getRow查询单条数据
  Optional<CityModel> cityModelRes = tableTemplate.getRow(getRowParam, CityModel .class);
  Assert.assertNotNull(cityModelRes);
}
更多示例请参考测试用例模块

在 Spring Boot 项目中使用 spring-boot-starter-hydraql

编译成功后,在项目 pom.xml 文件中增加如下 spring-boot-starter-hydraql的依赖

 
<properties>
   <hydraql.hbase.adapter.version>1.2 </hydraql.hbase.adapter.version>
   <!--    <hydraql.hbase.adapter.version>1.4</hydraql.hbase.adapter.version>-->
   <!--    <hydraql.hbase.adapter.version>2.2</hydraql.hbase.adapter.version>-->
   <!--    <hydraql.hbase.adapter.version>2.5</hydraql.hbase.adapter.version>-->
</properties>

<dependency>
   <groupId>com.hydraql </groupId>
   <artifactId>spring-boot-starter-hydraql_${hydraql.hbase.adapter.version} </artifactId>
   <version>1.0.0 </version>
</dependency>
在 Spring Boot 项目中,只需用 @Autowired注解注入 HBase 的模版操作类

 

@Service
public  class UserService {
     @Autowired
     private BaseHBaseTableTemplate tableTemplate;
     @Autowired
     private BaseHBaseAdminTemplate adminTemplate;
     @Autowired
     private BaseHBaseSqlTemplate sqlTemplate;
}
application.yaml文件中设置 HBase 集群的连接信息

 
spring:
   datasource:
     hbase:
       zk-quorum:  zk_host1,zk_host2,zk_host3
       zk-client-port:  2181  # (可选,默认2181)
       dfs-root-dir:  /hbase  # (可选,默认/hbase)
       zk-node-parent:  /hbase   # (可选,默认/hbase)
       security-auth-way:  simple  # (可选,默认simple)
       client-properties:  hbase.client.retries.number=3;key1=value2
server:
   port:  8088
然后即可调用各种模版操作类的方法了。
集成 kerberos 认证, application.yaml配置文件内容如下:

 
spring:
   datasource:
     hbase:
       zk-quorum:  myhbase
       zk-client-port:  2181
       dfs-root-dir:  /hbase
       zk-node-parent:  /hbase
       security-auth-way:  simple
       kerberos-principal:  hbase@HADOOP.LEO.COM
       keytab-file-path:  /etc/hbase/conf/hbase.keytab
       kerberos-proxy-user:  test
       rs-kerberos-principal:  hbase/_HOST@HADOOP.LEO.COM
       master-kerberos-principal:  hbase/_HOST@HADOOP.LEO.COM
       krb5-conf-path:  /etc/krb5.conf
       krb5-realm:
       krb5-kdc-server-addr:
       client-properties:  hbase.client.retries.number=3
server:
   port:  8088

HydraQL 的 SQL 语法介绍

创建虚拟表
虚拟表是一条元数据,其中定义了 HBase 表的 Schema 信息,如:列簇名、字段名、字段类型,row 类型等。
虚拟表需要与 HBase 原始表同名,但是创建(删除)虚拟表不会对原始表有任何影响。
创建虚拟表时,会自动在集群中创建一个名为:HQL.META_DATA 的元数据表,其结构如下
hql-meta
其内容如下:
hql-meta-data
创建虚拟表的 HQL 语句如下

 
CREATE  VIRTUAL  TABLE
IF  NOT  EXISTS  test:test_sql (
  row_key  VARCHAR (  100 )  NOT  NULL PRIMARY  KEY,
  f1 :  id  VARCHAR (  200 )  NOT  NULL,
  f1 :  name  VARCHAR (  200 )  NOT  NULL,
  f1 : age  SMALLINT  NULL,
  f1 : job  VARCHAR (  200 )  NOT  NULL,
  f1 : pay  FLOAT,
  f2 : address  VARCHAR (  200 )  NOT  NULL,
 f2 : commuter  VARCHAR (  200 )  NOT  NULL
 )
  WITH properties
 (  "hbase.client.scanner.caching" =  1000,
      "hbase.client.scan.batch" =  100,
      "hbase.client.delete.batch" =  5,
      "hbase.client.scanner.cache" =  FALSE
  );
Java 代码

 
sqlTemplate.createVirtualTable(hql);
查看所有虚拟表

 
show  virtual  tables;
show-tables
Java 代码

 
public void testShowTables() {
  String hql =  "show virtual tables";
  List<String> virtualTables = sqlTemplate.showVirtualTables(hql);
  System.out.println(virtualTables);
}
查看某张虚拟表的详情

 
show  create  virtual  table  test:test_sql;
Java 代码

 
public void testShowCreateTable() {
  String hql =  "show create virtual table test:test_sql;";
  String s = sqlTemplate.showCreateVirtualTable(hql);
  System.out.println(s);
}
show-create-virtual-table
删除一张虚拟表

 
drop  virtual  table  if  exists  test:test_sql
Java 代码

 
public void testDropVirtualTable() {
  sqlTemplate.dropVirtualTable( "drop virtual table if exists test:test_sql;");
}
Select

 
select *  from  test:test_sql  where startkey >  'a1000'  and endkey <=  'g1005'  limit  50
Java 代码

 
String hql =  "select * from test:test_sql where rowKey = 'a1005'";
HBaseDataSet dataSet = sqlTemplate.select(hql);
dataSet.show();
Select
多版本查询、时间戳筛选,请参考测试用例
Delete

 
delete  from  test:test_sql  where rowkey =  'a1002'
Java 代码

 
@Test
public void testDelete() {
  String hql =  "delete from test:test_sql where rowkey = 'a1002';";
  sqlTemplate.delete(hql);
}
Delete
更复杂的删除条件触发,请参考测试用例
Upset
保存单条或多条数据

 
 upsert into test:test_sql ( row_key , f1:id , f1:name , f1:age , f1:job , f1:pay , f2:address , f2:commuter )
  values ( '22222''10001' ,  'leojie' ,  null ,  '程序员' ,  15333.33 ,  '北京' ,  '' );


 upsert into test:test_sql ( row_key , f1:id , f1:name , f1:age , f1:job , f1:pay , f2:address , f2:commuter )
  values ( '22222''10001' ,  'leojie' ,  null ,  '程序员' ,  15333.33 ,  '北京' ,  '' ),
     ( '33333''10001' ,  'leojie' ,  null ,  '程序员' ,  15333.33 ,  '北京' ,  '' );
Java 代码

 
@Test
public void testInsertOne() {
  testCreateVirtualTable();
  String hql =  " upsert into test:test_sql ( row_key , f1:id , f1:name , f1:age , f1:job , f1:pay , f2:address , f2:commuter ) values ('22222', '10001' , 'leojie' , null , '程序员' , 15333.33 , '北京' , '' );";
  sqlTemplate.insert(hql);
}
upset
更丰富的 SQL 语法,请参考 GitHub 上的项目文档

hydraql-console 使用

Hydraql-console 的编译,参考上述小节,编译完成后得到 release.tar.gz 安装包
hydraql-console/target/hydraql-console_1.4-1.0.1-SNAPSHOT-release.tar.gz
我演示使用的版本是 1.4

 
tar -zxvf hydraql-console_1.4-1.0.1-SNAPSHOT-release.tar.gz
cd hydraql-console_1.4-1.0.1-SNAPSHOT

ll
(base)  leojie@宇宙无敌第一帅  ~/software/hydraql-console_1.4-1.0.1-SNAPSHOT  ll
total 0
drwxr-xr-x    3 leojie  staff    96B  9 28 16:08 bin  # 启动脚本
drwxr-xr-x    4 leojie  staff   128B  9 28 16:08 conf # 配置文件,不需要什么配置
drwxr-xr-x  127 leojie  staff   4.0K  9 28 16:08 lib  # 第三方jar
drwxr-xr-x    3 leojie  staff    96B  9 28 16:08 logs # 日志目录,方便排障

sh bin/hydraql-console.sh
成功启动的界面截图如下:
hydraql-console
此时未添加集群,运行命令会异常,我们需要添加集群信息,并切换我们需要的集群

 
#  添加集群信息
add_cluster test hbase.zookeeper.quorum=myhbase;hbase.zookeeper.property.clientPort=2181

#
 查看集群列表
list_clusters

#
 切换集群
switch_cluster test

#
 命令帮助
help
命令指南
命令大全
添加集群信息、查看集群列表、切换目标集群
console
hydraql-console 支持历史命令提示,以及 TAB 提示命令,上下键翻转选择历史命令,关键词色彩提示、HQL 语法解析异常提示等等
更多示例命令请参考 GitHub 项目主页,帮忙点亮 Star
more_command
and so on ......

写在后面

各位路过的大佬们,都看到这了,帮忙点亮 Star 呀!
https://gitee.com/weixiaotome/hydra-ql
https://github.com/CCweixiao/HydraQL