我有一个超过34M行(并且还在增长)的MySQL数据库表。
CREATE TABLE `sensordata` (
`userID` varchar(45) DEFAULT NULL,
`instrumentID` varchar(10) DEFAULT NULL,
`utcDateTime` datetime DEFAULT NULL,
`dateTime` datetime DEFAULT NULL,
`data` varchar(200) DEFAULT NULL,
`dataState` varchar(45) NOT NULL DEFAULT 'Original',
`gps` varchar(45) DEFAULT NULL,
`location` varchar(45) DEFAULT NULL,
`speed` varchar(20) NOT NULL DEFAULT '0',
`unitID` varchar(5) NOT NULL DEFAULT '1',
`parameterID` varchar(5) NOT NULL DEFAULT '1',
`originalData` varchar(200) DEFAULT NULL,
`comments` varchar(45) DEFAULT NULL,
`channelHashcode` varchar(12) DEFAULT NULL,
`settingHashcode` varchar(12) DEFAULT NULL,
`status` varchar(7) DEFAULT 'Offline',
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`),
UNIQUE KEY `id_UNIQUE` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=98772 DEFAULT CHARSET=utf8
我每分钟从多个线程(至少400个线程)访问此表,以将数据插入表中。 随着表的增长,读取和写入数据的速度变慢了。一个SELECT查询过去需要大约25秒,然后我添加了一个唯一索引
UNIQUE INDEX idx_userInsDate ( userID,instrumentID,utcDateTime)
这将读取时间从25秒减少到几毫秒,但由于必须更新每条记录的索引,因此增加了插入时间。 另外,如果我从多个线程运行SELECT查询,同时查询需要很长时间才能返回数据。
这是一个示例查询
Select dateTime from sensordata WHERE userID = 'someUserID' AND instrumentID = 'someInstrumentID' AND dateTime between 'startDate' AND 'endDate' order by dateTime asc;
请有人帮助我,改进表架构或添加有效索引以提高性能。
提前谢谢
答案 0 :(得分:1)
首先:避免索引的varchars,特别是ID。 varchar中的每个字符位置都在内部生成一个自己的索引条目!
2nd:你的select使用dateTime,你的索引设置为utcDateTime。它只接受userID和instrumentID并忽略utcDateTime-Part。
建议:更改id的数据类型并更改索引以匹配查询(dateTime,而不是utcDateTime)
使用索引会降低插入时的性能,不幸的是,现在没有像mysql中索引的填充因子那样。因此,您可以做的最好的事情是尝试尽可能小的索引。
具有随机访问权限的高负载数据库的另一种方法是:写入未编制索引的表,从索引表中读取。在给定的时间,构建索引并交换表(可能需要第三个表来创建索引,而其他的则保持不变)。
答案 1 :(得分:1)
这里的指数不是错误的。它是您的数据类型。随着磁盘上数据的大小增加,所有操作的速度都会降低。索引当然可以帮助加快选择 - 只要你的数据结构合理 - 但似乎它不是
CREATE TABLE `sensordata` (
`userID` int, /* shouldn't this have a foreign key constraint? */
`instrumentID` int,
`utcDateTime` datetime DEFAULT NULL,
`dateTime` datetime DEFAULT NULL,
/* what exactly are you putting here? Are you sure it's not causing any reduncy? */
`data` varchar(200) DEFAULT NULL,
/* your states will be a finite number of elements. They can be represented by constants in your code or a set of values in a related table */
`dataState` int,
/* what's this? Sounds like what you are saving in location */
`gps` varchar(45) DEFAULT NULL,
`location` point,
`speed` float,
`unitID` int DEFAULT '1',
/* as above */
`parameterID` int NOT NULL DEFAULT '1',
/* are you sure this is different from data? */
`originalData` varchar(200) DEFAULT NULL,
`comments` varchar(45) DEFAULT NULL,
`channelHashcode` varchar(12) DEFAULT NULL,
`settingHashcode` varchar(12) DEFAULT NULL,
/* as above and isn't this the same as */
`status` int,
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`),
UNIQUE KEY `id_UNIQUE` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=98772 DEFAULT CHARSET=utf8
答案 2 :(得分:1)
PRIMARY KEY
是UNIQUE
密钥。抛弃多余的UNIQUE(id)
!
是否id
被其他任何表引用?如果没有,那么一起摆脱它。而只是
PRIMARY KEY ( userID, instrumentID, utcDateTime)
即 if 该三元组保证是唯一的。您提到了DST - 使用数据类型TIMESTAMP
而不是DATETIME
。这样做,如果需要,您可以转换为DATETIME
,从而消除其中一列。
一个索引(PK)几乎没有空间,因为它是"聚集的"使用InnoDB中的数据。
你的桌子非常胖,所有VARCHARs
。例如,status
可以缩减为1字节的ENUM。其他人可以正常化。 speed
之类的内容可以是4字节FLOAT
或更小DECIMAL
,具体取决于您需要的范围和精度。
对于34M宽的行,您最近可能已经超出了RAM的可缓存性。通过使行更窄,您将推迟溢出。
为什么要攻击索引?在允许插入行之前,会检查每个UNIQUE
(包括PRIMARY
)索引。通过将其降至1指数,可以最大限度地降低成本。 (InnoDB确实需要PRIMARY KEY
。)
INT
是4个字节。你有十亿个乐器吗?也许instrumentID
可能是SMALLINT UNSIGNED
,这是2个字节,最大为64K?考虑所有其他ID。
你有400 INSERTs
/分钟,对吗?那不错。如果你达到400 /秒,我们需要有不同的谈话。
("填充因子"在MySQL中不可调,因为它没有太大区别。)
你有多少内存? innodb_buffer_pool_size
的设置是什么?最佳值约为可用 RAM的70%。
让我们看看你的主要问题;可能还有其他问题需要解决。