【问题标题】:SQL performance searching for long strings搜索长字符串的 SQL 性能
【发布时间】:2012-02-09 06:33:36
【问题描述】:

我需要将用户代理字符串存储在数据库中,以便跟踪和比较不同浏览器之间的客户行为和销售业绩。一个非常简单的用户代理字符串大约有 100 个字符长。决定使用varchar(1024) 在数据库中保存用户代理数据。 (我知道这有点矫枉过正,但这就是想法;它应该容纳未来几年的用户代理数据,并且一些设备、工具栏、应用程序已经推送了 500 个字符的长度。)包含这些字符串的表将被规范化(每个不同的用户代理字符串只会存储一次)并被视为缓存,因此我们不必一遍又一遍地解释用户代理。

典型的用例是:

  • 用户访问我们的网站,被检测为新访客
  • 为此用户创建了新的会话信息
  • 确定我们是否需要分析用户代理字符串,或者我们是否对它进行了有效的分析
  • 如果我们有,很好,如果没有,请分析它(目前,我们计划调用第 3 方 API)
  • 将相关信息(浏览器名称、版本、操作系统等)存储在与现有用户会话信息相关联并指向缓存条目的连接表中

注意:我倾向于说“搜索”数据库中的用户代理字符串,因为这不是简单的查找。但需要明确的是,查询将使用 '=' 运算符,而不是正则表达式或 LIKE % 语法。

因此,查找用户代理字符串的速度至关重要。我已经探索了一些确保它具有良好性能的方法。出于大小原因,索引整个列是正确的。部分索引也不是一个好主意,因为大多数用户代理最后都有区分信息。部分索引必须相当长才能使其值得,此时它的大小会导致问题。

所以它归结为一个散列函数。我的想法是在 Web 服务器代码中对用户代理字符串进行哈希处理,然后运行选择以查找数据库中的哈希值。我觉得这会最大限度地减少数据库服务器上的负载(而不是让它计算散列),特别是因为如果找不到散列,代码会转过来并要求数据库在插入时再次计算散列.

散列到整数值将提供最佳性能,但存在更高冲突的风险。我预计最多会看到数千或数万个用户代理;即使是 100,000 个用户代理也可以相当好地放入一个 2^32 大小的整数中,并且很少有冲突,这些冲突可以被 web 服务破译而对性能的影响最小。即使您认为整数哈希不是一个好主意,使用 32 个字符的摘要(例如 SHA-1、MD5)也应该比原始字符串快得多,对吧?

我的数据库是 MySQL InnoDB 引擎。 Web 代码将首先来自 C#,然后来自 php(在我们整合了一些托管和身份验证之后)(并不是说 Web 代码应该有很大的不同)。

如果您认为这是一个蹩脚的选择我的哈希算法问题,请让我在此道歉。我真的希望从以前做过类似事情的人那里得到一些意见,以及他们的决策过程。那么问题来了:

  • 您会为此应用程序使用哪个哈希?
  • 您是在代码中计算哈希值还是让数据库处理它?
  • 是否有完全不同的方法在数据库中存储/搜索长字符串?

【问题讨论】:

    标签: mysql string hash user-agent


    【解决方案1】:

    您对长字符串进行散列以创建令牌以在存储(缓存或数据库)中查找的想法是一个很好的想法。我已经看到在非常大的字符串和大容量环境中这样做了,而且效果很好。

    “你会为这个应用程序使用哪个哈希?”

    • 我不认为加密(散列)算法真的很重要,因为您不是散列来加密数据,而是散列以创建一个令牌,用作查找较长值的密钥。所以哈希算法的选择应该基于速度。

    “你是在代码中计算哈希还是让数据库处理它?”

    • 如果是我的项目,我会在应用层进行哈希处理,然后通过它在商店中查找(缓存,然后是数据库)。

    “在数据库中存储/搜索长字符串有完全不同的方法吗?”

    • 正如我所提到的,我认为对于您的特定目的,您提出的解决方案是一个很好的解决方案。

    餐桌推荐(仅供演示):

    user

    • id int(11) unsigned not null
    • name_first varchar(100) 不为空

    user_agent_history

    • user_id int(11) unsigned not null
    • agent_hash varchar(255) 不为空

    agent

    • agent_hash varchar(255) 不为空
    • browser varchar(100) 不为空
    • agent 文本不为空

    关于架构的几点说明:

    • 从您的 OP 看来,您需要在用户和代理之间建立 M:M 关系,因为用户可能在工作中使用 Firefox,但随后可能在家里切换到 IE9。因此需要数据透视表。

    • 用于agent_hash 的 varchar(255) 有待讨论。 MySQLsuggests 使用 varbinary 列类型来存储哈希,其中有几种类型。

    • 我还建议将 agent_hash 设为主键,或者至少为列添加一个 UNIQUE 约束。

    【讨论】:

    • 非常感谢您的反馈。如果我最终得到一个输出大于 8 字节的散列 (bigint),我肯定会研究 varbinary 类型。我读到的一件事是使用更长的散列函数并对高位进行异或运算,现在一起结束。这样做的好处是让我可以使用内置的通用哈希算法,但输出大小较小,可以更快地搜索。再加上还存储用户代理字符串长度的想法,我想我仍然可以很好地避免碰撞。
    • 每个用户会话都将获得自己的用户代理条目,因此这些表将保持一对一。出于安全目的,切换浏览器、位置等需要一个新会话。代理哈希必须保持非唯一、非主要的;我预计不会发生很多碰撞,但多年来将有一个非零数量,并且必须围绕它进行设计。
    • “内置的通用哈希算法”是什么意思?内置在 MySQL 中?此外,您的用户跟踪表将是暂时的,就像会话跟踪表一样?您可以将您的应用程序设置为记录新代理以及相关的哈希,因此当用户启动新会话时,您可以从用户代理创建一个哈希,并将其与您的 agent 表进行比较(如果它不存在)你可以添加它。
    • 我的意思是一种散列算法,它包含在我们服务器上使用的开发平台中。目前,我们已经广泛部署了 C#、MSSQL、MySQL 和 PHP;常见的散列算法是 MD5、RipeMD 和 SHA 系列。我想使用 32 位哈希摘要,但我找不到任何不需要包括额外的 3rd 方库或自定义代码的选项。用户跟踪表将永久保存在我们的 MySQL 数据库中(目前,它们可能会移动)。用户代理和它们代表的浏览器/设备的属性在他们自己的表中,每个不同的 UA 将被存储一次
    【解决方案2】:

    你的哈希想法是正确的。我实际上已经使用散列来加快对数百万条记录的搜索。哈希索引会更快,因为每个条目的大小相同。 md5 在您的情况下可能会很好,并且可能会给您最短的哈希长度。如果您担心哈希冲突,可以添加包含代理字符串的长度。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-08-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多