DoubleCloud 即将关闭。借助限时免费迁移服务迁移到 ClickHouse。立即联系我们。 ->->

博客 / 工程

数据库混淆的五种方法

author avatar
Alexey Milovidov
2023 年 2 月 16 日

很久很久以前……

ClickHouse 用户已经知道,它最大的优势是高速处理分析查询。但像这样的说法需要通过可靠的性能测试来证实。这就是我们今天想谈论的。

benchmarks.png

我们在 2013 年开始运行测试,早于 ClickHouse 作为开源软件发布。当时,我们主要关注的是网络分析产品的数据处理速度。我们从 2009 年 1 月开始存储这些数据,这些数据后来存储在 ClickHouse 中。一部分数据是从 2012 年开始写入数据库的,另一部分是从 OLAPServer 和 Metrage(解决方案以前使用的 数据结构)转换而来的。为了进行测试,我们从 10 亿次页面浏览的数据中随机抽取了第一个子集。我们的网络分析平台当时没有任何查询,所以我们想出了我们感兴趣的查询,使用所有可能的过滤、聚合和排序数据的方式。

ClickHouse 的性能与 Vertica 和 MonetDB 等类似系统进行了比较。为了避免偏差,测试由未参与 ClickHouse 开发的员工执行,并且代码中的特殊情况在获得所有结果之前没有进行优化。我们使用相同的方法来获取用于功能测试的数据集。

ClickHouse 在 2016 年作为开源软件发布后,人们开始质疑这些测试。

私有数据测试的缺点

我们的性能测试

  • 由于使用了无法发布的私有数据,因此无法独立复制。由于相同的原因,某些功能测试对外部用户不可用。
  • 需要进一步开发。测试集需要大幅扩展,以便隔离系统各个部分的性能变化。
  • 没有按提交或按单个拉取请求运行。外部开发人员无法检查其代码以查找性能倒退。

我们可以通过丢弃旧测试并根据公开数据编写新测试来解决这些问题,例如 美国航班数据纽约出租车行程。或者我们可以使用像 TPC-H、TPC-DS 和 星型模式基准测试 这样的基准测试。缺点是这些数据与网络分析数据大不相同,我们更愿意保留测试查询。

为什么使用真实数据很重要

性能应该只在生产环境中的真实数据上进行测试。让我们看一些例子。

示例 1

假设您使用均匀分布的伪随机数填充数据库。在这种情况下,数据压缩将不起作用,尽管数据压缩对于分析数据库至关重要。选择合适的压缩算法以及将其集成到系统中的合适方式,没有万能的解决方案,因为数据压缩需要在压缩和解压缩速度以及潜在的压缩效率之间进行权衡。但是,无法压缩数据的系统注定失败。如果您的测试使用均匀分布的伪随机数,则会忽略此因素,并且结果会失真。

底线:测试数据必须具有真实的压缩率。

示例 2

假设我们对以下 SQL 查询的执行速度感兴趣。

SELECT RegionID, uniq(UserID) AS visitors
    FROM test.hits
GROUP BY RegionID
ORDER BY visitors DESC
LIMIT 10

这是网络分析产品的典型查询。什么会影响处理速度?

  • GROUP BY 如何执行。
  • 用于计算 uniq 聚合函数的哪种数据结构。
  • 存在多少个不同的 RegionID,以及 uniq 函数的每个状态需要多少内存。

但另一个重要因素是,数据的数量在不同地区之间分布不均。(它可能遵循幂律。我将分布放在对数对数图上,但我不能确定。)如果是这样,则具有较少值的 uniq 聚合函数的状态必须使用很少的内存。当存在许多不同的聚合键时,每字节都很重要。我们如何获取具有所有这些属性的生成数据?显而易见的解决方案是使用真实数据。

许多 DBMS 为 COUNT(DISTINCT) 的近似值实现了 HyperLogLog 数据结构,但它们都无法很好地工作,因为该数据结构使用固定数量的内存。ClickHouse 具有一个函数,它使用 三种不同数据结构的组合,具体取决于数据集的大小。

底线:测试数据必须充分代表真实数据的分布特性,即基数(每列的不同值的数目)和跨列基数(跨多个不同列计算的不同值的数目)。

