SAP HANA中PAL算法使用入门

1 应用场合

         SAP HANA作为一款内存数据库产品, 使得数据常驻内存, 物理磁盘的存储作为数据备份与日志记录, 以防断电内存中数据丢失. 这种构架大大的缩短了数据存取的时间, 使得SAP HANA很”高速”.

         在传统数据模型中,数据库只是作为存取数据一个工具,对于类似下图所示的应用, 客户端从Database获取数据,然后计算,最后再把结果写回Database, 如果数据量过大, 数据传输的开销过大,并且如果客户端的内存不够, 计算分析的过程也将非常缓慢.

         借助于大内存的优势, SAP HANA的解决方案是把数据敏感的相关计算逻辑都移动到SAP HANA内, 从而省去了数据传输的开销. 典型的框架如下:

对于一些简单的计算分析, 可以利用SQLScript脚本完成, SQLScript提供了基本的变量定义语句,流程控制语句. 但是对于复杂的分析与计算, 单纯使用SQLScript可能不是特别方便, 比如对1T的数据表作聚类分析. 为此 ,SAP HANA提供  AFL (Application Function Library) ,  把一些常见的分析任务用C++实现,作为库函数的形式, 提供给SQLScript调用,极大地丰富了SQLScript的功能.

2 PAL简介

         PAL (Predictive Analysis Library)是SAP HANA中AFL (Application Function Library)框架下的一个函数库, 主要用于数据预测与分析, 提供了很多数据挖掘算法的实现. 按应用的场景进行分类,PAL函数主要包括以下类别:

Ø  聚类

Ø  分类

Ø  关联分析

Ø  时间序列分析

Ø  数据预处理

Ø  统计分析

Ø  社会网络分析

具体到每个类别下面, 有常见算法的实现, 比如聚类下面的K-means算法.

值得一提的是, AFL是一个单独的包, 需要另外进行安装. 另外AFL 的版本号需要与SAP HANA的版本号匹配.

3 基本使用步骤

     PAL函数的使用包括三个步骤:

(1)    生成AFL_WRAPPER_GENERATOR 与 AFL_WRAPPER_ERASER存储过程.

       对于具体的某个算法, 在使用之前,利用AFL_WRAPPER_GENERATOR生成该算法的一个包装器,然后才能进行调用, 可以理解为生成该算法的一个实例, AFL_WRAPPER_ERASER作用是删除这个算法的实例.

      这两个存储过程的生成很简单. 在AFL插件的目录下有afl_wrapper_generator.sql, afl_wrapper_eraser.sql两个脚文文件, 把它们的内容拷贝到SAP HANA Studio的SQL Console,然后执行以及即可.然后为用户分配执行权限:

   GRANT EXECUTE ON system.afl_wrapper_generator to USER1;

   GRANT EXECUTE ON system.afl_wrapper_eraser to USER1;

这个步骤只需要首次利用使用AFL时执行一次,对于后续其他的算法使用,就不需要执行了.

(2)    生成算法的实例

          CALL SYSTEM.AFL_WRAPPER_GENERATOR(

              '<procedure_name>',        

              '<area_name>',

              '<function_name>', <signature_table>);

Procedure_name:自定义的名称;

Area_name:通常为AFLPAL;

Function_name:算法名称;

Signature_table:指定一个用户表,作为方法签名的信息;

(3)  调用算法实例

CALL <procedure_name>(

<data_input_table> {,…},   

<parameter_table>,

<output_table> {,…}) with overview;

Procedure_name:算法实例名;

Data_input_table:输入数据表;

Parameter_table:参数表;

Output_table:输出表;

4 示例Demo

  下面以DBSCAN聚类算法来说明说PAL算法的调用过程.(因测试机AFL_WRAPPER_GENERATOR存储过程已经存在,故第一步不再执行,另假定Schema为TEST)

  DBSCAN聚类算法是一个基于密度的聚类算法,该算法有很好的降噪能力,有关该算法的更多介绍,请参考http://en.wikipedia.org/wiki/DBSCAN

/*创建数据表类型 ,id,属性1,属性2 */
CREATE TYPE PAL_DBSCAN_DATA_T AS TABLE ( ID integer, ATTRIB1 double, ATTRIB2
double);
/*创建算法控制参数类型*/
CREATE TYPE PAL_CONTROL_T AS TABLE( NAME varchar(50), INTARGS integer, DOUBLEARGS
double, STRINGARGS varchar(100));
/*结构表类型,ID,类簇编号*/
CREATE TYPE PAL_DBSCAN_RESULTS_T AS TABLE( ID integer, RESULT integer);
/*创建方法参数表*/
CREATE COLUMN TABLE PAL_DBSCAN_PDATA_TBL( "ID" INT, "TYPENAME" VARCHAR(100), "DIRECTION" VARCHAR(100) );
/*向参数表中插入相关参数数据*/
INSERT INTO PAL_DBSCAN_PDATA_TBL VALUES (1, 'TEST.PAL_DBSCAN_DATA_T', 'in');
INSERT INTO PAL_DBSCAN_PDATA_TBL VALUES (2, 'TEST.PAL_CONTROL_T', 'in');
INSERT INTO PAL_DBSCAN_PDATA_TBL VALUES (3, 'TEST.PAL_DBSCAN_RESULTS_T', 'out');
/*分配权限*/
GRANT SELECT ON DM_PAL.PAL_DBSCAN_PDATA_TBL to SYSTEM;
/*生成PAL_DBSCAN9算法实例*/
call SYSTEM.afl_wrapper_eraser('PAL_DBSCAN9');
call SYSTEM.afl_wrapper_generator('PAL_DBSCAN9', 'AFLPAL', 'DBSCAN', PAL_DBSCAN_PDATA_TBL);
/* 创建数据表*/
CREATE COLUMN TABLE PAL_DBSCAN_DATA_TBL ( ID integer, ATTRIB1 double, ATTRIB2 double);
/*插入测试数据*/
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(1,0.10,0.10);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(2,0.11,0.10);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(3,0.10,0.11);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(4,0.11,0.11);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(5,0.12,0.11);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(6,0.11,0.12);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(7,0.12,0.12);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(8,0.12,0.13);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(9,0.13,0.12);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(10,0.13,0.13);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(11,0.13,0.14);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(12,0.14,0.13);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(13,10.10,10.10);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(14,10.11,10.10);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(15,10.10,10.11);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(16,10.11,10.11);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(17,10.11,10.12);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(18,10.12,10.11);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(19,10.12,10.12);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(20,10.12,10.13);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(21,10.13,10.12);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(22,10.13,10.13);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(23,10.13,10.14);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(24,10.14,10.13);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(25,4.10,4.10);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(26,7.11,7.10);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(27,-3.10,-3.11);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(28,16.11,16.11);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(29,20.11,20.12);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(30,15.12,15.11);
/*用临时表来存储算法的输入参数*/
CREATE LOCAL TEMPORARY COLUMN TABLE #PAL_CONTROL_TBL( NAME varchar(50), INTARGS
integer, DOUBLEARGS double, STRINGARGS varchar(100));
/*指定DBSCAN算法的输入参数*/
/*线程数18*/
INSERT INTO #PAL_CONTROL_TBL VALUES('THREAD_NUMBER',18,null,null);
/*自动确定MINPTS与RADIUS参数*/
INSERT INTO #PAL_CONTROL_TBL VALUES('AUTO_PARAM',null,null,'true');
/*点与点之间的距离采用Manhattan距离*/
INSERT INTO #PAL_CONTROL_TBL VALUES('DISTANCE_METHOD',1,null,null);
/*结果表*/
CREATE COLUMN TABLE PAL_DBSCAN_RESULTS_TBL( ID integer, RESULT integer);
/*调用DBSCAN算法*/
CALL _SYS_AFL.PAL_DBSCAN9(PAL_DBSCAN_DATA_TBL, "#PAL_CONTROL_TBL",
PAL_DBSCAN_RESULTS_TBL) with overview;
/*查看结果*/
SELECT * FROM PAL_DBSCAN_RESULTS_TBL; 
/*创建数据表类型 ,id,属性1,属性2 */
CREATE TYPE PAL_DBSCAN_DATA_T AS TABLE ( ID integer, ATTRIB1 double, ATTRIB2
double);
/*创建算法控制参数类型*/
CREATE TYPE PAL_CONTROL_T AS TABLE( NAME varchar(50), INTARGS integer, DOUBLEARGS
double, STRINGARGS varchar(100));
/*结构表类型,ID,类簇编号*/
CREATE TYPE PAL_DBSCAN_RESULTS_T AS TABLE( ID integer, RESULT integer);
/*创建方法参数表*/
CREATE COLUMN TABLE PAL_DBSCAN_PDATA_TBL( "ID" INT, "TYPENAME" VARCHAR(100), "DIRECTION" VARCHAR(100) );
/*向参数表中插入相关参数数据*/
INSERT INTO PAL_DBSCAN_PDATA_TBL VALUES (1, 'TEST.PAL_DBSCAN_DATA_T', 'in');
INSERT INTO PAL_DBSCAN_PDATA_TBL VALUES (2, 'TEST.PAL_CONTROL_T', 'in');
INSERT INTO PAL_DBSCAN_PDATA_TBL VALUES (3, 'TEST.PAL_DBSCAN_RESULTS_T', 'out');
/*分配权限*/
GRANT SELECT ON DM_PAL.PAL_DBSCAN_PDATA_TBL to SYSTEM;
/*生成PAL_DBSCAN9算法实例*/
call SYSTEM.afl_wrapper_eraser('PAL_DBSCAN9');
call SYSTEM.afl_wrapper_generator('PAL_DBSCAN9', 'AFLPAL', 'DBSCAN', PAL_DBSCAN_PDATA_TBL);
/* 创建数据表*/
CREATE COLUMN TABLE PAL_DBSCAN_DATA_TBL ( ID integer, ATTRIB1 double, ATTRIB2 double);
/*插入测试数据*/
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(1,0.10,0.10);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(2,0.11,0.10);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(3,0.10,0.11);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(4,0.11,0.11);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(5,0.12,0.11);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(6,0.11,0.12);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(7,0.12,0.12);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(8,0.12,0.13);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(9,0.13,0.12);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(10,0.13,0.13);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(11,0.13,0.14);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(12,0.14,0.13);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(13,10.10,10.10);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(14,10.11,10.10);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(15,10.10,10.11);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(16,10.11,10.11);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(17,10.11,10.12);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(18,10.12,10.11);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(19,10.12,10.12);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(20,10.12,10.13);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(21,10.13,10.12);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(22,10.13,10.13);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(23,10.13,10.14);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(24,10.14,10.13);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(25,4.10,4.10);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(26,7.11,7.10);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(27,-3.10,-3.11);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(28,16.11,16.11);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(29,20.11,20.12);
INSERT INTO PAL_DBSCAN_DATA_TBL VALUES(30,15.12,15.11);
/*用临时表来存储算法的输入参数*/
CREATE LOCAL TEMPORARY COLUMN TABLE #PAL_CONTROL_TBL( NAME varchar(50), INTARGS
integer, DOUBLEARGS double, STRINGARGS varchar(100));
/*指定DBSCAN算法的输入参数*/
/*线程数18*/
INSERT INTO #PAL_CONTROL_TBL VALUES('THREAD_NUMBER',18,null,null);
/*自动确定MINPTS与RADIUS参数*/
INSERT INTO #PAL_CONTROL_TBL VALUES('AUTO_PARAM',null,null,'true');
/*点与点之间的距离采用Manhattan距离*/
INSERT INTO #PAL_CONTROL_TBL VALUES('DISTANCE_METHOD',1,null,null);
/*结果表*/
CREATE COLUMN TABLE PAL_DBSCAN_RESULTS_TBL( ID integer, RESULT integer);
/*调用DBSCAN算法*/
CALL _SYS_AFL.PAL_DBSCAN9(PAL_DBSCAN_DATA_TBL, "#PAL_CONTROL_TBL",
PAL_DBSCAN_RESULTS_TBL) with overview;
/*查看结果*/
SELECT * FROM PAL_DBSCAN_RESULTS_TBL; 

