2010-10-01 35 views
7

Tôi có một bảng kho trông như thế này:thêm cột di tích MySQL hiệu suất

CREATE TABLE Warehouse (
    id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, 
    eventId BIGINT(20) UNSIGNED NOT NULL, 
    groupId BIGINT(20) NOT NULL, 
    activityId BIGINT(20) UNSIGNED NOT NULL, 
    ... many more ids, 
    "txtProperty1" VARCHAR(255), 
    "txtProperty2" VARCHAR(255), 
    "txtProperty3" VARCHAR(255), 
    "txtProperty4" VARCHAR(255), 
    "txtProperty5" VARCHAR(255), 
    ... many more of these 
    PRIMARY KEY ("id") 
    KEY "WInvestmentDetail_idx01" ("groupId"), 
    ... several more indices 
) ENGINE=INNODB; 

Bây giờ, các truy vấn sau đây dành khoảng 0.8s trong truy vấn thời gian và 0.2s trong lấy thời gian, đối với một tổng cộng khoảng một giây. Truy vấn trả về ~ 67.000 hàng.

SELECT eventId 
FROM Warehouse 
WHERE accountId IN (10, 8, 13, 9, 7, 6, 12, 11) 
    AND scenarioId IS NULL 
    AND insertDate BETWEEN DATE '2002-01-01' AND DATE '2011-12-31' 
ORDER BY insertDate; 

Việc thêm nhiều id vào mệnh đề chọn không thực sự thay đổi hiệu suất.

SELECT eventId, groupId, activityId, insertDate 
FROM Warehouse 
WHERE accountId IN (10, 8, 13, 9, 7, 6, 12, 11) 
    AND scenarioId IS NULL 
    AND insertDate BETWEEN DATE '2002-01-01' AND DATE '2011-12-31' 
ORDER BY insertDate; 

Tuy nhiên, thêm cột "thuộc tính" sẽ thay đổi thành 0,6 giây tìm nạp và thời gian truy vấn 1,8 giây.

SELECT eventId, txtProperty1 
FROM Warehouse 
WHERE accountId IN (10, 8, 13, 9, 7, 6, 12, 11) 
    AND scenarioId IS NULL 
    AND insertDate BETWEEN DATE '2002-01-01' AND DATE '2011-12-31' 
ORDER BY insertDate; 

Bây giờ để thực sự thổi tất ngắn của bạn. Thay vì txtProperty1, sử dụng txtProperty2 thay đổi số lần tìm nạp 0,8, truy vấn 24 giây!

SELECT eventId, txtProperty2 
FROM Warehouse 
WHERE accountId IN (10, 8, 13, 9, 7, 6, 12, 11) 
    AND scenarioId IS NULL 
    AND insertDate BETWEEN DATE '2002-01-01' AND DATE '2011-12-31' 
ORDER BY insertDate; 

Hai cột này giống hệt nhau về loại dữ liệu mà chúng giữ: hầu như không rỗng và không được lập chỉ mục (không phải là tạo nên sự khác biệt). Để chắc chắn bản thân bảng là lành mạnh, tôi đã chạy phân tích/tối ưu hóa nó.

Điều này thực sự gây khó chịu cho tôi. Tôi có thể thấy lý do tại sao việc thêm các cột vào mệnh đề select chỉ có thể tăng thời gian tìm nạp, nhưng nó không nên thay đổi thời gian truy vấn, đặc biệt là không đáng kể. Tôi sẽ đánh giá cao bất kỳ ý tưởng nào về những gì đang gây ra sự chậm lại này.

CHỈNH SỬA - Các điểm dữ liệu khác

SELECT * thực sự vượt trội hơn txtProperty2 - 0.8 truy vấn, 8.4s tìm nạp. Quá tệ Tôi không thể sử dụng vì thời gian tìm nạp (dự kiến) quá dài.

+0

Thời gian này có thể lặp lại như thế nào? –

+2