示例 3

与其测试 ClickHouse DBMS 的性能,不如采取一些更简单的方法,例如哈希表。对于哈希表来说,选择合适的哈希函数至关重要。对于 std::unordered_map 来说,这并不那么重要,因为它是一个基于链的哈希表,并且使用素数作为数组大小。GCC 和 Clang 中的标准库实现使用一个简单的哈希函数作为数字类型的默认哈希函数。但是,当我们寻求最大速度时,std::unordered_map 并不是最佳选择。对于开地址哈希表,我们不能只使用标准哈希函数。选择合适的哈希函数成为决定性因素。

很容易找到使用随机数据的哈希表性能测试,这些测试没有考虑使用的哈希函数。许多哈希函数测试还集中在计算速度和某些质量标准上,即使它们忽略了使用的数据结构。但事实是,哈希表和 HyperLogLog 需要不同的哈希函数质量标准。

alexey_chat.png

挑战

我们的目标是获取用于测试性能的数据,这些数据具有与我们的网络分析数据相同的结构,并具有对基准测试很重要的所有属性,但以这样一种方式,即这些数据中不再存在真实网站用户的任何痕迹。换句话说,数据必须匿名,并且仍保留其

  • 压缩率。
  • 基数(不同值的个数)。
  • 多个不同列之间的互基数。
  • 可用于数据建模的概率分布属性(例如,如果我们认为区域按幂律分布,则指数——分布参数——应该在人工数据和真实数据中大致相同)。

如何才能为数据获得类似的压缩率?如果使用 LZ4,二进制数据中的子串必须以大致相同的距离重复,并且重复必须具有大致相同的长度。对于 ZSTD,每个字节的熵也必须一致。

最终目标是创建一个公开可用的工具,任何人都可以使用它将数据集匿名化以供发布。这将使我们能够调试和测试其他人在与我们的生产数据类似的数据上的性能。我们也希望生成的数据有趣。

但是,这些要求定义非常宽松,我们不打算为此任务编写正式的问题陈述或规范。

可能的解决方案

我不想让人觉得这个问题特别重要。它实际上从未被纳入计划,也没有人打算对此进行研究。我希望有一天会产生一个想法,突然我会心情很好,能够把所有其他事情都推迟到以后。

显式概率模型

  • 我们希望保留时间序列数据的连续性。这意味着对于某些类型的数据,我们需要对相邻值之间的差异进行建模,而不是对值本身进行建模。
  • 为了对列的“联合基数”进行建模,我们还必须明确反映列之间的依赖关系。例如,每个用户 ID 通常只有很少的 IP 地址,因此要生成 IP 地址,我们必须使用用户 ID 的哈希值作为种子并添加少量其他伪随机数据。
  • 我们不确定如何表达同一个用户经常在近似相同的时间访问具有匹配域的 URL 的依赖关系。

所有这些都可以用 C++“脚本”编写,其中分布和依赖关系被硬编码。但是,马尔可夫模型是从统计信息与平滑和添加噪声的组合中获得的。我开始编写这样的脚本,但是写了十列的显式模型后,它变得难以忍受地无聊——而网络分析产品的“命中”表早在 2012 年就超过了 100 列。

EventTime.day(std::discrete_distribution<>({
    0, 0, 13, 30, 0, 14, 42, 5, 6, 31, 17, 0, 0, 0, 0, 23, 10, ...})(random));
EventTime.hour(std::discrete_distribution<>({
    13, 7, 4, 3, 2, 3, 4, 6, 10, 16, 20, 23, 24, 23, 18, 19, 19, ...})(random));
EventTime.minute(std::uniform_int_distribution<UInt8>(0, 59)(random));
EventTime.second(std::uniform_int_distribution<UInt8>(0, 59)(random));

UInt64 UserID = hash(4, powerLaw(5000, 1.1));
UserID = UserID / 10000000000ULL * 10000000000ULL
    + static_cast<time_t>(EventTime) + UserID % 1000000;

random_with_seed.seed(powerLaw(5000, 1.1));
auto get_random_with_seed = [&]{ return random_with_seed(); };

优点

  • 概念简单。

缺点

  • 需要大量工作。
  • 该解决方案仅适用于一种类型的数据。