如果执行无误,将看到如上图所示的结果,记录被聚成三类,0,1,-1各代表一个类簇.

5结束语

本文介绍了SAP HANA中PAL算法的使用,以DBSCAN聚类算法作为具体的实现例子.其他的相关算法使用流程上与上述流程都相似,主要的工作在于准备数据表,根据算法的接口文档定义相关的参数,并将参数存入参数表.最后调用算法实例即可.

从效率上讲,在SAP HANA中使用PAL,一方面利用了大内存的优势,另一方面利用了C++作为编译型语言本身的高效性,如果使用得当,对于大数据的相关分析任务,在速度上将会有一个很大的飞跃!

[注: 本文的测试案例所使用的SAP HANA版本为SAP HANA SPS06]

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

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

相关文章

5分钟速成渐变色css

色彩的分支——渐变色定义&#xff1a;按照一定规律做阶段性变化的色彩&#xff08;抽象&#xff01;&#xff01;&#xff01;&#xff09; 我们可以将图片分为两块 以中心线为参考&#xff0c;再来看渐变色的定义&#xff1a;按照一定规律做阶段性变化的色彩 既然是按一定的…

京津冀光伏展

京津冀光伏展是中国在京津冀地区举办的一项光伏产业展览活动。该展览旨在展示京津冀地区光伏产业的最新发展成果&#xff0c;促进光伏行业的交流与合作&#xff0c;推动光伏产业的可持续发展。 光伏产业是指利用太阳能将光能转化为电能的产业。作为一种清洁能源&#xff0c;光伏…

