索引SecureRandom字符串列时使用什么前缀

时间:2019-05-28 00:47:00

标签: mysql ruby indexing

我有一个DB列(类型varchar(255)),该列存储SecureRandom.urlsafe_base64生成的URL安全的base 64字符串。方法调用使用默认值,因此结果应为16 bytes or 22 characters in length

base64值用于在用户访问站点时查找记录,以掩盖数据库ID。由于此查找,我需要为该列创建索引,但是我不想为整个列建立索引,因为可能在存储方面效率很低。

确定在这种情况下使用的最佳索引前缀的最佳方法是什么?我现在在想的是这样的:

  1. 创建具有约10万条记录的样本数据以模拟生产数据
  2. 为基数为64的列添加一个带有前缀(例如8个字符)的索引
  3. 在基础64列的查找上运行EXPLAIN,以查看有多少 记录需要检查
  4. 向上或向下调整索引,然后重新执行步骤3。
  5. 选择一个前缀大小,以平衡(a)存储要求和(b)匹配匹配返回的记录数。

这里的问题是我知道SecureRandom会生成唯一的基数为64的字符串,但我不确定它们是多么独特。例如,在10万条记录中,如果我使用8个字符的前缀,那么该前缀将由10条记录或100条记录共享吗?

关于我的方法的一些更具体的问题:

  1. 100k是否足以记录一个样本以选择合适的前缀大小?
  2. 如果我确实使用前缀应用了 索引,我是否怀疑这在存储方面是错误的?
  3. 期望直接查询表并仍然受益于索引的合理记录数量是多少?

注释

  • 我的数据库是MySQL(实际上是Percona)
  • SecureRandom来自Ruby
  • 我假设SecureRandom的URL安全功能没有改变base 64输出的唯一性特征。

2 个答案:

答案 0 :(得分:0)

这只是一个随机数,对吗?不加密。

不要使用前缀;尽管它会缩小索引的大小,但在许多情况下会使索引的使用无效。的确,22个字节长于8个字符的字符串或4个字节的INT。但是,无法使用该索引的不利条件更加严重。

默认值为16(22)足以使随机字符串具有唯一性,从而避免意外碰撞。

如果最大值为22,请不要说VARCHAR(255)。如果是固定长度,请说CHAR(22),如果您允许用户选择最大为16的长度,请说VARCHAR(22)

针对该列说CHARACTER SET ascii COLLATE ascii_bin。这样可以避免(1)utf8的开销和(2)大小写折叠的错误。

如果您要为十亿个此类项目建立索引,则将存在重大的性能问题,如here所述(尽管在不同的上下文中)。一百万行可能不是问题-它取决于索引何时变得大于可以在buffer_pool中的RAM中缓存的索引。

(如果我没记错的话,对于您所描述的8个字符,在300K中有一个机会,一个包含300K条目的索引将包含重复项。但这不是问题。)

答案 1 :(得分:0)

结果

我决定按照我在问题中概述的步骤进行操作。结果,我得到了选择索引前缀所需的信息。

此实验产生了两个变化:

  1. 我将列的大小从255减小到22(也由@Rick James建议)。有关数据为何不超过22个字符的问题,请参阅问题中的详细信息。
  2. 我在列上添加了一个索引,前缀为8个字符。字符集为utf8mb4,因此密钥大小不会与前缀中的字符数成正比。

实验详情

我测试了长度为1、2、4、8和16个字符的前缀,并使用EXPLAIN来查看列中的查找将执行的操作。我还测试了没有索引作为基准。我的实验是基于一组10万条记录。我本可以用更多的记录进行测试,但是出于我的目的,没有必要变得更加精确。

以下是实验的重点:

  • 没有索引,SecureRandom列上的查询将进行表扫描。
  • 使用1个字符的前缀,MySQL必须扫描大约3200条100k的记录。
  • 带有2个字符的前缀,MySQL必须扫描79条记录。
  • 带有4个字符的前缀,MySQL会立即获取准确的记录,而无需在表上进行查找。

除了4个字符的前缀之外,还有一个100k的数据集,并且基于SecureRandom.urlsafe_base64的特征,我无法得到更精确的结果。根据生产中表格的当前大小以及对表格增长率的粗略了解,我们决定将8个字符的前缀用于未来,同时节省磁盘和内存使用。

替代品

通过这次探索,我还了解了MySQL的索引选择性功能。这将允许使用相同的100k记录样本集来测试针对每个前缀长度检索到的记录数,而不必在每次测试之前运行迁移来添加/删除不同的索引。例如,要测试4个字符的前缀的选择性,请执行以下操作:

SELECT count(*) AS count, LEFT(uuid_column, 4) AS prefix
  FROM string_prefix_test GROUP BY count DESC LIMIT 10;

有关索引选择性的更多详细信息,请参见2012年版的高性能MySQL (第160页)。

未来的工作

接下来,我将研究varchar列索引的前缀,这些索引的数据不具有SecureRandom.urlsafe_base64的唯一性。仅因为获取具有代表性的数据集比较困难,这将更具挑战性。