我正在开发基于MySql数据库的PyQT软件。数据库包含一些记录的电信号,以及描述这些信号的所有信息(采样率,重新编码日期等)。
要想到,一个数据库包含10 000到100 000行,总大小> 10Gb。所有这些数据都存储在专用服务器上。实际上,大多数数据都是信号本身,它位于名为analogsignal.signal的BLOB字段中(见下文)
这是数据库的架构:http://packages.python.org/OpenElectrophy/_images/simple_diagram1.png
我无法更改它(我可以添加列和索引,但我无法移动或删除现有列。)
在软件中,我需要列出所有的analogsignal列(id,name,channel,t_start,sampling_rate),但analogsignal.signal除外,后者通过analogsignal.id调用。所以我正在进行以下查询
SELECT block.id,block.datetime,segment.id,analogsignal.id,analogsignal.name,analogsignal.channel,analogsignal.sampling_rate,block.fileOrigin,block.info
FROM segment,block,analogsignal
WHERE block.id = segment.id_block
AND segment.id = analogsignal.id_segment
ORDER BY analogsignal.id
问题是,由于analogsignal.signal列的存在,我的查询很慢(如果请求不在缓存中,则为10分钟)。如果我正确理解了发生了什么,那么即使analogsignal.signal不在SELECT字段中,也会逐行读取表格,包括analogsignal.signal。
有没有人知道如何优化数据库或查询而不在另一个表中移动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 ;
编辑:问题解决了,以下是关键点:
- 我必须添加一个多列索引,在analogsignal表中的所有SELECT字段上键入INDEX - ' TEXT'的列type阻止了索引的使用。我在VARCHAR(xx)中转换了这些TEXT字段。为此我使用了这个简单的命令:
SELECT MAX(LENGTH(field_to_query))FROM table_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进行查询,没有缓存...
答案 0 :(得分:4)
除了尝试通过将数据放入外部物理文件并仅将路径\文件名存储在相应的记录中来缩小“blob”列要求之外,我将尝试以下作为替代...
我会反转查询并将您的AnalogSignal表放在第一位,因为它是order by子句的基础,并将查询反向反转到块。此外,为了防止必须读取每个文本行的数据,如果在输出中所需的所有列上构建复合索引,它将生成更大的索引,但查询将直接从键表达式中提取值而不是从回读到实际的数据行。
在AnalogSignal上创建索引KeyDataOnly(id,id_segment,name,channel,sampling_rate)
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
答案 1 :(得分:1)
如果您无法删除BLOB列,是否必须填写它?您可以添加一列来存储信号的路径/到/文件名,然后将所有信号文件放在相应的目录中。完成后,将BLOB字段值设置为null。
它可能打破了你所受限制的精神。但是经常需要规避任意限制。
答案 2 :(得分:1)
所以根据评论我确定你的问题是由MyISAM存储引擎及其存储数据的行为引起的。 toxicate20是对的。 MySQL无论如何都必须跳过那些无效的大blob。您可以更改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。
答案 3 :(得分:0)
由于模拟信号列非常大,查询将花费很长时间,因为在执行选择查询时,它必须跳过(或者如果你隐喻地跳过它们)。我要做的是以下内容:不是在数据库中有blob,而是通过
生成二进制文件$fh = fopen("analogfile.spec", 'w') or die("can't open file");
$data = $yourAnalogDataFromSomewhere;
fwrite($fh, $data);
fclose($fh);
例如,文件名将由列的ID给出。您只需在服务器目录结构中添加文件路径,而不是blob。
这样,您的查询运行速度非常快,因为它不必跳过blob列中的大块数据。