【问题标题】:How can I access MySQL InnoDB index values directly without the MySQL client?如何在没有 MySQL 客户端的情况下直接访问 MySQL InnoDB 索引值?
【发布时间】:2012-10-30 08:17:20
【问题描述】:

我在 InnoDB 表中的列 a VARCHAR(255), b INT 上有一个索引。给定两个 a,b 对,我可以使用 MySQL 索引来确定来自 c 程序的对是否相同(即不使用 strcmp 和数值比较)?

  1. MySQL InnoDB 索引存储在文件系统的什么位置?
  2. 可以从单独的程序中读取和使用它吗?格式是什么?
  3. 如何使用索引来确定两个键是否相同?

注意:这个问题的答案应该是 a) 提供一种访问 MySQL 索引的方法以完成此任务,或者 b) 解释为什么实际上不能以这种方式访问​​/使用 MySQL 索引。特定于平台的答案很好,我使用的是 Red Hat 5.8。


下面是这个问题的先前版本,它提供了更多的上下文,但似乎分散了实际问题的注意力。我知道在 MySQL 中还有其他方法可以完成此示例,我提供了两种方法。这不是关于优化的问题,而是考虑到存在于许多不同动态生成的查询中的复杂性。

我可以使用带有子分组的子选择来完成我的查询,例如

SELECT c, AVG(max_val)
FROM (
    SELECT c, MAX(val) AS max_val
    FROM table
    GROUP BY a, b) AS t
GROUP BY c

但我已经编写了一个 UDF,它允许我通过单个选择来完成,例如

SELECT b, MY_UDF(a, b, val)
FROM table
GROUP by c

这里的关键是我将字段ab 传递给UDF,并且我手动管理每个组中的a,b 子组。列 a 是一个 varchar,因此这涉及到调用 strncmp 以检查匹配项,但速度相当快。

但是,我有一个索引my_key (a ASC, b ASC)。我可以访问和使用 MySQL 索引,而不是手动检查 a 和 b 上的匹配项吗?也就是说,我可以在 my_key 中获取给定行的索引值,还是在 c(UDF 内部)中获取 a,b 对?如果是这样,是否可以保证索引值对于任何值a,b 都是唯一的?

我想调用MY_UDF(a, b, val),然后从UDF中查找c中的mysql索引值(a,b)

【问题讨论】:

  • @Clockwork-Muse 我知道如何用 C 编写扩展,它被称为 udf。我专门询问如何从 udf 访问 mysql 索引。你是对的,将子选择放在其他地方不是解决方案。
  • 承认我从来没有写过:不,我不这么认为。或者至少,如果不写一些部署特定的东西(而且真的很hacky) - 你可能不得不硬编码表的名称。鉴于functions seems to work 的调用顺序,您实际上甚至看不到实际的表对象(或索引)本身,您会从优化器中获得预先排序和预先分组的行。就个人而言,无论如何我都不想这样做,因为它允许更好的未来灵活性(以防应该选择不同的索引)。
  • 您想通过在最里面的查询中选择未分组和未聚合的c 来实现什么?您能否提供一些示例数据和所需的输出?
  • 听起来您正在寻找一种接近于 hack 的解决方案。如果我的理解是正确的,请看一下this post:这家伙毫不掩饰它是一个黑客,尽管包括mysql_priv.h 无论如何都会放弃实现的黑客性质。看起来他的代码可以从 UDF 的主体中访问内存中的 mysql 表;相同的路径可能会引导您找到允许您访问索引的解决方案。
  • @jmilloy MySQL 访问表和 UDF 使用 MySQL 的 private 标头访问表之间的区别在于 UDF 与 MySQL 的发布周期不同:如果设计师MySQL 决定明天更改他们的标题和内部结构,他们也会更改其余代码以匹配标题中的更改,并且没问题;相比之下,UDF 就会崩溃。更糟糕的是,他们可以在不让任何人知道的情况下对其语义进行重大更改。对他们来说,这是一场公平的比赛,因为标题是私有的。对于其他依赖私有标头的人来说,这是一个很大的风险。

标签: mysql c indexing innodb


【解决方案1】:

回头看看你原来的查询

SELECT c, AVG(max_val)
FROM
(
    SELECT c, MAX(val) AS max_val
    FROM table
    GROUP BY a, b
) AS t
GROUP BY c;

您应该首先确保子选择通过运行为您提供所需的内容

SELECT c, MAX(val) AS max_val
FROM table
GROUP BY a, b;

如果子选择的结果正确,则运行您的完整查询。如果该结果是正确的,那么您应该执行以下操作:

