Java解压.lz4文件踩坑实录

文摘   2024-06-13 10:00   江苏  
  1. 背景

    市面上云存储产品蛮多的,没有云的公司,存储一直是个问题,需要花费昂贵的资源存储数据或一些业务需要产生的文件,对于附件,不管时间多长,用户需要的时候就要展示,但频率不高。


  2. 现状

  • 公司当前使用的是阿里云服务,存储自然是OSS,这玩意儿无论是按使用量付费还是包年,总归是要出钱的,区别就是钱多钱少的问题,运维同学自然是想越少越好,每个月找老板签字不会被BB


  • 近期做一个业务,需要下载OSS上的(视频)附件,之后调用算法脚本,他们会解析视频内容按业务生成其它数据。关于Java如何调用算法可以参考:《批量更新SQL如何写?》一篇文末有讲(名字怪是因为当时做那个业务也需要调用算法就放在里面了),有兴趣的同学可以转一下身,跳过去看看。


  • 随着业务产生的视频文件越来多,历史文件跑过算法后属于冷数据,原始大小存储太占用空间浪费钱了,而且还不断有新的文件继续进来,这个时候算法同学想到了要节约空间、节约上传时间(也就变向节约了带宽,这些都是钱呐),想到了对文件进行压缩的正常操作,有一说一,压缩后的文件是.lz4我是第一次接触,正常压缩大家都是zip、rar、tar,新后缀着实让我开了眼


  • 学习

  • 官网(https://lz4.org)对LZ4的概述如下:

    译:LZ4 是一种无损压缩算法,每个内核的压缩速度大于 500 MB/s(大于 0.15 字节/周期)。它的解码器速度极快,每个内核可达到数 GB/秒(~1 字节/周期)。它还有一个名为 LZ4_HC 的高压缩衍生工具,可根据压缩率定制 CPU 时间。LZ4 库采用 BSD 许可作为开源软件提供。(by www.deepl.com

    压缩率:


    lz4: 559 ÷ 2048 ≈ 0.27 

    zip: 445 ÷ 1638 ≈ 0.28


    假设上传速度 10 MB/s ,下载速度100 MB/s,对于一个100TB的数据在不压缩的情况下:

    上传时间为:(100 TB * (1000^2) MB/TB) ÷ (10MB/s*3600s/hour) ≈  2777 hours ≈ 115 days

    下载时间为:(100 TB * (1000^2) MB/TB) ÷ (100MB/s*3600s/hour) ≈  277 hours ≈ 11 days

    zip压缩后:100T*0.28 = 28T

    上传时间:

    (28 TB * (1000^2) MB/TB) ÷ (10 MB/s * 3600 s/hour)  777 hours  32 days

    下载时间:

    (28 TB * (1000^2) MB/TB) ÷ (100MB/s * 3600 s/hour)  77 hours ≈ 3 days

    lz4压缩后:100T*0.27= 27T

    上传时间:

    (27 TB * (1000^2) MB/TB) ÷ (10 MB/s * 3600 s/hour)  750 hours  31days

    下载时间:

    (27 TB * (1000^2) MB/TB) ÷ (100MB/s * 3600 s/hour)  75 hours  3 days


    上面是随便找了个压缩软件做的一个例子对比,本来想准备多弄几个压缩算法的对比数据,但一想到各位都比我聪明,能意会说明目的就达到了


  • 适配

  • 原始文件的压缩后我这边需要改动的有两点:a.原来下载文件的地方改为下载压缩文件;b.调用算法前对压缩文件解压。以上2点改动后程序自动和原有逻辑保持一致,第一点变动无需多说,比较简单。第二点遇到了一些问题

    Plan A:Java解压失败:针对这种公开的算法以为百度一下,导入maven坐标就可以,结果失败了:

    // https://blog.51cto.com/u_16175470/7944619<dependency>    <groupId>org.lz4</groupId>    <artifactId>lz4-java</artifactId>    <version>1.7.1</version></dependency>



    Plan B:让运行同学在机器上装一个LZ4的指令,Java像调用tar指令一样调用是否可以呢?答案是:No


    Plan C:跟算法同学沟通,让他们改算法,将Java传的文件先解压再执行逻辑,可以是可以,但没人(其实不想动);


    Plan D:再次算法同学沟通,怀疑是压缩侧的同学使用的lz4组件不一样(他们是别的语言),所以对方提供了文档,得按着他们给的文档执行指令才可以:


    #Decompresslz4 -d file.lz4

    算法更新,Java发版后进docker是可以执行的,但应用拼接指令发送后Linux底层不执行:



    试了各种办法(https://blog.51cto.com/u_16213332/7535711)都不灵光


    有的说权限、有的说要把命令放到脚本中、在命令中加bash -c 我一一的试了个遍,结果都不行如果是tar可以指定输入文件和输出路径,类似如下:


    现在这个指令(lz4 -d file.lz4)只指定了输入,输出就是在当前目录,让对方升级下也不愿意。后来发现指令挂着时外面满屏的乱码:

    完全看不懂,后来无意间发现在docker下执行时有个进度百分比,上面这些会不会也是执行的时候流输出,把流写到文件是不是就OK了,接着写如下代码:

    String[] arr = new String[]{"lz4" ,"-d" ,"/data/xxx/input.lz4"};File file = new File("/data/xxx/output.dat");ProcessBuilder builder = new ProcessBuilder();builder.redirectOutput(ProcessBuilder.Redirect.to(file));//builder.redirectError(ProcessBuilder.Redirect.INHERIT);builder.command(arr);Process process = builder.start();process.waitFor();int code = process.exitValue();log.info("builder exit code:{}",code);

    至此,上述代码发现能在指令执行后成功的看到了对应目录下的目标文件,存在一个问题就是时间有点长,解压一个文件大约10秒左右,不过这已经成功了一大步,万一不能提升就这样先上线后续再优化。


    继续思考有没有别的方式把这个流转文件,这个就太多了,不用自带的,可以自己读出来再写:

      ProcessBuilder builder = new ProcessBuilder();  builder.command(cmd);  Process process = builder.start();  InputStream input = process.getInputStream();  OutputStream output = new FileOutputStream(outPath);   //这里构建数组大小为1024、2048或 input.available()对性能都有影响,好奇的可以试试  byte[] buffer = new byte[1<<24];  int length;  while ((length = input.read(buffer)) > 0) {      output.write(buffer, 0, length);  }  output.close();  input.close();  process.waitFor();


    这个比上面就快多了,实测下来1~2秒左右就能解压并完成写文件。到这里,本次节能降本Java侧的适配完成


  • 总结

  • 为方便后续其它同学复用,对lz4的解压抽成工具类放到了commot tools中:


    更好的做法其实应该是压缩侧的同学考虑要周全,一个新的指令如果能类似tar命令一样的通用,支持类似:lz4 -d input_file -o output_file,根本就不会这么麻烦(当然也没有这次的学习进步了)。


    对于写文件时数组大小的定义,要多试几次:

    InputStream input = process.getInputStream();byte[] buffer = new byte[1<<24];// ignore

    这里构建数组大小为1024、2048或 input.available()对性能都有影响,好奇的可以试试


    官网:https://lz4.org/ 提供了各种语言相关的文档,压缩和解压都用同一语言应该没有问题。


    P.S:把.lz4文件拷贝到docker容器测试用以下命令:

    docker cp /xxx/xxx.lz4 ${docker_id or server_name}:/xxx/xxx.lz4


    看到的同学觉得有用,点赞、关注、转发搞一个阔以不

    前事不忘,后事之师。·

    刘向《战国策·赵策一》

    晚霞程序员
    一位需要不断学习的30+程序员……