GZipStream/DeflateStream 压缩替代方案

got*_*tqn 6 compression sql-server-2012 sql-clr

我创建了一个简单的 CLR 函数来压缩/解压缩NVARCHAR列:

[SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true)]
public static SqlBinary Compress( string str ){
    if( str == null ){return new SqlBinary();}

    if( String.IsNullOrEmpty( str ) ){str = " ";}

    byte[] bytes = Encoding.Unicode.GetBytes( str );
    using( MemoryStream msi = new MemoryStream( bytes ) ){
        using( MemoryStream mso = new MemoryStream() ){
            using( GZipStream gs = new GZipStream( mso, CompressionMode.Compress ) ){
                msi.CopyTo( gs );
            }
            return new SqlBinary( mso.ToArray() );
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我得到的压缩率大约是 4,或者如果我有 1024 KB 的未压缩数据,我将得到 256 KB 的压缩数据。我知道比率取决于数据本身及其大小,但我想获得更好的比率。

由于我使用SQL Server 2012和.NET 4.0,是有变化的是,按压不给,因为像问题的预期比这个

是否有我可以在 SQL CLR 函数中使用的替代类?有这样的替代方案,但目前不支持。

Sol*_*zky 5

这里有一些关于这个的想法:

  1. 您知道您应该对测试过的字符串进行更好的压缩吗?您是否通过 .NET 之外的 gzip 测试过这些相同的字符串?比如在Linux/CygWin上——UNIX实用程序/PHP/等的DOS端口?

  2. 如果您已经更新您的系统与.NET 4.5,那么你正在使用更新的GZipStream。这是因为它在System.dll 中,这是一个受支持的库。您可以使用允许CompressionLevel的新构造函数对此进行测试。只需更改CompressionMode.CompressCompressionLevel.Optimal. SQL Server 绑定到特定版本的CLR,而不绑定到特定版本的.NET Framework。这意味着,任何受支持库中的任何新功能都可以使用,只要您将代码部署到的任何服务器都已更新其 .NET。

    这并不意味着您将获得更好的压缩。我测试了这段代码,它为 PHP 和 Fiddler 生成的“Hello World”提供了相同的 31 个字节,如您链接到的问题之一所述:https : //stackoverflow.com/questions/11435200/why-does -my-c-sharp-gzip-produce-a-larger-file-than-fiddler-or-php

    我刚刚用一串 3405 个随机字符(即“fsdkjf skdj f...”)再次测试。NVARCHAR(4000)在进行了我在此处建议的更改后,我将变量声明为 as并在您的代码中运行它。压缩二进制文件的长度为 211 字节。然后我将相同的字符串复制并粘贴到 Notepad++ 中,确保将编码设置为“UCS-2 Little Endian”并保存。我在 Windows 资源管理器中检查了该文件,它是 6812 字节(数据中的 6810,也由变量的 DATALENGTH 报告,加上字节顺序标记的 2)。我以二进制模式将它传输到 Linux 服务器。Linux 服务器上的文件大小仍为 6812。然后我运行gzip -9它(即最大压缩;默认为-6)。压缩尺寸?231 字节。所以 .NET GZipStream 实际上稍微好一些。

  3. CompressionMode.Compress并且CompressionLevel.Optimal在功能上是等效的。在指定另一个时,每个都是假定的默认值。

  4. 不要string用于输入参数;使用SqlString.

  5. 摆脱byte[] bytes线

  6. new MemoryStream( bytes )将第一个using块更改为:
    new MemoryStream(str.GetUnicodeBytes())

  7. 你可以摆脱这if( str == null ){return new SqlBinary();}条线。无需在 .NET 代码中处理此问题,只需添加WITH RETURNS NULL ON NULL INPUTCREATE FUNCTION 即可。这样,如果输入为 NULL ,SQL Server 甚至不会调用您的代码 :)。请记住,当您有多个输入参数时,如果其中任何一个为 NULL ,此选项将返回NULL。如果自然地至少其中一个应该能够传入 NULL,那么您必须在代码中处理这种情况。

  8. 替换这一行if( String.IsNullOrEmpty( str ) ){str = " ";}——它实际上返回一个不是空字符串的压缩空间——用:

    if (str.Value.Length == 0)
    {
        return SqlBinary.Null;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  9. 我还没有尝试过“zlib”,但不幸的是,“SharpZipLib”和“DotNotZip”都有问题,并且已经好几年没有更新了,也没有迹象表明它们会更新。但是,“DotNetZip”中的错误似乎主要与 zip 文件存档有关,而不是 GZip 功能(它在SQL# 中运行良好:-))。