我更喜欢更通用的解决方案,该解决方案可用于混淆任何数据集。

无论如何,此解决方案都可以改进。我们可以实现一个模型目录,而不是手动选择模型,并在其中选择最好的模型(最佳拟合加上某种正则化)。或者也许我们可以对所有类型的字段使用马尔可夫模型,而不仅仅是对文本使用。数据之间的依赖关系也可以自动提取。这将需要计算列之间的相对熵(相对信息量)。一个更简单的替代方案是计算每对列的相对基数(类似于“对于固定值 B,A 的平均不同值有多少个”)。例如,这将清楚地表明 URLDomain 完全依赖于 URL,反之则不然。

但我也不喜欢这个想法,因为要考虑的因素太多,编写它会花费太长时间。

神经网络

正如我已经提到的,这项任务并不在优先事项清单的首位——没有人想过要尝试解决它。但幸运的是,我们的同事伊万·普齐雷夫斯基在高等经济学院任教。他问我是否有适合作为他学生论文课题的有趣问题。当我向他提供这个时,他向我保证它很有潜力。所以我把这个挑战交给了“街上”的一个好小伙沙里夫(不过他必须签署一份保密协议才能访问数据)。

我把我的所有想法都告诉了他,但强调了如何解决这个问题没有任何限制,一个好选择是尝试我所不知道的方法,例如使用 LSTM 生成数据的文本转储。在看到文章循环神经网络的不可思议的有效性后,这似乎很有希望。

第一个挑战是我们需要生成结构化数据,而不仅仅是文本。但目前尚不清楚循环神经网络是否可以生成具有所需结构的数据。有两个方法可以解决这个问题。第一个解决方案是使用单独的模型来生成结构和“填充物”,并且只使用神经网络来生成值。但这种方法被推迟,然后从未完成。第二个解决方案是简单地生成 TSV 转储作为文本。经验表明,文本中的一些行不符合结构,但这些行可以在加载数据时丢弃。

第二个挑战是循环神经网络生成数据序列,因此数据中的依赖关系必须按照序列的顺序排列。但在我们的数据中,列的顺序可能与它们之间的依赖关系相反。我们没有采取任何措施来解决这个问题。

随着夏天的临近,我们有了第一个生成数据的 Python 工作脚本。乍一看,数据质量似乎不错。

python_script.jpg

然而,我们确实遇到了一些困难

  1. 模型的大小约为 1 GB。我们尝试为大小为几个 GB 的数据创建模型(首先)。生成的模型如此之大引起了人们的担忧。是否可以提取它训练过的真实数据?不太可能。但我对机器学习和神经网络不太了解,而且我还没有阅读过这位开发人员的 Python 代码,所以我怎么能确定呢?当时发表了几篇文章,介绍如何在不损失质量的情况下压缩神经网络,但没有实现。一方面,这似乎不是一个严重的问题,因为我们可以选择不发布模型,而只发布生成的数据。另一方面,如果发生过拟合,生成的数据可能包含源数据的一部分。

  2. 在一台具有单核 CPU 的机器上,数据生成速度约为每秒 100 行。我们的目标是生成至少十亿行。计算表明,这在论文答辩日期之前不会完成。使用额外的硬件没有意义,因为目标是制作任何人都可以使用的数据生成工具。

沙里夫试图通过比较统计数据来分析数据质量。除其他事项外,他计算了源数据和生成数据中出现不同字符的频率。结果令人震惊:最常见的字符是 Ð 和 Ñ。

不过,不用担心沙里夫。他成功地完成了论文答辩,我们开心地忘记了所有事情。

压缩数据的变异

假设问题陈述已简化为一个要点:我们需要生成与源数据具有相同压缩率的数据,并且数据必须以相同的速度解压缩。我们如何才能实现这一点?我们需要直接编辑压缩数据字节!这使我们能够更改数据而不更改压缩数据的大小,而且所有操作都将快速执行。尽管它解决的问题与我们最初的问题不同,但我还是想立即尝试一下这个想法。但情况总是这样。

那么我们如何编辑压缩文件呢?假设我们只对 LZ4 感兴趣。LZ4 压缩数据由序列组成,而序列又是未压缩字节(字面量)的字符串,后面跟着匹配复制

  1. 字面量(按原样复制以下 N 个字节)。
  2. 匹配,最小重复长度为 4(在距离为 M 的文件中重复 N 个字节)。