và không được lập chỉ mục (không phải là tạo nên sự khác biệt) .... nó có thể (http://en.wikipedia.org/wiki/Index_%28database%29#Covering_Index) –

+0

@Rowland - Có, tôi có lặp lại timings trên hai máy tính xách tay dev và một máy chủ QA. Rõ ràng, máy chủ là nhanh hơn, nhưng mô hình vẫn còn. –

Trả lời

0

Tôi sẽ thừa nhận rằng đây là một chút đoán, nhưng tôi sẽ cung cấp cho nó một shot.

Bạn có id - trường đầu tiên - làm khóa chính. Tôi không chắc chắn 100% cách MySQL lập chỉ mục clustered như xa như tra cứu, nhưng nó là hợp lý để nghi ngờ rằng, đối với bất kỳ ID nào, có một số "con trỏ" để ghi lại với ID đó.

Thật dễ dàng tìm thấy sự khởi đầu của các trường khi tất cả các trường trước có độ rộng cố định. Tất cả các trường BIGINT(20) của bạn đều có kích thước xác định, giúp cho công cụ db dễ dàng tìm thấy trường được đưa ra một con trỏ tới đầu bản ghi; đó là một phép tính đơn giản. Tương tự như vậy, bắt đầu của trường VARCHAR(255) đầu tiên dễ tìm. Sau đó, mặc dù, vì các trường là các trường VARCHAR, công cụ db phải tính đến dữ liệu để tìm sự bắt đầu của trường tiếp theo, chậm hơn nhiều so với việc tính toán đơn giản là trường đó nên ở đâu. Vì vậy, đối với bất kỳ trường nào sau txtProperty1, bạn sẽ gặp phải vấn đề này.

Điều gì sẽ xảy ra nếu bạn thay đổi tất cả các trường VARCHAR(255) thành CHAR(255) trường? Rất có thể truy vấn của bạn sẽ nhanh hơn nhiều, mặc dù với chi phí sử dụng dung lượng lưu trữ tối đa cho mỗi trường CHAR(255) bất kể dữ liệu nào thực sự chứa.

+0

Thay đổi 5 thuộc tính đầu tiên thành CHAR (255) thực sự làm cho truy vấn chạy trong truy vấn 98s, tìm nạp 1.5. Tuy nhiên, thử nghiệm trong lĩnh vực này đã dẫn tôi đến một khám phá lạ khác: chọn txtProperty8 bị phạt tương tự như txtProperty1 (chỉ 2 giây) txtProperty7 là một nơi nào đó ở giữa (khoảng 5 giây) .Điều này hoàn toàn rất lạ lùng –

0

Không gian bảng phân mảnh?Hãy thử một null thay đổi bảng:

ALTER TABLE tbl_name ENGINE=INNODB 
+0

Xin lỗi .. nó không hoạt động. Điều này có thực hiện được điều tương tự như tối ưu hóa trong InnoDB không? –

+0

Tuy nhiên, tôi nghĩ rằng có thể có một số lỗi không gian bảng để giải thích sự khác biệt đáng kể giữa các cột gần như giống hệt nhau. – igelkott

1

Các MySQL documentation cho động cơ InnoDB gợi ý rằng nếu dữ liệu varchar của bạn không phù hợp trên trang (tức là nút của cấu trúc b-tree), sau đó các thông tin sẽ được tham chiếu trên các trang tràn. Vì vậy, trên bảng Warehouse rộng của bạn, có thể là txtProperty1 nằm trên trang và txtProperty2 là trang ngoài, do đó yêu cầu bổ sung I/O để truy xuất.

Không quá chắc chắn là tại sao số SELECT * thì tốt hơn; nó có thể tận dụng lợi thế của việc đọc dữ liệu tuần tự, thay vì chọn theo cách của nó xung quanh đĩa.

+0

Kịch bản này hoàn toàn có thể cho n dữ liệu của tôi. Tôi là một chút ngạc nhiên lúc 2s -> 24s tăng trong thời gian thu hồi, mặc dù. Bất kỳ ý tưởng về cách tôi có thể cải thiện thời gian truy vấn? –

+0

Tôi không có bất kỳ trải nghiệm thực tế nào về điều này: dường như có 2 cách tiềm năng để nhận được nhiều dữ liệu hơn trên trang. a) Bạn có thể thử làm cho trang rộng hơn: bằng cách đặt KEY_BLOCK_SIZE hoặc b) bạn có bất kỳ sự linh hoạt nào về kích thước datatype hay không, ví dụ: Bạn có cần các số để được BIGINT (sẽ là một unsigned INT hoặc MEDIUMINT làm gì?), và/hoặc có thể VARCHARs chỉ là 100 chiều dài? – richaux

