5

我正在开发基于 MySql 数据库的 PyQT 软件。该数据库包含一些记录的电信号,以及描述这些信号的所有信息(采样率、重新编码的日期等......)。

想一想,一个数据库包含 10 000 到 100 000 行,总大小 > 10Gb。所有这些数据都存储在专用服务器上。事实上,大部分数据是信号本身,它在一个名为analogsignal.signal的BLOB字段中(见下文)

这是数据库的架构:http ://packages.python.org/OpenElectrophy/_images/simple_diagram1.png

我无法更改它(我可以添加列和索引,但我无法移动或删除现有列)。

在软件中,我需要列出所有的模拟信号列(id、名称、通道、t_start、sampling_rate),除了analogsignal.signal,稍后通过analogsignal.id 调用。所以我正在做以下查询

选择 block.id、block.datetime、segment.id、analogsignal.id、analogsignal.name、analogsignal.channel、analogsignal.sampling_rate、block.fileOrigin、block.info

FROM 段、块、模拟信号

WHERE block.id=segment.id_block

AND segment.id=analogsignal.id_segment

由类比信号.id 订购

问题是,由于analogsignal.signal 列的存在,我的查询非常慢(如果请求不在缓存中,则> 10 分钟)。如果我正确理解发生了什么,表格会逐行读取,包括analogsignal.signal,即使analogsignal.signal 不在SELECT 字段中。

有谁知道如何在不将 BLOB 移动到其他表中的情况下优化数据库或查询(我同意这更合乎逻辑,但我不控制这一点)。

谢谢!

这是 AnalogSignal 表的 CREATE TABLE 命令(从评论中提取/格式化)

CREATE TABLE analogsignal 
  ( id int(11) NOT NULL AUTO_INCREMENT, 
    id_segment int(11) DEFAULT NULL,
    id_recordingpoint int(11) DEFAULT NULL, 
    name text, 
    channel int(11) DEFAULT NULL, 
    t_start float DEFAULT NULL, 
    sampling_rate float DEFAULT NULL, 
    signal_shape varchar(128) DEFAULT NULL, 
    signal_dtype varchar(128) DEFAULT NULL, 
    signal_blob longblob, Tag text, 
    PRIMARY KEY (id), 
    KEY ix_analogsignal_id_recordingpoint (id_recordingpoint),
    KEY ix_analogsignal_id_segment (id_segment) 
  ) ENGINE=MyISAM AUTO_INCREMENT=34798 DEFAULT CHARSET=latin1 ;

编辑:问题解决了,这里是关键点:

-我必须添加一个多列索引,在模拟信号表中的所有 SELECT 字段上键入 INDEX
-“TEXT”类型的列阻止了索引的使用。我在 VARCHAR(xx) 中转换了这些 TEXT 字段。为此,我使用了这个简单的命令:

从 table_to_query 中选择 MAX(LENGTH(field_to_query))

在转换之前检查最小文本长度,以确保我不会丢失任何数据

ALTER TABLE table_to_query CHANGE field_to_query field_to_query VARCHAR(24)

我第一次使用了 VARCHAR(8000),但是在这个设置下,VARCHAR 就像一个 TEXT 字段,并且索引不起作用。VARCHAR(24) 没有这样的问题。如果我是对的,则查询中的总 TEXT 长度(包括所有字段)不得超过 1000 个字节

然后我如上所述索引了所有列,索引中没有大小参数

最后,使用更好的查询结构(感谢 DRapp),也改进了查询。我从 215s 传递到 0.016s 的查询,没有缓存......

4

4 回答 4

4

除了通过将数据放入外部物理文件并将路径\文件名存储在相应的记录中来尝试缩小“blob”列要求之外,我还可以尝试以下方法...

我将反转查询并将您的 AnalogSignal 表放在第一位,因为它是 order by 子句的基础,并将查询反向到块。此外,为了避免读取每一行数据,如果您在输出中所需的所有列上构建复合索引,它将创建一个更大的索引,但是查询将直接从键表达式中提取值,而不是从回读到实际的数据行。

在 AnalogSignal ( id, id_segment, name, channel, sampling_rate ) 上创建索引 KeyDataOnly

SELECT STRAIGHT_JOIN
      block.id, 
      block.datetime, 
      segment.id, 
      analogsignal.id, 
      analogsignal.name, 
      analogsignal.channel, 
      analogsignal.sampling_rate, 
      block.fileOrigin, 
      block.info
   FROM 
      analogsignal
         JOIN Segment
            on analogsignal.id_segment = segment.id
            JOIN block
               on segment.id_block = block.id
   ORDER BY 
      analogsignal.id
于 2012-11-08T13:58:10.380 回答
1

如果无法删除 BLOB 列,是否必须填写?您可以添加一列来存储信号的路径/到/文件名,然后将所有信号文件放在适当的目录中。完成后,将 BLOB 字段值设置为 null。

这可能打破了您所受限制的精神。但通常需要规避任意限制。

于 2012-11-08T13:49:31.957 回答
1

因此,根据评论,我确定您的问题是由 MyISAM 存储引擎及其存储数据的行为引起的。有毒物20是对的。无论如何,MySQL 必须跳过那些无效的大块。您可以更改 InnoDB 的存储引擎,这将对解决此问题有很大帮助。如果您在 SELECT ... 部分明确要求它,只会读取 blob 数据。

ALTER TABLE analogsignal ENGINE=InnoDB;

这将需要一段时间,但对性能有很大帮助。您可以在此处阅读有关 InnoDB 文件格式的更多信息:

http://dev.mysql.com/doc/innodb/1.1/en/innodb-row-format-antelope.html http://dev.mysql.com/doc/innodb/1.1/en/innodb-row-format -dynamic.html

免责声明:如果您对表中的任何列使用全文搜索(MATCH ... AGAINST http://dev.mysql.com/doc/refman/5.5/en//fulltext-search.html),则无法更改它到 InnoDB。

于 2012-11-08T14:43:08.607 回答
0

由于模拟信号列非常大,查询将花费很长时间,因为在执行选择查询时它必须跳过(或者如果您隐喻地看到它,则跳过它们)它们。我要做的是:而不是在数据库中有一个blob,而是通过生成二进制文件

$fh = fopen("analogfile.spec", 'w') or die("can't open file");
$data = $yourAnalogDataFromSomewhere;
fwrite($fh, $data);
fclose($fh);

例如,文件名将由列的 ID 给出。您只需在服务器目录结构中添加文件路径,而不是 blob。

这样,您的查询将运行得非常快,因为它不必跳过 blob 列中的大块数据。

于 2012-11-08T13:43:03.460 回答