源数据

Hello world Hello。

压缩数据(任意示例)

字面量 12 "Hello world " 匹配 5 12。

在压缩文件中,我们将“匹配”保留原样,并更改“字面量”中的字节值。因此,在解压缩后,我们将获得一个文件,其中所有至少 4 个字节长的重复序列也以相同的距离重复,但它们由一组不同的字节组成(基本上,修改后的文件不包含从源文件中获取的单个字节)。

但我们如何更改字节呢?答案并不明显,因为除了列类型之外,数据本身还具有我们想要保留的内部隐式结构。例如,文本数据通常以 UTF-8 编码存储,我们希望生成的数据也为有效的 UTF-8。我开发了一种简单的启发式方法,它涉及满足多个标准

  • 空字节和 ASCII 控制字符保持原样。
  • 一些标点符号保持原样。
  • ASCII 转换为 ASCII,对于其他所有内容,最高有效位保持不变(或者为不同的 UTF-8 长度编写显式的“if”语句集)。在一个字节类中,随机均匀地选择一个新值。
  • https:// 之类的片段得以保留;否则,它看起来有点傻。

这种方法的唯一缺点是数据模型本身是源数据,这意味着它不能发布。该模型仅适用于生成不超过源数据大小的数据量。相反,以前的方法提供了允许生成任意大小数据的模型。

http://ljc.she/kdoqdqwpgafe/klwlpm&qw=962788775I0E7bs7OXeAyAx
http://ljc.she/kdoqdqwdffhant.am/wcpoyodjit/cbytjgeoocvdtclac
http://ljc.she/kdoqdqwpgafe/klwlpm&qw=962788775I0E7bs7OXe
http://ljc.she/kdoqdqwdffhant.am/wcpoyodjit/cbytjgeoocvdtclac
http://ljc.she/kdoqdqwdbknvj.s/hmqhpsavon.yf#aortxqdvjja
http://ljc.she/kdoqdqw-bknvj.s/hmqhpsavon.yf#aortxqdvjja
http://ljc.she/kdoqdqwpdtu-Unu-Rjanjna-bbcohu_qxht
http://ljc.she/kdoqdqw-bknvj.s/hmqhpsavon.yf#aortxqdvjja
http://ljc.she/kdoqdqwpdtu-Unu-Rjanjna-bbcohu_qxht
http://ljc.she/kdoqdqw-bknvj.s/hmqhpsavon.yf#aortxqdvjja
http://ljc.she/kdoqdqwpdtu-Unu-Rjanjna-bbcohu-702130

结果是积极的,数据很有趣,但有些地方不太对劲。URL 保持相同的结构,但在其中一些 URL 中,很容易识别出原始词语,例如“avito”(俄罗斯一个流行的市场),所以我创建了一个启发式方法,可以交换其中一些字节。

也有一些其他问题。例如,敏感信息可能驻留在以二进制表示形式的 FixedString 列中,并且可能包含 ASCII 控制字符和标点符号,我决定保留它们。但是,我没有考虑数据类型。

另一个问题是,如果列以“长度、值”格式存储数据(这是 String 列的存储方式),我如何确保变异后长度保持正确?当我试图解决这个问题时,我立即失去了兴趣。

随机排列

不幸的是,问题没有得到解决。我们进行了一些实验,结果更糟。唯一剩下的就是无所事事,随机浏览网页,因为魔法消失了。幸运的是,我看到了一页解释了游戏 Wolfenstein 3D 中渲染主角死亡的算法

wolfenstein.gif

动画做得真不错——屏幕上充满了鲜血。这篇文章解释说,这实际上是一种伪随机排列。一组元素的随机排列是随机选择的集合的一对一(一对一)变换。换句话说,映射中每个派生元素都对应一个且只有一个原始元素(反之亦然)。换句话说,它是一种随机遍历数据集所有元素的方法。这正是图片中显示的过程:每个像素都以随机顺序填充,没有任何重复。如果我们在每一步都随机选择一个像素,那么到达最后一个像素需要很长时间。