Databend 开源周报第 134 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 支持多语句事务…

1907_Arm Cortex-M3的基本了解

1907_Arm Cortex-M3的基本了解 全部学习汇总&#xff1a; g_arm_cores: ARM内核的学习笔记 (gitee.com) 我发现Arm Coretex-M3有一个专门的DataSheet&#xff0c;看起来这个的确是被当做了一个设计的产品来对待的。正好&#xff0c;基于这个文件来看看M3具备哪些基本的特性&…

灯塔:HTML笔记

网页由哪些部分组成&#xff1f; *文字 图片 音频 视频 超链接 程序员写的代码是通过浏览器转换成网页的 五大浏览器有哪些&#xff1f; *IE浏览器 *火狐浏览器&#xff08;Firefox&#xff09; *谷歌浏览器&#xff08;Chrome&#xff09; *Safari浏览器 *欧朋浏览器&…

【机器学习】生成对抗网络GAN

概述 生成对抗网络&#xff08;Generative Adversarial Network&#xff0c;GAN&#xff09;是一种深度学习模型架构&#xff0c;由生成器&#xff08;Generator&#xff09;和判别器&#xff08;Discriminator&#xff09;两部分组成&#xff0c;旨在通过对抗训练的方式生成逼…

【Linux】Shell命令运行原理和权限详解

【Linux】Shell命令运行原理和权限详解 一、剩余指令的补充1.tar指令2.bc指令3.uname4.热键 二、Shell命令运行原理1.Shell2.为什么Linux不让用户直接使用kernel 三、Linux权限概念四、Linux权限管理1.文件访问的用户分类2.文件类型和访问权限&#xff08;1&#xff09;文件类型…

GitHub登不上:修改hosts文件来解决(GitHub520,window)

参考链接&#xff1a;GitHub520: 本项目无需安装任何程序&#xff0c;通过修改本地 hosts 文件&#xff0c;试图解决&#xff1a; GitHub 访问速度慢的问题 GitHub 项目中的图片显示不出的问题 花 5 分钟时间&#xff0c;让你"爱"上 GitHub。 (gitee.com) GitHub网站…

Prometheus结合Grafana监控MySQL,这篇不可不读!

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

IOS 发布遇到“Unable to authenticate with App Store Connect”错误咋解决?

问题&#xff1a; 在开发ios app后&#xff0c;先发布adhoc版本&#xff0c;测试通过后&#xff0c;再发布testflight版本测试&#xff0c;但是可能会遇到一下问题。 解决办法&#xff1a; 在Signing &Capabilities中&#xff0c;在ios下边要指定有发布权限的Team账号&a…