ALTER TABLE `table` ADD INDEX abc_ndx (a,b,c,val);

这将通过仅从索引中获取所有需要的数据来加快查询速度。永远不需要查询源表。

编写 UDF 并将其称为单个 SELECT 只是伪装子选择并产生比查询所需更多的开销。只需将完整查询(对数据进行一次嵌套传递)放在存储过程中,将比在 UDF 中获取大部分数据并迭代执行单行选择更有效(类似于 O(n log n) 运行时间可能更长Sending data 州)。

更新 2012-11-27 13:46 EDT

你可以通过做两件事来访问索引而不接触表格

  • 创建一个体面的覆盖索引

    ALTER TABLE table ADD INDEX abc_ndx (a,b,c,val);

  • 运行我之前提到的SELECT查询

由于查询的所有列都在索引中,查询优化器只会触及索引(或预缓存索引页)。如果表是 MyISAM,你可以...

  1. 将 MyISAM 表设置为具有可在 mysqld 启动时预加载的专用密钥缓存
  2. 运行SELECT a,b,c,val FROM table;将索引页面加载到MyISAM的默认keycache中

相信我,你真的不想违背 mysqld 的意愿访问索引页面。这是什么意思?

对于 MyISAM,MyISAM 表的索引页存储在表的.MYI 文件中。每个 DML 语句都会调用一个全表锁。

对于 InnoDB,索引页面被加载到 InnoDB 缓冲池中。因此,相关的数据页也将加载到 InnoDB 缓冲池中。

由于 MyISAM 需要常量 I/O 或 InnoDB 使用常量 MVCC 协议,您不应该使用 Python、Perl、PHP、C++ 或 Java 来规避对索引页面的访问。

有一个 NoSQL 范式(称为 HandlerSocket)允许对 MySQL 表的低级访问,可以完全绕过 mysqld 的正常访问模式。 I would not recommend it since there was a bug in it when using it to issue writes.

更新 2012-11-30 12:11 EDT

来自您的上一条评论

我正在使用 InnoDB,我可以看到 MVCC 模型如何使事情复杂化。然而,显然 InnoDB 在索引中只存储一个版本(最新的)。相关表的访问模式是一次写入,多次读取,因此如果可以访问索引,它可以为每个键提供一个单一的、可靠的数据。

对于 InnoDB,MVCC 并没有让任何事情复杂化。它实际上可以成为你最好的朋友,前提是:

  • 如果你启用了autocommit(应该默认启用)
  • 相关表的访问模式是一次写入,多次读取

如果重复读取,我希望访问的索引页面几乎永远位于 InnoDB 缓冲池中。 I would just make sure your innodb_buffer_pool_size is set high enough to hold necessary InnoDB data.

【讨论】:

【解决方案2】:

如果您只是想要访问 MySQL 之外的索引,则必须使用MySQL storage engines 之一的 API。默认引擎是 InnoDB。在此处查看概述:InnoDB Internals。这(在非常高的层次上)描述了磁盘上的数据布局和访问它的 API。更详细的描述在这里:Embedded InnoDB

但是,与其编写自己的程序直接使用 InnoDB API(这需要大量工作),不如使用已经完成这项工作的项目之一:

  • HandlerSocket:允许 NoSQL 访问 InnoDB 表,在 UDF 中运行。见a very informative blog post from the developer。 HandlerSocket 的目标是提供一个公开为网络守护进程的 NoSQL 接口,但是您可以使用相同的技术(以及大部分相同的代码)来提供一些将被 MySQL 查询使用的东西。

    李>
  • memcached InnoDB plugin。提供对 InnoDB 表的 memcached 样式访问。

  • HailDB:允许 NoSQL 访问 InnoDB 表,在嵌入式 InnoDB 之上运行。见conference presentation编辑: HailDB 可能无法与 MySQL 并行运行。

我相信其中任何一个都可以与 MySQL 并行运行(使用相同的实时表),并且可以从 C 中使用,因此它们确实满足您的要求。

如果您可以使用/迁移到 MySQL 集群,另请参阅 NDB API,直接 API 和 ndbmemcache,一种使用 memcache API 访问 MySQL 集群的方法。

如果不知道您为什么要这样做,这很难回答,因为不同方法的含义是非常不同的。

