簡體   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