数据库之间数据迁移工具datax

简介 DataX 是阿里云 DataWorks数据集成 的开源版本&#xff0c;在阿里巴巴集团内被广泛使用的离线数据同步工具/平台。DataX 实现了包括 MySQL、Oracle、OceanBase、SqlServer、Postgre、HDFS、Hive、ADS、HBase、TableStore(OTS)、MaxCompute(ODPS)、Hologres、DRDS, databe…

链路负载均衡之DNS透明代理

一、DNS透明代理 一般来说&#xff0c;企业的客户端上都只能配置一个运营商的DNS服务器地址&#xff0c;DNS服务器通常会将域名解析成自己所在ISP内的Web服务器地址&#xff0c;这将导致内网用户的上网流量都集中在一个ISP的链路上转发&#xff0c;最终可能会造成链路拥塞&…

常用的Linux命令;Linux常用命令用法及实现方式

1.系统工作命令 (1)echo命令&#xff1a;echo命令用于在终端设备上输出字符串或变量提取后的值&#xff0c;语法格式为“echo [字符串] [$变量]”。 (2)date命令&#xff1a;date命令用于显示或设置系统的时间与日期&#xff0c;语法格式为“date [指定的格式]”。 (3)timedate…

VS2019 - error C2653: 不是类或命名空间名称

文章目录 VS2019 - error C2653: 不是类或命名空间名称概述笔记类的头文件类的实现文件备注END VS2019 - error C2653: 不是类或命名空间名称 概述 工程开了预编译头包含. 编码中, 随手写一个类, 将功能函数加入, 还没开始用这个类, 先习惯性的编译一下. 编译报错如下: St…

常用的17个运维监控系统(必备知识)

1. Zabbix Zabbix 作为企业级的网络监控工具&#xff0c;通过从服务器&#xff0c;虚拟机和网络设备收集的数据提供实时监控&#xff0c;自动发现&#xff0c;映射和可扩展等功能。 Zabbix的企业级监控软件为用户提供内置的Java应用服务器监控&#xff0c;硬件监控&#xff0c…

QT----写完的程序打包为APK在自己的手机上运行

目录 1、qt安装android组件2、打开qt配置Android 环境3、手机打开开发者模式&#xff0c;打开usb调试&#xff0c;连接电脑4、运行代码 1、qt安装android组件 qtcreater–工具-QTMaintenaceTool-startMaintenaceTool—登陆—添加或修改组件—找到android&#xff0c;安装 若是…

【脑切片图像分割】MATLAB 图像处理 源码

1. 简单图像处理 加载图像 Brain.jpg&#xff0c;使用直方图和颜色分割成区域这些区域有不同的颜色。 这是一个更高级的问题&#xff0c;有多个解决它的方法。 例如&#xff0c;您可以计算具有特定数字的图像的直方图&#xff08;例如 16 - 32&#xff09;&#xff0c;找到直方…

回归预测 | Matlab实现BiTCN基于双向时间卷积网络的数据回归预测

回归预测 | Matlab实现BiTCN基于双向时间卷积网络的数据回归预测 目录 回归预测 | Matlab实现BiTCN基于双向时间卷积网络的数据回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现BiTCN基于双向时间卷积网络的数据回归预测&#xff08;完整源码和数据&a…

Vue 3的Composition API和vue2的不同之处

Vue 3的Composition API是Vue.js框架的一个重要更新&#xff0c;它提供了一种新的组件逻辑组织和复用方式。在Vue 2中&#xff0c;我们通常使用Options API&#xff08;data、methods、computed等&#xff09;来组织组件的逻辑&#xff0c;但这种组织方式在处理复杂组件时可能会…

Czkawka 一款开源、免费的多功能重复文件清理工具,支持多平台

Czkawka (波兰语为 "打嗝"&#xff09;是一款简单、快速、免费的多功能重复文件清理工具&#xff0c;支持空文件、相似图像/视频/音频、损坏/无效文件的查找。 它扫描速度极快&#xff0c;并且适配 Linux、Windows、macOS、FreeBSD 等多个平台、支持中文。 &#x1…