【讨论】:

  • 谢谢。我试图详细说明我对此感兴趣的确切原因。我正在动态生成查询,这些查询可以将数据分组到层次结构中的多个级别中的任何一个,但可能需要子分组来正确聚合数据并为特定连接公开正确的数据。我使用 UDF 极大地简化了 sql 生成,它处理相关的不同/子组/聚合功能而不会损失太多速度。但我想知道,在 UDF 内部,我是否可以利用特定索引来根据索引确定两行是否不同。
  • 我用的是InnoDB,相关行只写一次,然后再读,没有更新。
  • @jmilloy:听起来您想像 HandlerSocket 一样直接从 UDF 中访问表。 HandlerSocket 可能是您最好的起点,您可以获取它并对其进行修改,直到它完全符合您的要求。您可能可以完全删除其中的网络(守护程序)部分。
【解决方案3】:

您可能无法直接访问密钥。 我认为这实际上不会对性能产生任何影响。

如果您以正确的顺序设置覆盖索引,MySQL 将不会从硬盘中获取单个页面,而是直接从索引中传递结果。没有比这更快的了。

请注意,如果结果大于您的tmp_table_sizemax_heap_table_size,您的子选择可能最终会出现在磁盘上的临时表中。

如果不确定,请检查Created_tmp_tables_disk_tables 的状态。

您可以在此处找到更多关于 MySQL 如何使用内部临时表的信息 http://dev.mysql.com/doc/refman/5.5/en/internal-temporary-tables.html

如果需要,请发布您的表格结构以供审核。

【讨论】:

  • 嗯。我想使用该键快速确定两行是否根据该索引不同。覆盖索引或临时表将如何影响我确定 UDF 中两行是否不同的能力?
  • 我说的是使用 MySQL 自己的功能优化查询,而不是替换 UDF 中给定的功能
  • 好的。 subselect 版本针对我的需要进行了优化,出于其他原因,我将其替换为单个 select + udf。不过还是谢谢。
【解决方案4】:

没有。没有实用的方法来利用 MySQL 索引,从 C 程序中,以 MySQL 引擎以外的方式访问 MySQL 索引,以检查两个 (a,b) 对(键)是否相同.

还有更实用的解决方案,不需要访问 MySQL 引擎之外的 MySQL 数据文件或编写用户定义的函数。


问:你知道mysql索引在文件系统中的存放位置吗?

文件系统中索引的位置将取决于表的存储引擎。对于 MyISAM 引擎,索引存储在 datadir/database 目录下的 .MYI 文件中; InnoDB 索引存储在 InnoDB 管理的表空间文件中。 f innodb_file_per_table 变量在创建表时设置,innodb_data_home_dir/database 子目录下的每个表都会有一个单独的.ibd 文件。

问:你知道格式是什么吗?

每个存储引擎的存储格式不同,MyISAM、InnoDB等,也取决于版本。就 MySQL 对存储引擎的要求而言,我对数据的存储方式有一定的了解。有关内部的详细信息将特定于每个引擎。

问:是什么让它不切实际?

这是不切实际的,因为它需要大量工作,并且将取决于未来可能发生变化的存储引擎的细节。定义问题空间并编写返回所需内容的 SQL 语句会更实用。

正如 Quassnoi 在他对您的问题的评论中指出的那样,您完全不清楚您要通过创建 UDF 或从 MySQL 外部访问 MySQL 索引来解决什么特定问题。我确信 Quassnoi 有一个很好的方法,可以通过高效的 SQL 语句完成您需要的工作。

【讨论】:

  • 我不反对你,但是......你知道mysql索引在文件系统中的存储位置吗?你知道格式是什么吗?是什么让它不切实际?
  • 我正在使用 InnoDB。我不相信使用了 innodb_file_per_table。
  • @spencer7593,很清楚他想在这里做什么。他想访问 InnoDB 存储引擎。他不想访问 MySQL 数据库引擎。
  • @Pacerier:感谢您的评论。如果您查看该问题的编辑历史记录,您会注意到对“InnoDB”的引用是在我对此答案的编辑之后 3 1/2 小时添加的。当我发布我的答案时,问题中没有提到 InnoDB,或者任何存储引擎,都提到了 MySQL。 OP 对我的简短回答发表了评论,并提出了三个问题。我编辑了我的答案以添加后续内容,以回答 OP 提出的三个具体问题。 InnoDB 的引用被添加到问题 3 1/2 小时后我最后一次编辑问题。
  • @Pacerier:我所说的“完全不清楚”是 什么问题 OP 试图通过访问索引和/或 UDF 来解决。是的,他询问有关在 MySQL 之外访问索引的问题。那很清楚。但我不满意的是对为什么首先需要这样做的描述。这样做会解决什么实际的问题
猜你喜欢
  • 2018-07-28
  • 2019-05-03
  • 2013-06-17
  • 1970-01-01
  • 2014-03-02
  • 1970-01-01
  • 2018-08-19
  • 2014-05-16
  • 2017-04-29
相关资源
最近更新 更多