我centOS上的MySQL速度很慢

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我centOS上的MySQL速度很慢相关的知识,希望对你有一定的参考价值。

我用JAVA写了一条读取1000次查询,在本地测试时相当快,在centOS服务器上就相当慢,我用ssh直接在服务器上运行java程序的。速度很慢。后来又写了一个php的,用浏览器访问,速度也慢。我看了很多mysql优化的,也没什么效果。mysql占用cpu %CPU 199.0,TOP命令显示是这样。
不知道是什么问题?

参考技术A 你1000查询的表有多少数据?
查询条件是怎样的?
SQL语句的条件中是否用到了索引字段?
另外就还要看你服务器硬件配置怎样了。

MYSQL 查询执行速度很慢

【中文标题】MYSQL 查询执行速度很慢【英文标题】:MYSQL query performs very slow 【发布时间】:2016-06-08 05:56:07 【问题描述】:

我开发了一个用户批量上传模块。有两种情况,当我在数据库的记录为零时批量上传 20 000 条记录。大约需要5个小时。但是当数据库已经有大约 30 000 条记录时,上传非常非常慢。上传 20 000 条记录大约需要 11 个小时。我只是通过fgetcsv 方法读取一个CSV 文件。

if (($handle = fopen($filePath, "r")) !== FALSE) 
            while (($peopleData = fgetcsv($handle, 10240, ",")) !== FALSE) 
                if (count($peopleData) == $fieldsCount) 

//inside i check if user already exist (firstName & lastName & DOB)
//if not, i check if email exist. if exist, update the records.
//other wise insert a new record.

以下是运行的查询。 (我用的是 Yii 框架)

SELECT * 
FROM `AdvanceBulkInsert` `t` 
WHERE renameSource='24851_bulk_people_2016-02-25_LE CARVALHO 1.zip.csv' 
LIMIT 1

SELECT cf.*, ctyp.typeName, cfv.id as customId, cfv.customFieldId, 
       cfv.relatedId, cfv.fieldValue, cfv.createdAt 
FROM `CustomField` `cf` 
    INNER JOIN CustomType ctyp on ctyp.id = cf.customTypeId 
    LEFT OUTER JOIN CustomValue cfv on cf.id = cfv.customFieldId 
                and relatedId = 0 
    LEFT JOIN CustomFieldSubArea cfsa on cfsa.customFieldId = cf.id 
WHERE ((relatedTable = 'people' and enabled = '1') 
  AND (onCreate = '1')) 
  AND (cfsa.subarea='peoplebulkinsert') 
ORDER BY cf.sortOrder, cf.label

SELECT * 
FROM `User` `t` 
WHERE `t`.`firstName`='Franck' 
  AND `t`.`lastName`='ALLEGAERT ' 
  AND `t`.`dateOfBirth`='1971-07-29' 
  AND (userType NOT IN ("1")) 
LIMIT 1

如果存在更新用户:

UPDATE `User` SET `id`='51394', `address1`='49 GRANDE RUE', 
                  `mobile`='', `name`=NULL, `firstName`='Franck', 
                  `lastName`='ALLEGAERT ', `username`=NULL, 
                  `password`=NULL, `email`=NULL, `gender`=0, 
                  `zip`='60310', `countryCode`='DZ', 
                  `joinedDate`='2016-02-23 10:44:18', 
                  `signUpDate`='0000-00-00 00:00:00', 
                  `supporterDate`='2016-02-25 13:26:37', `userType`=3, 
                  `signup`=0, `isSysUser`=0, `dateOfBirth`='1971-07-29', 
                  `reqruiteCount`=0, `keywords`='70,71,72,73,74,75', 
                  `delStatus`=0, `city`='AMY', `isUnsubEmail`=0, 
                  `isManual`=1, `isSignupConfirmed`=0, `profImage`=NULL, 
                  `totalDonations`=NULL, `isMcContact`=NULL, 
                  `emailStatus`=NULL, `notes`=NULL, 
                  `addressInvalidatedAt`=NULL, 
                  `createdAt`='2016-02-23 10:44:18', 
                  `updatedAt`='2016-02-25 13:26:37', `longLat`=NULL 
WHERE `User`.`id`='51394'

如果用户不存在,插入新记录。

表引擎类型是 MISAM。只有 email 列有索引。

如何优化它以减少处理时间?

查询 2 花费了 0.4701 秒,这意味着对于 30 000 条记录,它将花费 14103 秒,也就是大约 235 分钟。大约 6 小时。

