MySQL-需要帮助来提高查询性能

时间:2018-07-16 21:06:13

标签: mysql performance transaction-isolation

最初的问题是基于将tx隔离设置为READ UNCOMMITTED的最佳方式,但是在提出一些建议之后,看来我最初的想法是不正确的。

DDL

CREATE TABLE `tblgpslog` (
    `GPSLogID` BIGINT(20) NOT NULL AUTO_INCREMENT,
    `DTSaved` DATETIME NULL DEFAULT NULL,
    `PrimaryAssetID` BIGINT(20) NULL DEFAULT NULL,
    `SecondaryAssetID` BIGINT(20) NULL DEFAULT NULL,
    `ThirdAssetID` BIGINT(20) NULL DEFAULT NULL,
    `JourneyType` CHAR(1) NOT NULL DEFAULT 'B',
    `DateStamp` DATETIME NULL DEFAULT NULL,
    `Status` VARCHAR(50) NULL DEFAULT NULL,
    `Location` VARCHAR(255) NULL DEFAULT '',
    `Latitude` DECIMAL(11,8) NULL DEFAULT NULL,
    `Longitude` DECIMAL(11,8) NULL DEFAULT NULL,
    `GPSFix` CHAR(2) NULL DEFAULT NULL,
    `Speed` BIGINT(20) NULL DEFAULT NULL,
    `Heading` INT(11) NULL DEFAULT NULL,
    `LifeOdometer` BIGINT(20) NULL DEFAULT NULL,
    `Extra` VARCHAR(20) NULL DEFAULT NULL,
    `BatteryLevel` VARCHAR(5) NULL DEFAULT '--',
    `Ignition` TINYINT(4) NOT NULL DEFAULT '1',
    `Radius` INT(11) NOT NULL DEFAULT '0',
    `GSMLatitude` DECIMAL(11,8) NOT NULL DEFAULT '0.00000000',
    `GSMLongitude` DECIMAL(11,8) NOT NULL DEFAULT '0.00000000',
    PRIMARY KEY (`GPSLogID`),
    UNIQUE INDEX `GPSLogID` (`GPSLogID`),
    INDEX `SecondaryUnitID` (`SecondaryAssetID`),
    INDEX `ThirdUnitID` (`ThirdAssetID`),
    INDEX `DateStamp` (`DateStamp`),
    INDEX `PrimaryUnitIDDateStamp` (`PrimaryAssetID`, `DateStamp`, `Status`),
    INDEX `Location` (`Location`),
    INDEX `DTSaved` (`DTSaved`),
    INDEX `PrimaryAssetID` (`PrimaryAssetID`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=153076364
;

原始查询如下

SELECT L.GPSLogID, L.DateStamp, L.Status, Location, Latitude, Longitude, GPSFix, Speed, Heading, LifeOdometer, BatteryLevel, Ignition, L.Extra 
FROM tblGPSLog L 
WHERE PrimaryAssetID = 183 AND L.GPSLogID > 147694199
ORDER BY DateStamp ASC 
LIMIT 100;

"id","select_type","table","type","possible_keys","key","key_len","ref","rows","Extra"
"1","SIMPLE","L","index_merge","PRIMARY,GPSLogID,PrimaryUnitIDDateStamp,PrimaryAssetID","PrimaryAssetID,PRIMARY","9,8",\N,"96","Using intersect(PrimaryAssetID,PRIMARY); Using where; Using filesort"

几个月前出现了问题,经过一番调查后,我将查询更改为以下内容,但现在的行为非常相似。

EXPLAIN SELECT GPSLogID, DateStamp, tmpA.Status, Location, Latitude, Longitude, GPSFix, Speed, Heading, LifeOdometer, BatteryLevel, Ignition, tmpA.Extra, 
PrimaryAssetID FROM (SELECT L.GPSLogID, L.DateStamp, L.Status, Location, Latitude, Longitude, GPSFix, Speed, Heading, LifeOdometer, 
BatteryLevel, Ignition, L.Extra, PrimaryAssetID 
FROM tblGPSLog L 
WHERE L.GPSLogID > 147694199) AS tmpA 
WHERE PrimaryAssetID = 183 
ORDER BY DateStamp ASC;

"id","select_type","table","type","possible_keys","key","key_len","ref","rows","Extra"
"1","PRIMARY","<derived2>","ALL",\N,\N,\N,\N,"5380842","Using where; Using filesort"
"2","DERIVED","L","range","PRIMARY,GPSLogID","PRIMARY","8",\N,"8579290","Using where"

谢谢您的建议。

吉姆

2 个答案:

答案 0 :(得分:2)

  

我相信将tx隔离设置为READ UNCOMMITTED,将阻止SELECT锁定表。

您为什么会认为“ READ UNCOMMITTED”将完成此任务?

在默认情况下,除了SERIALIZABLE以外,SELECT在所有隔离级别上均已处于非锁定状态。

也就是说,除非您使用FOR UPDATEFOR SHARE / LOCK IN SHARE MODE,否则SELECT始终是非锁定的。使用SERIALIZABLE隔离级别时,SELECT被隐式转换为锁定的SELECT FOR SHARE。参见https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html

我强烈建议从不使用READ UNCOMMITTED。这不是一个好主意,因为您的事务可以读取其他事务的未提交工作,这意味着您可以读取不一致的数据(部分完成的事务)和幻象数据(最终回滚的事务的更改) )。这样做没有好处,而且查询返回错误结果的可能性。

是什么让您认为锁定是造成性能问题的原因?您是否在慢查询日志中发现锁定时间增加了?

更常见的是由于查询优化不佳或系统资源不足而导致的性能问题。

如果您的数据库在8年以上之后变慢,我想数据库会一直增长,直到活动数据集不再适合RAM。


发表评论:

  

是否有工具或方法可以对此进行进一步调查?我知道导致问题的查询,只是无法确定原因

有许多调查工具和方法。关于此主题的书籍有High Performance MySQL之类,整个公司都致力于创建绩效监控工具,例如PerconaVividCortex

如果不知道更多具体细节,我无法猜测一个建议。如果您需要更多帮助,可以在上方编辑您的原始问题并添加:

  • 遇到问题的SQL查询。
  • 遇到问题的查询的EXPLAIN <query>输出。
  • 查询所引用的每个表的SHOW CREATE TABLE <tablename>的输出。您可以在MySQL客户端中运行此语句。

那是给初学者的。

答案 1 :(得分:1)

您的声明

  

很少有INSERT发生时SELECT会碰到表的情况,即使这样做,也不会引起什么大问题。   每周仅在非高峰时段安排一次DELETE语句,

等同于“更改隔离模式没有太大帮助。”

我建议设置long_query_time=1并打开慢速日志。稍后,使用pt-query-digest浏览慢速日志以查找一些“最差”的查询。然后让我们讨论改进它们。

更多

INDEX `PrimaryUnitIDDateStamp` (`PrimaryAssetID`, `DateStamp`,
INDEX `PrimaryAssetID`         (`PrimaryAssetID`)

其中第一个照顾第二个,因此第二个不必要。

PRIMARY KEY             (`GPSLogID`),
UNIQUE INDEX `GPSLogID` (`GPSLogID`),

PK是UNIQUE键,因此请取下第二个。额外的 unique 索引会减慢插入速度并浪费磁盘空间。

在这种情况下,我认为没有理由进行查询和子查询:

SELECT  GPSLogID, DateStamp, tmpA.Status, Location, Latitude,
        Longitude, GPSFix, Speed, Heading, LifeOdometer, BatteryLevel,
        Ignition, tmpA.Extra, PrimaryAssetID
    FROM  
        ( SELECT  L.GPSLogID, L.DateStamp, L.Status, Location, Latitude,
                  Longitude, GPSFix, Speed, Heading, LifeOdometer, BatteryLevel,
                  Ignition, L.Extra, PrimaryAssetID
            FROM  tblGPSLog L
            WHERE  L.GPSLogID > 147694199
        ) AS tmpA
    WHERE  PrimaryAssetID = 183
    ORDER BY  DateStamp ASC;

一对DECIMAL(11,8)加起来最多为12个字节,对于经纬度来说是过大的。有关较小的替代方案,请参见this

桌子的大小一直在增加,对吗?而且,当它变得如此庞大之后,性能会急剧下降吗?缩小数据类型以缩小表是一种方法,尽管是临时解决方法。

Using intersect(PrimaryAssetID,PRIMARY)-几乎总是,建立复合索引比使用“索引合并相交”更好。

尽管

INDEX `PrimaryAssetID`         (`PrimaryAssetID`)

应该等同于

INDEX `PrimaryAssetID`         (`PrimaryAssetID`, GPSLogID)

某种原因阻止了它。建议您添加此2列复合索引。也许很大一部分行都有PrimaryAssetID = 183?如果方便,请执行SELECT COUNT(*) FROM tblgpslog WHERE PrimaryAssetID = 183

您要从此日志中清除“旧”数据吗?如果是这样,则最佳方式涉及PARTITIONing;参见this