+0

Có vẻ như 'SHOW TABLE STATUS' sẽ hiển thị cho bạn' KEY_BLOCK_SIZE' hiện tại. Giá trị đó là gì? Và những gì để các kích thước cột lên đến txtProperty1 thêm đến? –

0

Vì tôi là người dùng SQL Server chứ không phải là một anh chàng MySQL, đây là một cảnh quay dài. Trong SQL Server chỉ mục được nhóm là bảng. Tất cả dữ liệu bảng được lưu trữ trong chỉ mục nhóm. Các chỉ mục bổ sung lưu trữ các bản sao dự phòng của dữ liệu được lập chỉ mục được sắp xếp theo thứ tự sắp xếp thích hợp.

Lý do của tôi là như vậy. Khi bạn thêm ngày càng nhiều dữ liệu vào truy vấn, thời gian tìm nạp vẫn không đáng kể. Tôi đoán điều này là vì bạn đang tìm nạp tất cả dữ liệu từ chỉ mục được nhóm trong giai đoạn truy vấn và không có gì có hiệu quả trong suốt giai đoạn tìm nạp.

Lý do SELECT * hoạt động theo cách của nó vì bảng của bạn quá rộng. Miễn là bạn chỉ yêu cầu khóa và một hoặc hai cột bổ sung, tốt nhất là chỉ cần có mọi thứ trong truy vấn. Một khi bạn yêu cầu tất cả mọi thứ, nó trở nên rẻ hơn để tách biệt việc tìm nạp giữa hai giai đoạn. Tôi đoán rằng nếu bạn thêm cột vào truy vấn của mình một lần, bạn sẽ khám phá ra ranh giới nơi trình phân tích truy vấn chuyển từ thực hiện tất cả tìm nạp trong giai đoạn truy vấn để thực hiện hầu hết các lần tìm nạp trong giai đoạn tìm nạp.

+0

Điều này nghe giống như kỹ thuật "bao gồm chỉ số" được đề cập bởi một vài người khác. Nó vẫn là trường hợp nếu không txtProperty1 cũng không txtProperty2 là một phần của bất kỳ chỉ mục? –

0

Bạn nên đăng kế hoạch giải thích của hai truy vấn để chúng tôi có thể xem chúng là gì.

Đoán của tôi là tốc độ nhanh nhất là sử dụng "Chỉ mục bao" và không có chỉ số chậm. Điều này có nghĩa là người chậm phải thực hiện 67.000 tra cứu khóa chính, điều này sẽ rất kém hiệu quả nếu bảng không phải là tất cả trong bộ nhớ (thường yêu cầu 67k hoạt động IO nếu bảng tùy ý lớn và mỗi hàng trong trang riêng của nó).).

Trong MySQL, EXPLAIN sẽ hiển thị "Sử dụng chỉ mục" nếu chỉ mục bao gồm đang được sử dụng.

+0

Giải thích giống hệt nhau trong cả hai trường hợp. Mặc dù các mục trong mệnh đề where được lập chỉ mục, MySQL đang quyết định trong cả hai trường hợp để thực hiện quét toàn bộ bảng (có thể vì 67000 đại diện cho một phần đáng kể kích thước của toàn bộ bảng). Trong hai truy vấn cuối cùng tôi đã đề cập, không thể sử dụng kỹ thuật "bao gồm chỉ mục" vì cả hai đều chứa các cột không được lập chỉ mục. –

0

Tôi Đã xảy ra sự cố tương tự và tạo thêm chỉ mục có kích thước phù hợp đã giúp đáng kể. Điều gì cũng giúp là sử dụng các bảng cơ sở dữ liệu phân vùng và điều chỉnh ram cơ sở dữ liệu.

ví dụ: thêm một chỉ số để bàn cho (EventId, txtProperty2)

Lưu ý: Tôi nhận thấy rằng bạn đã nêu "Kho". Hãy ghi nhớ rằng nó có phần dự kiến ​​rằng nếu bạn có một bảng cơ sở dữ liệu khổng lồ bạn đang làm việc với sự chậm trễ bổ sung được mong đợi với mỗi điều kiện tăng lên.