更新

CREATE TABLE IF NOT EXISTS `User` (
  `id` bigint(20) NOT NULL,
  `address1` text COLLATE utf8_unicode_ci,
  `mobile` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL,
  `name` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL,
  `firstName` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `lastName` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `username` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
  `password` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL,
  `email` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL,
  `gender` tinyint(2) NOT NULL DEFAULT '0' COMMENT '1 - female, 2-male, 0 - unknown',
  `zip` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL,
  `countryCode` varchar(3) COLLATE utf8_unicode_ci DEFAULT NULL,
  `joinedDate` datetime DEFAULT NULL,
  `signUpDate` datetime NOT NULL COMMENT 'User signed up date',
  `supporterDate` datetime NOT NULL COMMENT 'Date which user get supporter',
  `userType` tinyint(2) NOT NULL,
  `signup` tinyint(2) NOT NULL DEFAULT '0' COMMENT 'whether user followed signup process 1 - signup, 0 - not signup',
  `isSysUser` tinyint(1) NOT NULL DEFAULT '0' COMMENT '1 - system user, 0 - not a system user',
  `dateOfBirth` date DEFAULT NULL COMMENT 'User date of birth',
  `reqruiteCount` int(11) DEFAULT '0' COMMENT 'User count that he has reqruited',
  `keywords` text COLLATE utf8_unicode_ci COMMENT 'Kewords',
  `delStatus` tinyint(2) NOT NULL DEFAULT '0' COMMENT '0 - active, 1 - deleted',
  `city` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
  `isUnsubEmail` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0 - ok, 1 - Unsubscribed form email',
  `isManual` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0 - ok, 1 - Manualy add',
  `longLat` varchar(45) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Longitude and Latitude',
  `isSignupConfirmed` tinyint(4) NOT NULL DEFAULT '0' COMMENT 'Whether user has confirmed signup ',
  `profImage` tinytext COLLATE utf8_unicode_ci COMMENT 'Profile image name or URL',
  `totalDonations` float DEFAULT NULL COMMENT 'Total donations made by the user',
  `isMcContact` tinyint(1) DEFAULT NULL COMMENT '1 - Mailchimp contact',
  `emailStatus` tinyint(2) DEFAULT NULL COMMENT '1-bounced, 2-blocked',
  `notes` text COLLATE utf8_unicode_ci,
  `addressInvalidatedAt` datetime DEFAULT NULL,
  `createdAt` datetime NOT NULL,
  `updatedAt` datetime DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

CREATE TABLE IF NOT EXISTS `AdvanceBulkInsert` (
  `id` int(11) NOT NULL,
  `source` varchar(256) NOT NULL,
  `renameSource` varchar(256) DEFAULT NULL,
  `countryCode` varchar(3) NOT NULL,
  `userType` tinyint(2) NOT NULL,
  `size` varchar(128) NOT NULL,
  `errors` varchar(512) NOT NULL,
  `status` char(1) NOT NULL COMMENT '1:Queued, 2:In Progress, 3:Error, 4:Finished, 5:Cancel',
  `createdAt` datetime NOT NULL,
  `createdBy` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

CREATE TABLE IF NOT EXISTS `CustomField` (
  `id` int(11) NOT NULL,
  `customTypeId` int(11) NOT NULL,
  `fieldName` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `relatedTable` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `defaultValue` text COLLATE utf8_unicode_ci,
  `sortOrder` int(11) NOT NULL DEFAULT '0',
  `enabled` char(1) COLLATE utf8_unicode_ci DEFAULT '1',
  `listItemTag` char(1) COLLATE utf8_unicode_ci DEFAULT NULL,
  `required` char(1) COLLATE utf8_unicode_ci DEFAULT '0',
  `onCreate` char(1) COLLATE utf8_unicode_ci DEFAULT '1',
  `onEdit` char(1) COLLATE utf8_unicode_ci DEFAULT '1',
  `onView` char(1) COLLATE utf8_unicode_ci DEFAULT '1',
  `listValues` text COLLATE utf8_unicode_ci,
  `label` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `htmlOptions` text COLLATE utf8_unicode_ci
) ENGINE=MyISAM AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

CREATE TABLE IF NOT EXISTS `CustomFieldSubArea` (
  `id` int(11) NOT NULL,
  `customFieldId` int(11) NOT NULL,
  `subarea` varchar(256) COLLATE utf8_unicode_ci NOT NULL
) ENGINE=MyISAM AUTO_INCREMENT=43 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

CREATE TABLE IF NOT EXISTS `CustomValue` (
  `id` int(11) NOT NULL,
  `customFieldId` int(11) NOT NULL,
  `relatedId` int(11) NOT NULL,
  `fieldValue` text COLLATE utf8_unicode_ci,
  `createdAt` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=MyISAM AUTO_INCREMENT=86866 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

整个 PHP 代码在这里http://pastie.org/10737962

更新 2

解释查询的输出

【问题讨论】:

发布一些完整的代码,这样更有意义。还要确保在 JOIN 点上建立了索引。 你知道哪个部分需要时间吗?更新或选择之一 如果您发布您的问题,这样我们就可以真正阅读它,这会有所帮助。 首先要优化的是不要使用SELECT * 来检查是否存在使用该婴儿车的用户,您可以在该表中仅SELECT id id 这意味着用户存在 正如我所说的将索引添加到连接点! 【参考方案1】:

索引是你的朋友。

UPDATE User ... WHERE id = ... -- 迫切需要一个 ID 索引,可能是PRIMARY KEY

renameSource 也是如此。

SELECT * 
FROM `User` `t` 
WHERE `t`.`firstName`='Franck' 
  AND `t`.`lastName`='ALLEGAERT ' 
  AND `t`.`dateOfBirth`='1971-07-29' 
  AND (userType NOT IN ("1")) 
LIMIT 1;

需要INDEX(firstName, lastName, dateOfBirth);字段可以按任何顺序排列(在这种情况下)。

查看每个查询以了解它需要什么,然后将 INDEX 添加到表中。 Read my Cookbook on building indexes.

【讨论】:

对于名字、姓氏和出生日期,单独的索引在部分姓名搜索或出生日期范围等情况下会更有用。 firstName LIKE 'F%' 不会很好地处理我的索引。但是,生日范围就可以了,因为该列在索引的最后。 userType 添加到索引上可能会有所帮助。【参考方案2】:

尝试以下方法来提高查询性能:

在您的数据库结构中定义索引,并只获取您想要的列。 不要在选择查询中使用 *。 不要将 ID 放在 User.id='51394' 这样的引号中,而是使用 User.id= 51394。 如果您在引号中提供 ID,那么您的索引将不起作用。这种方法将您的查询性能提高了 20%。 如果您使用的是ENGINE=MyISAM,那么您无法在数据库表之间定义索引,请将数据库引擎更改为ENGINE=InnoDB。并创建一些索引,如外键、全文索引。

【讨论】:

我已经进行了一些快速测试并将 ID 是否放在引号中似乎对查询时间没有明显影响。你有支持 20% 速度提升的参考吗? 我说的是不带引号的 id。并应用索引 @MattRaines:只有在定义主键(将应用索引)时,使用整数而不是字符串才会有所作为。你可以在这里阅读更多信息:***.com/questions/1071180/… 在该问题或任何答案上都没有提到类型强制。根据EXPLAIN,使用引号对查询是否使用主键没有影响。 mysql> 解释 SELECT * FROM foo WHERE id = '1'\G *************************** 1. 行 ***** ************************ id: 1 select_type: SIMPLE table: foo type: const possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: const rows: 1 Extra : 使用索引【参考方案3】:

如果我理解,对于SELECT * FROM AdvanceBulkInsert 的所有结果...您运行一个请求SELECT cf.*,对于所有SELECT cf.*,您运行SELECT * FROM User

我认为问题在于您向基地发送了太多请求。

我认为您应该将所有选择请求合并到一个大请求中。

为此:

替换 SELECT * FROM AdvanceBulkInsert EXISTS IN (SELECT * FROM AdvanceBulkInsert where ...)JOIN

SELECT * FROM User 替换为NOT EXISTS IN(SELECT * from User WHERE )

然后你在合并选择的所有结果上调用更新。

您也应该对您的请求逐个计时,以找出其中哪个请求花费的时间最多,并且 你也应该use ANALYSE 找出请求的哪一部分需要时间。

编辑:

现在我看到了你的代码:

一些线索:

你有没有索引 cf.customTypeId 、 cfv.customFieldId 、 cfsa.customFieldId 、用户。出生日期,用户。 firstName,user.lastName ?

如果您有一个使用 CustomFieldSubArea 的 WHERE,则不需要执行 LEFT JOIN CustomFieldSubArea,一个简单的 JOIN CustomFieldSubArea 就足够了。

您将多次使用 relatedId = 0 启动查询 2,也许您可​​以将结果保存在 var 中?

如果您不需要排序数据,请删除 "ORDER BY cf.sortOrder, cf.label" 。 否则,在 cf.sortOrder、cf.label 上添加索引

【讨论】:

能否给我们 CustomValue::model()->getCustomData 的代码?另外,你能对查询 2 做一个解释吗?(dev.mysql.com/doc/refman/5.7/en/using-explain.html)) 这里是CustomValue::model()->getCustomDatapastie.org/10738382的代码 Query 2 explain 已在问题中更新。顺便说一句,当我通过删除cf.* 并添加cf.fieldName,cf.label,cf.required,cf.defaultValue,cf.listValues,cf.htmlOptions 来更改查询时。它好一点。现在从 csv 文件处理 30 000 条用户记录只需 106 分钟。【参考方案4】:

当您需要找出查询需要很长时间的原因时,您需要检查各个部分。正如您在问题Explain statement 中所示,可以为您提供很大帮助。通常最重要的列是:

select_type - 这应该始终是简单查询/子查询。相关子查询带来很多麻烦。幸运的是你没有使用任何 可能的键 - 此选择要搜索的键是什么 rows - 有多少候选行由键/缓存和其他技术确定。数字越小越好 额外 - “使用”告诉您找到的行的准确程度,这是最有用的信息

查询分析

我会发布第一个和第三个查询的分析,但它们都是非常简单的查询。以下是给您带来麻烦的查询的细分:

EXPLAIN SELECT cf.*, ctyp.typeName, cfv.id as customId, cfv.customFieldId, 
   cfv.relatedId, cfv.fieldValue, cfv.createdAt 
FROM `CustomField` `cf` 
    INNER JOIN CustomType ctyp on ctyp.id = cf.customTypeId 
    LEFT OUTER JOIN CustomValue cfv on cf.id = cfv.customFieldId 
                and relatedId = 0 
    LEFT JOIN CustomFieldSubArea cfsa on cfsa.customFieldId = cf.id 
WHERE ((relatedTable = 'people' and enabled = '1') 
  AND (onCreate = '1')) 
  AND (cfsa.subarea='peoplebulkinsert') 
ORDER BY cf.sortOrder, cf.label
ctyp.id 上的 INNER JOIN CustomType ctyp = cf.customTypeId cf.id 上的左外连接 CustomValue cfv = cfv.customFieldIdrelatedId = 0 cfsa.customFieldId 上的左连接 CustomFieldSubArea cfsa = cf.id WHERE ((relatedTable = 'people' and enabled = '1') AND (onCreate = '1')) AND (cfsa.subarea='peoplebulkinsert') 按 cf.sortOrder 排序,cf.label

解决方案

让我解释一下上面的列表。 粗体列完全必须有索引。连接表是一项昂贵的操作,否则需要遍历两个表的所有行。如果您在可连接列上创建索引,数据库引擎将找到更快更好的方法来完成它。这应该是任何数据库的常见做法

italic 列不是必须要有索引,但如果你有大量的行(20 000 是很大的数量),你还应该对用于搜索的列有索引,它可能不会对处理速度产生这种影响,但值得多花一点时间。

所以你需要在这些列中添加索引

CustomType - id CustomField - customTypeId、id、relatedTable、启用、onCreate、sortOrder、标签 CustomValue - customFieldId CustomFieldSubArea - customFieldId,子区域

要验证结果,请尝试在添加索引后再次运行解释语句(可能还有一些其他选择/插入/更新查询)。额外的列应该说“使用索引”之类的内容,并且 possible_keys 列应该列出使用的键(每个连接查询甚至两个或更多)。

旁注:您的代码中有一些拼写错误,您应该修复它们以防其他人也需要处理您的代码:“reqruiteCount”作为表列,“fileUplaod”作为引用代码中的数组索引。

【讨论】:

为了获得更好的性能,在适用的情况下使用复合索引,不要很多单独的索引。 MySQL 几乎从不在一个查询中使用两个索引。 (这是因为使用两个索引效率低下。)【参考方案5】:

对于我的工作,我必须每天添加一个包含 524 列和 10k 记录的 CSV。当我尝试解析它并用php添加记录时,太可怕了。

所以,我建议你看看关于LOAD DATA LOCAL INFILE的文档

例如,我复制/粘贴了我自己的代码,但让他适应您的需求

$dataload = 'LOAD DATA LOCAL INFILE "'.$filename.'"
                REPLACE
                INTO TABLE '.$this->csvTable.' CHARACTER SET "utf8"
                FIELDS TERMINATED BY "\t"
                IGNORE 1 LINES
            ';

$result = (bool)$this->db->query($dataload);

其中 $filename 是 CSV 的本地路径(您可以使用 dirname(__FILE__) 获取它)

此 SQL 命令非常快(添加/更新所有 CSV 只需 1 或 2 秒)

编辑:阅读文档,但当然你需要在你的用户表上有一个 uniq 索引才能“替换”工作。因此,您无需检查用户是否存在。而且你不需要用php解析CSV文件。

【讨论】:

【参考方案6】:

您似乎有可能(概率?)对每条记录进行 3 次查询。这 3 个查询将需要 3 次访问数据库(如果您使用 yii 将记录存储在 yii 对象中,那么这可能会进一步减慢速度)。

您能否在名字/姓氏/出生日期和电子邮件地址上添加唯一键?

如果是这样,您只需执行 INSERT....ON DUPLICATE KEY UPDATE。这会将其减少为对每条记录进行一次查询,从而大大加快处理速度。

但这种语法的一大优点是您可以一次插入/更新多条记录(我通常坚持大约 250 条),因此访问数据库的次数更少。

您可以敲出一个只将记录传递给的类,并在记录数达到您的选择时插入该类。还要添加调用以在析构函数中插入记录以插入任何最终记录。

另一种选择是将所有内容读入临时表,然后将其用作连接到用户表的源以进行更新/插入。这需要对索引进行一些努力,但是对临时表的批量加载很快,并且使用有用索引的更新会很快。使用它作为插入源也应该很快(如果排除已更新的记录)。

另一个问题似乎是您的以下查询,但不确定您在哪里执行此查询。它似乎只需要执行一次,在这种情况下它可能无关紧要。您还没有给出 CustomType 表的结构,但是它连接到 Customfield 并且字段 customTypeId 没有索引。因此,加入会很慢。同样,在基于 customFieldId 连接的 CustomValue 和 CustomFieldSubArea 连接上,并且在该字段上都没有索引(希望是唯一索引,就好像这些字段不是唯一的一样,您将获得很多记录返回 - 每个可能的组合都有 1 行)

SELECT cf.*, ctyp.typeName, cfv.id as customId, cfv.customFieldId, 
       cfv.relatedId, cfv.fieldValue, cfv.createdAt 
FROM `CustomField` `cf` 
    INNER JOIN CustomType ctyp on ctyp.id = cf.customTypeId 
    LEFT OUTER JOIN CustomValue cfv on cf.id = cfv.customFieldId 
                and relatedId = 0 
    LEFT JOIN CustomFieldSubArea cfsa on cfsa.customFieldId = cf.id 
WHERE ((relatedTable = 'people' and enabled = '1') 
  AND (onCreate = '1')) 
  AND (cfsa.subarea='peoplebulkinsert') 
ORDER BY cf.sortOrder, cf.label

【讨论】:

【参考方案7】:

你可以尝试减少查询,用sql在线编译器检查时间段然后包含在项目下。

【讨论】:

【参考方案8】:

始终在交易中进行批量导入

        $transaction = Yii::app()->db->beginTransaction();
        $curRow = 0;
        try
        
            while (($peopleData = fgetcsv($handle, 10240, ",")) !== FALSE) 
            $curRow++;
            //process $peopleData
            //insert row
            //best to use INSERT ... ON DUPLICATE  KEY UPDATE
            // a = 1
            // b = 2;
            if ($curRow % 5000 == 0) 
               $transaction->commit();
               $transaction->beginTransaction();
            
        
        catch (Exception $ex)
        
            $transaction->rollBack();
            $result = $e->getMessage();                    
        
        //don't forget the remainder.
        $transaction->commit();

我已经看到,通过简单地使用这种技术,导入例程的速度提高了 500%。我还看到了一个导入过程,它为 每个 行执行了 600 个查询(选择、插入、更新和显示表结构的混合)。这种技术将流程加快了 30%。

【讨论】:

以上是关于我centOS上的MySQL速度很慢的主要内容,如果未能解决你的问题,请参考以下文章

tomcat启动很慢很慢很慢

Linux Centos7 配置代理

Linux Centos7 配置代理

win10电脑打开此电脑资源管理器任务管理器软件等突然很慢很慢,cup内存磁盘利用率却很低

SAP migo这个事物代码在做操作的时候,多人操作同一个事务代码时很慢很慢,是啥原因?

公司网络很慢很卡的原因分析与处理