繁体   English   中英

简单的选择查询在C#应用程序的MySQL数据库的非常大的表中花费更多时间

[英]Simple select query takes more time in very large table in MySQL database in C# application

我在带有C#Web应用程序的ASP.NET中使用MySQL数据库。 MySQL服务器版本为5.7,PC中有8 GB RAM。 当我在MySQL数据库表中执行选择查询时,它需要花费更多的时间来执行。 一个简单的选择查询大约需要42秒 跨表的1千万记录(1000万记录)。 我还为该表建立了索引。 我怎样才能解决这个问题?

以下是我的表结构。

CREATE TABLE `smstable_read` (
    `MessageID` int(11) NOT NULL AUTO_INCREMENT,
    `ApplicationID` int(11) DEFAULT NULL,
    `Api_userid` int(11) DEFAULT NULL,
    `ReturnMessageID` varchar(255) DEFAULT NULL,
    `Sequence_Id` int(11) DEFAULT NULL,
    `messagetext` longtext,
    `adtextid` int(11) DEFAULT NULL,
    `mobileno` varchar(255) DEFAULT NULL,
    `deliverystatus` int(11) DEFAULT NULL,
    `SMSlength` int(11) DEFAULT NULL,
    `DOC` varchar(255) DEFAULT NULL,
    `DOM` varchar(255) DEFAULT NULL,
    `BatchID` int(11) DEFAULT NULL,
    `StudentID` int(11) DEFAULT NULL,
    `SMSSentTime` varchar(255) DEFAULT NULL,
    `SMSDeliveredTime` varchar(255) DEFAULT NULL,
    `SMSDeliveredTimeTicks` decimal(28,0) DEFAULT '0',
    `SMSSentTimeTicks` decimal(28,0) DEFAULT '0',
    `Sent_SMS_Day` int(11) DEFAULT NULL,
    `Sent_SMS_Month` int(11) DEFAULT NULL,
    `Sent_SMS_Year` int(11) DEFAULT NULL,
    `smssent` int(11) DEFAULT '1',
    `Batch_Name` varchar(255) DEFAULT NULL,
    `User_ID` varchar(255) DEFAULT NULL,
    `Year_ID` int(11) DEFAULT NULL,
    `Date_Time` varchar(255) DEFAULT NULL,
    `IsGroup` double DEFAULT NULL,
    `Date_Time_Ticks` decimal(28,0) DEFAULT NULL,
    `IsNotificationSent` int(11) DEFAULT NULL,
    `Module_Id` double DEFAULT NULL,
    `Doc_Batch` decimal(28,0) DEFAULT NULL,
    `SMS_Category_ID` int(11) DEFAULT NULL,
    `SID` int(11) DEFAULT NULL,
    PRIMARY KEY (`MessageID`),
    KEY `index2` (`ReturnMessageID`),
    KEY `index3` (`mobileno`),
    KEY `BatchID` (`BatchID`),
    KEY `smssent` (`smssent`),
    KEY `deliverystatus` (`deliverystatus`),
    KEY `day` (`Sent_SMS_Day`),
    KEY `month` (`Sent_SMS_Month`),
    KEY `year` (`Sent_SMS_Year`),
    KEY `index4` (`ApplicationID`,`SMSSentTimeTicks`),
    KEY `smslength` (`SMSlength`),
    KEY `studid` (`StudentID`),
    KEY `batchid_studid` (`BatchID`,`StudentID`),
    KEY `User_ID` (`User_ID`),
    KEY `Year_Id` (`Year_ID`),
    KEY `IsNotificationSent` (`IsNotificationSent`),
    KEY `isgroup` (`IsGroup`),
    KEY `SID` (`SID`),
    KEY `SMS_Category_ID` (`SMS_Category_ID`),
    KEY `SMSSentTimeTicks` (`SMSSentTimeTicks`)
) ENGINE=MyISAM AUTO_INCREMENT=16513292 DEFAULT CHARSET=utf8;

以下是我的选择查询:

SELECT messagetext, SMSSentTime, StudentID, batchid,
User_ID,MessageID,Sent_SMS_Day, Sent_SMS_Month,
Sent_SMS_Year,Module_Id,Year_ID,Doc_Batch
FROM smstable_read
WHERE StudentID=977 AND SID = 8582 AND MessageID>16013282

您需要了解复合索引和覆盖索引。 了解这些东西。

您的查询很慢,因为它正在对表进行半扫描。 它使用主键查找具有合格MessageID的第一行,然后查看表的每一行以查找匹配的行。

您的过滤条件是StudentID = constantSID = constantMessageID > constant 这意味着您需要在索引中按顺序排列这三列。 前两个过滤条件将随机访问索引到正确的位置。 第三个条件将在查询中的常量值之后立即开始扫描索引。 这称为索引范围扫描操作,并且非常有效。

ALTER TABLE smstable_read
  ADD INDEX StudentSidMessage (StudentId, SID, MessageId);

该复合索引应使您的查询高效。 请注意,在MyISAM中,表的主键列应出现在复合索引中。 在这种情况下,这很酷,因为它也是查询条件的一部分。

如果此查询的使用频率很高,则可以创建覆盖索引:您可以将查询的其他列(在SELECT子句中提到的列)添加到索引中。

但是,不幸的是,您已经使用长longtext数据类型定义了messageText列。 这样一来,每封邮件最多可包含4 GB。 (为什么?这真的是SMS数据吗?SMS中的每条消息限制为160个字节。四个千兆字节>> 160个字节。)

现在,覆盖索引的重点是允许完全从索引中满足查询,而无需返回表。 但是,当您在索引中包含长longtext或任何其他LOB列时,它仅包含数据的子集。 因此覆盖指数的点丢失了。

如果您是我,我将更改表,使messageTextVARCHAR(255)数据类型,然后创建此覆盖索引:

ALTER TABLE smstable_read
  ADD INDEX StudentSidMessage (StudentId, SID, MessageId,
            SMSSentTime, batchid,
            User_ID, Sent_SMS_Day, Sent_SMS_Month,
            Sent_SMS_Year,Module_Id,Year_ID,Doc_Batch,
            messageText);

(请注意,如果可以,应将变长项目放在索引的最后。)

如果您不能将应用程序更改为处理VARCHAR(255)请使用我提到的第一个索引。

专家提示: 在MySQL表上放置许多单列索引很少会提高SELECT性能,并且始终会损害INSERT和UPDATE性能。 您需要在主键上建立索引,并且还需要索引来支持您运行的查询。 多余的索引是有害的。

看来您的数据库没有正确索引,甚至没有正确规范化。 规范化数据库将大大加快所有查询的速度。 特别是考虑到MySQL在查询中每个表仅使用一个索引这一事实。 即使您有很多索引,也无法使用它们。

您当前的查询会根据StudentIDSIDMessageID过滤。 最后一个是不平等比较,因此索引将不会非常有效,但是其他两列是相等比较。 我建议这样的索引:

KEY `studid` (`StudentID`,`SID`)

接下来,删除SID上的现有索引。 如果发现您不想因为在另一个查询中使用它而将其删除,请进一步证明您的表迫切需要规范化。

太多的索引会减慢插入速度,并给每个SELECT添加一点点开销,因为查询计划者需要更多的精力来确定要使用哪个索引。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM