在索引列上缓慢加入和分组

时间:2014-09-27 23:00:51

标签: mysql sql performance sql-optimization

以下查询需要15-20秒才能运行

SELECT "Trade_US"."ID" AS "Trade_US.ID",
"Server_US"."Name" AS "Server_US.Name",
"Trade_US"."TradeTypeID" AS "Trade_US.TradeTypeID",
"Users"."ID" AS "Users.ID",
"Users"."NickName" AS "Users.NickName",
"Item"."ID" AS "Item.ID",
"Item"."ItemTypeID" AS "Item.ItemTypeID",
"Item"."ItemQualityID" AS "Item.ItemQualityID",
"Item"."Level" AS "Item.Level",
"Item"."IconURL" AS "Item.IconURL",
"Trade_US"."Amount" AS "Trade_US.Amount",
"Trade_US"."BonusPercent" AS "Trade_US.BonusPercent",
"Trade_US"."StartBidTotalSilver" AS "Trade_US.StartBidTotalSilver",
"Trade_US"."BuyOutTotalSilver" AS "Trade_US.BuyOutTotalSilver",
"Trade_US"."Description" AS "Trade_US.Description",
"Trade_US"."LastUpdate" AS "Trade_US.LastUpdate",
"Trade_US"."LastBump" AS "Trade_US.LastBump",
CONCAT(COALESCE("ItemPrefix_Text_EN"."PrefixName",""),"Item_Text_EN"."ItemName") AS "FullItemName",
MAX("TradeOffer_US"."PriceTotalSilver") AS "BestOfferTotalSilver" 
FROM "Trade_US" 
  Inner JOIN "Users" ON (("Trade_US"."TraderUserID" = "Users"."ID"))
  Inner JOIN "Item" ON (("Trade_US"."ItemID" = "Item"."ID"))
  Inner JOIN "Item_Text_EN" ON (("Item"."ID" = "Item_Text_EN"."ItemID"))
  Inner JOIN "Server_US" ON (("Server_US"."ID" = "Trade_US"."GameServerID"))
  Left JOIN "ItemPrefix_Text_EN" ON (("ItemPrefix_Text_EN"."ItemPrefixID" = "Trade_US"."ItemPrefixID"))
  Left JOIN "TradeReply_US" ON (("TradeReply_US"."TradeID" = "Trade_US"."ID"))
  Left JOIN "TradeOffer_US" ON (("TradeOffer_US"."ReplyID" = "TradeReply_US"."ID"))
 GROUP BY "Trade_US"."ID"
 ORDER BY "Trade_US"."LastBump" desc LIMIT 300000, 10

解释给出以下结果
enter image description here

以下是表格定义

CREATE TABLE Users(
    ID int primary key,

    NickName nvarchar(50) unique not null
);/*2 rows*/

CREATE TABLE ItemType(
    ID INT primary key,
    Name varchar(50) not null
);/*80 rows*/

CREATE TABLE ItemQuality(
    ID INT primary key,
    Name varchar(50) not null
);/*6 rows*/

CREATE TABLE Item(
    ID INT   primary key AUTO_INCREMENT,
    ItemTypeID int not null,
    `Level` int not null,
    ItemQualityID int not null,
    IconURL varchar(100) not null,

    FOREIGN KEY (ItemTypeID) REFERENCES ItemType(ID),
    FOREIGN KEY (ItemQualityID) REFERENCES ItemQuality(ID)
);/*5k rows*/
CREATE INDEX Level ON Item(Level);

CREATE TABLE Item_Text_EN(
    ItemID int primary key,
    ItemName varchar(50) not null,

    FOREIGN KEY (ItemID) REFERENCES Item (ID)
);/*5k rows*/
CREATE INDEX ItemName ON Item_Text_EN(ItemName);

CREATE TABLE ItemPrefix(
    ID INT PRIMARY KEY AUTO_INCREMENT,
    PrefixTypeID INT not null,

    FOREIGN KEY (PrefixTypeID) REFERENCES ItemPrefixType(ID)
);/*100 rows*/

CREATE TABLE ItemPrefix_Text_EN(
    ItemPrefixID INT PRIMARY KEY,
    PrefixName nvarchar(50) not null,

    FOREIGN KEY (ItemPrefixID) REFERENCES ItemPrefix(ID)
);/*100 rows*/

CREATE TABLE Trade_US(
    ID INT primary key AUTO_INCREMENT,
    GameServerID int not null,
    TradeTypeID int not null,
    TraderUserID int not null,
    ItemID int not null,
    Amount int not null,
    BonusPercent int null,
    ItemPrefixID INT null,
    StartBidTotalSilver int null,
    BuyOutTotalSilver int not null,

    `Description` nvarchar(200) null,
    LastUpdate datetime not null,
    LastBump datetime not null,

    FOREIGN KEY (GameServerID) REFERENCES Server_US(ID),
    FOREIGN KEY (TradeTypeID) REFERENCES TradeType(ID),
    FOREIGN KEY (TraderUserID) REFERENCES Users(ID),
    FOREIGN KEY (ItemPrefixID) REFERENCES ItemPrefix(ID),
    FOREIGN KEY (ItemID) REFERENCES Item(ID),

    CHECK (BonusPercent >=0 ),
    CHECK (Amount > 0 AND Amount < 99999),
    CHECK (StartBidTotalSilver < 100000000 AND StartBidTotalSilver >= 0),
    CHECK (BuyOutTotalSilver < 100000000 AND BuyOutTotalSilver >= 0)
);/*1million duplicated rows*/
CREATE INDEX BonusPercent ON Trade_US (BonusPercent);
CREATE INDEX StartBidTotalSilver ON Trade_US (StartBidTotalSilver);
CREATE INDEX BuyOutTotalSilver ON Trade_US (BuyOutTotalSilver);
CREATE INDEX LastBump ON Trade_US (LastBump);

CREATE TABLE TradeReply_US(
    ID INT primary key NOT NULL AUTO_INCREMENT,
    TradeID int not null,
    ReplyUserID int not null,
    `Message` nvarchar(200) null,
    `Time` datetime not null,

    FOREIGN KEY (ReplyUserID) REFERENCES Users(ID),
    FOREIGN KEY (TradeID) REFERENCES Trade_US (ID) on delete cascade
);/*5 rows*/

CREATE TABLE TradeOffer_US(
    ReplyID int primary key,
    PriceTotalSilver int not null,

    FOREIGN KEY (ReplyID) REFERENCES TradeReply_US (ID) on delete cascade,
    CHECK (PriceTotalSilver < 100000000 AND PriceTotalSilver >= 0)
);/*2 rows*/

CREATE TABLE Server_US(
    ID INT primary key,
    Name varchar(20) not null
);/*3 rows*/

分析结果:

17 Init 61 µs 
18 Optimizing 14 µs 
19 Statistics 158 µs 
20 Preparing 41 µs 
21 Creating Tmp Table 165 µs 
22 Executing 2 µs 
23 Copying To Tmp Table 14.2 s 
24 Sorting Result 972.5 ms 
25 Sending Data 1.1 s 
26 End 5 µs 

*任何名为ID的列都是主键
*数据库客户端版本:libmysql - mysqlnd 5.0.11-dev - 20120503
*现在它没有where子句,但是用户可能会选择进行搜索,这会导致where子句中包含索引列的任意组合。

我试图删除所有group by子句+ max(...),运行大约需要8秒。我使用了LastBump列上的强制索引,并且考虑到所有列都已编入索引并且只有1m行数据,它将花费大约6秒来运行,这是不合理的长时间。我尝试在Trade_US中的所有列上添加compsite索引,这些列将用于加入,它根本没有帮助。我擦除整个数据库,然后重新创建所有表,看看它是否是高索引碎片的问题,但仍然没有运气。完全没有想法..感谢任何帮助。

0 个答案:

没有答案
相关问题