该游戏使用一种非常简单的伪随机排列算法,称为线性反馈移位寄存器(LFSR)。类似于伪随机数生成器,随机排列,或者更确切地说它们的家族,当通过密钥参数化时,可以是密码学上安全的。这正是我们数据转换所需的功能。但是,细节更复杂。例如,使用预先确定的密钥和初始化向量对 N 字节进行密码学上安全的加密,似乎可以对 N 字节字符串集进行伪随机排列。事实上,这是一种一对一变换,它似乎是随机的。但是,如果我们对所有数据使用相同的变换,结果可能会受到密码分析的攻击,因为多次使用了相同的初始化向量和密钥值。这类似于块密码的电子密码本操作模式。

例如,murmurhash 终结器使用了三次乘法和两次异或移位操作。此操作是一个伪随机排列。但是,我应该指出,哈希函数不必是一对一的(即使是 N 位到 N 位的哈希)。

或者,这里还有另一个来自 Jeff Preshing 网站的来自初等数论的有趣示例

我们如何使用伪随机排列来解决我们的问题?我们可以使用它们来转换所有数字字段,从而我们可以保留所有字段组合的基数和互基数。换句话说,COUNT(DISTINCT) 将返回与转换之前相同的值,而且,对于任何 GROUP BY 也是如此。

值得注意的是,保留所有基数在某种程度上与我们数据匿名化的目标相矛盾。假设有人知道站点会话的源数据包含一个访问了来自 10 个不同国家/地区的站点的用户,并且他们希望在转换后的数据中找到该用户。转换后的数据还显示该用户访问了来自 10 个不同国家/地区的站点,这使得缩小搜索范围变得容易。但是,即使他们发现用户被转换成了什么,它也不会很有用;所有其他数据也已被转换,因此他们将无法弄清楚用户访问了哪些站点或其他任何内容。但是这些规则可以串联使用。例如,假设有人知道我们数据中最常出现的网站是 Google,其次是 Yahoo。在这种情况下,他们可以使用排名来确定哪些转换后的站点标识符实际上代表 Yahoo 和 Google。这没什么奇怪的,因为我们正在处理一个非正式的问题陈述,并且我们试图在数据匿名化(隐藏信息)和保留数据属性(信息披露)之间找到平衡。有关如何更可靠地处理数据匿名化问题的信息,请阅读这篇文章文章

除了保持值的原始基数之外,我还想保持值的量级。我的意思是,如果源数据包含 10 以内的数字,那么我希望转换后的数字也很小。我们如何实现这一点?

例如,我们可以将一组可能的值划分为大小类,并在每个类中分别执行排列(保持大小类)。最简单的方法是将数字中最显著位的最近的 2 的幂或位置作为大小类(它们是一样的)。数字 0 和 1 将始终保持原样。数字 2 和 3 有时会保持原样(概率为 1/2),有时会交换(概率为 1/2)。数字集 1024..2047 将映射到 1024!(阶乘)个变体中的一个,依此类推。对于有符号数字,我们将保留符号。

我们是否需要一对一的函数也是值得怀疑的。我们可能可以使用密码学上安全的哈希函数。转换将不是一对一的,但基数将接近相同。

但是,我们需要一个密码学上安全的随机排列,以便当我们定义一个密钥并使用该密钥推导出一个排列时,在不知道密钥的情况下从重新排列的数据中恢复原始数据将很困难。

有一个问题:除了对神经网络和机器学习一无所知之外,我对密码学也非常无知。那就只剩下我的勇气了。我仍在阅读随机网页,并在Hacker News 上找到了一个指向 Fabien Sanglard 页面上讨论的链接。它有一个指向 Redis 开发人员 Salvatore Sanfilippo 的博客文章 的链接,该文章讨论了使用一种奇妙的通用方法来获取随机排列,称为Feistel 网络

Feistel 网络是迭代的,由多轮组成。每一轮都是一个非凡的变换,它允许你从任何函数获得一对一的函数。让我们看看它是如何工作的。

  1. 参数的位被分成两半
 arg: xxxxyyyy
 arg_l: xxxx
 arg_r: yyyy
  1. 右半部分替换左半部分。在它的位置,我们放置了左半部分的初始值和对右半部分的初始值应用函数的结果的异或运算的结果,如下所示
res: yyyyzzzz
res_l = yyyy = arg_r
res_r = zzzz = arg_l ^ F(arg_r)

还有一种说法是,如果我们对 F 使用密码学上安全的伪随机函数,并且至少应用了四次 Feistel 轮,我们将获得一个密码学上安全的伪随机排列。

这就像一个奇迹:我们取一个基于数据产生随机垃圾的函数,将其插入 Feistel 网络,现在我们有一个基于数据产生随机垃圾的函数,但它是可逆的!

Feistel 网络是几种数据加密算法的核心。我们将要做的就像加密一样,只是它真的很糟糕。有两个原因

  1. 我们以相同的方式独立地加密各个值,类似于电子密码本操作模式。
  2. 我们存储有关量级(最近的 2 的幂)和值符号的信息,这意味着某些值根本不会改变。

这样,我们可以在保留所需属性的同时模糊数字字段。例如,在使用 LZ4 后,压缩率应该保持大致相同,因为源数据中的重复值将在转换后的数据中重复,并且彼此之间的距离相同。

马尔可夫模型

文本模型用于数据压缩、预测输入、语音识别和随机字符串生成。文本模型是所有可能字符串的概率分布。假设我们有一个关于人类可以写的所有书籍的文本的虚构概率分布。为了生成一个字符串,我们只需要从该分布中取一个随机值并返回结果字符串(人类可以写的一本随机书籍)。但是我们如何找出所有可能字符串的概率分布呢?

首先,这将需要太多信息。有 256^10 个可能的字符串,它们长度为 10 字节,需要大量的内存才能显式地写入包含每个字符串概率的表。其次,我们没有足够的统计数据来准确地评估分布。

这就是为什么我们使用从粗略统计数据获得的概率分布作为文本模型的原因。例如,我们可以计算每个字母出现在文本中的概率,然后通过以相同的概率选择每个下一个字母来生成字符串。这个原始模型有效,但字符串仍然很不自然。

为了稍微改进模型,我们还可以利用字母出现时的条件概率,如果它在 N 个特定字母之前。N 是一个预设常量。假设 N = 5,并且我们正在计算字母“e”在字母“compr”之后出现的概率。这种文本模型称为 Order-N 马尔可夫模型。

P(cata | cat) = 0.8
P(catb | cat) = 0.05
P(catc | cat) = 0.1
...

让我们看看马尔可夫模型在Hay Kranen 的网站 上是如何工作的。与 LSTM 神经网络不同,这些模型只有足够的内存用于固定长度 N 的小上下文,因此它们会生成有趣的无意义文本。马尔可夫模型也被用于生成垃圾邮件的原始方法,并且生成的文本可以通过统计不适合该模型的统计数据轻松地区分出真实文本。有一个优势:马尔可夫模型比神经网络工作速度快得多,这正是我们所需要的。

标题示例(我们的示例是土耳其语,因为使用了数据)

Hyunday Butter'dan anket shluha — Politika head manşetleri | STALKER BOXER Çiftede book — Yanudistkarışmanlı Mı Kanal | League el Digitalika Haberler Haberleri — Haberlerisi — Hotels with Centry'ler Neden babah.com

我们可以从源数据中计算统计数据,创建一个马尔可夫模型,并生成新数据。请注意,该模型需要平滑以避免披露有关源数据中罕见组合的信息,但这并不是问题。我们使用 0 到 N 的模型组合。如果统计数据不足以进行 N 阶,则使用 N−1 阶模型。

但我们仍然希望保留数据的基数。换句话说,如果源数据有 123456 个唯一 URL 值,则结果应该具有大约相同数量的唯一值。我们可以使用确定性初始化的随机数生成器来实现这一点。最简单的方法是使用哈希函数并将其应用于原始值。换句话说,我们得到一个由原始值显式确定的伪随机结果。

另一个要求是,源数据可能具有许多不同的 URL,它们以相同的开头但并不完全相同。例如:https://www.clickhouse.com/images/cats/?id=xxxxxx。我们希望结果也具有以相同开头但不同的开头 URL。例如:http://ftp.google.kz/cgi-bin/index.phtml?item=xxxxxx。作为用于使用马尔可夫模型生成下一个字符的随机数生成器,我们将从指定位置的 8 字节滑动窗口中获取哈希函数(而不是从整个字符串中获取)。

https://www.clickhouse.com/images/cats/?id=12345 ^^^^^^^^ distribution: [aaaa][b][cc][dddd][e][ff][ggggg][h]... hash("images/c") % total_count: ^

事实证明,这正是我们所需要的。这是页面标题的示例

PhotoFunia - Haber7 - Hava mükemment.net Oynamak içinde şaşıracak haber, Oyunu Oynanılmaz • apród.hu kínálatában - RT Arabic PhotoFunia - Kinobar.Net - apród: Ingyenes | Posti PhotoFunia - Peg Perfeo - Castika, Sıradışı Deniz Lokoning Your Code, sire Eminema.tv/ PhotoFunia - TUT.BY - Your Ayakkanın ve Son Dakika Spor, PhotoFunia - big film izle, Del Meireles offilim, Samsung DealeXtreme Değerler NEWSru.com.tv, Smotri.com Mobile yapmak Okey PhotoFunia 5 | Galaxy, gt, după ce anal bilgi yarak Ceza RE050A V-Stranç PhotoFunia :: Miami olacaksını yerel Haberler Oyun Young video PhotoFunia Monstelli'nin En İyi kisa.com.tr –Star Thunder Ekranı PhotoFunia Seks - Politika,Ekonomi,Spor GTA SANAYİ VE PhotoFunia Taker-Rating Star TV Resmi Söylenen Yatağa każdy dzież wierzchnie PhotoFunia TourIndex.Marketime oyunu Oyna Geldolları Mynet Spor,Magazin,Haberler yerel Haberleri ve Solvia, korkusuz Ev SahneTv PhotoFunia todo in the Gratis Perky Parti'nin yapıyı bu fotogram PhotoFunian Dünyasın takımız halles en kulları - TEZ

结果

在尝试了四种方法后,我厌倦了这个问题,所以现在是时候选择一些东西,将其变成一个可用的工具,并宣布解决方案了。我选择了使用随机排列和通过密钥参数化的马尔可夫模型的解决方案。它被实现为 clickhouse-obfuscator 程序,它非常易于使用。输入是任何支持格式(如 CSV 或 JSONEachRow)的表转储,命令行参数指定表结构(列名和类型)和密钥(任何字符串,你可以在使用后立即忘记)。输出是相同数量的行混淆数据。

该程序与 clickhouse-client 一起安装,没有依赖项,并且可以在几乎所有 Linux 版本上运行。你可以将其应用于任何数据库转储,而不仅仅是 ClickHouse。例如,你可以从 MySQL 或 PostgreSQL 数据库生成测试数据,或创建与你的生产数据库类似的开发数据库。

clickhouse-obfuscator \
    --seed "$(head -c16 /dev/urandom | base64)" \
    --input-format TSV --output-format TSV \
    --structure 'CounterID UInt32, URLDomain String, \
        URL String, SearchPhrase String, Title String' \
    < table.tsv > result.tsv
clickhouse-obfuscator --help

当然,并非所有事情都那么简单,因为该程序转换的数据几乎完全可逆。问题是是否可以在不知道密钥的情况下执行逆向转换。如果转换使用的是加密算法,那么此操作将与暴力破解搜索一样困难。尽管转换使用了一些加密原语,但它们没有以正确的方式使用,并且数据容易受到某些分析方法的攻击。为避免问题,这些问题在程序的文档中进行了说明(使用 --help 访问它)。

最后,我们转换了我们需要的 用于功能和性能测试的数据集,并获得了数据安全团队的批准发布。

我们的开发人员和社区成员在优化 ClickHouse 内部算法时,使用这些数据进行真实的性能测试。第三方用户可以向我们提供他们混淆的数据,以便我们可以为他们使 ClickHouse 更快。我们还在此数据的基础上发布了一个独立的开放基准测试,用于硬件和云提供商:https://benchmark.clickhouse.com/

分享此帖子

订阅我们的时事通讯

随时了解功能发布、产品路线图、支持和云产品!
正在加载表单...
关注我们
Twitter imageSlack imageGitHub image
Telegram imageMeetup imageRss image