2013-05-25 88 views
12

tôi có 10 bảng có cùng cấu trúc ngoại trừ tên bảng.thủ tục lưu trữ mysql chậm hơn 20 lần so với truy vấn chuẩn

tôi có một sp (stored procedure) được xác định như sau:

select * from table1 where (@param1 IS NULL OR [email protected]) 
UNION ALL 
select * from table2 where (@param1 IS NULL OR [email protected]) 
UNION ALL 
... 
... 
UNION ALL 
select * from table10 where (@param1 IS NULL OR [email protected]) 

tôi kêu gọi các sp với dòng sau:

call mySP('test') //it executes in 6,836s 

Sau đó, tôi mở một cửa sổ truy vấn tiêu chuẩn mới. Tôi vừa sao chép truy vấn ở trên. Sau đó thay thế @ param1 bằng 'test'.

Điều này được thực hiện bằng 0,321 và nhanh hơn khoảng 20 lần so với quy trình được lưu trữ.

Tôi đã thay đổi giá trị tham số nhiều lần để ngăn kết quả được lưu vào bộ nhớ cache. Nhưng điều này không thay đổi kết quả. SP chậm hơn khoảng 20 lần so với truy vấn chuẩn tương đương.

Bạn có thể giúp tôi tìm hiểu lý do tại sao điều này xảy ra?

Có ai gặp phải sự cố tương tự không?

Tôi đang sử dụng mySQL 5.0.51 trên máy chủ Windows 2008 R2 64 bit.

chỉnh sửa: Tôi đang sử dụng Navicat để kiểm tra.

Bất kỳ ý tưởng nào cũng hữu ích cho tôi.

EDIT1:

Tôi vừa thực hiện một số kiểm tra theo câu trả lời của Barmar.

Tại cuối cùng tôi đã thay đổi các sp như dưới đây với một chỉ một dòng:

SELECT * FROM table1 WHERE [email protected] AND [email protected] 

Sau đó, trước tiên tôi thực hiện các truy vấn standart

SELECT * FROM table1 WHERE col1='test' AND col2='test' //Executed in 0.020s 

Sau khi tôi gọi là sp của tôi:

CALL MySp('test','test') //Executed in 0.466s 

Vì vậy, tôi đã thay đổi hoàn toàn mệnh đề where nhưng không có gì thay đổi. Và tôi gọi là sp từ cửa sổ lệnh mysql thay vì navicat. Nó cho kết quả tương tự. Tôi vẫn còn bị mắc kẹt trên đó.

sp DDL của tôi:

CREATE DEFINER = `myDbName`@`%` 
PROCEDURE `MySP` (param1 VARCHAR(100), param2 VARCHAR(100)) 
BEGIN 
    SELECT * FROM table1 WHERE col1=param1 AND col2=param2 
END 

Và col1 và col2 được kết hợp lập chỉ mục.

Bạn có thể nói rằng tại sao bạn không sử dụng truy vấn standart? Thiết kế phần mềm của tôi không thích hợp cho việc này. Tôi phải sử dụng thủ tục lưu trữ. Vì vậy, vấn đề này rất quan trọng đối với tôi.

EDIT2:

Tôi đã nhận được thông tin hồ sơ truy vấn. Sự khác biệt lớn là do "gửi hàng dữ liệu" trong Thông tin Tiểu sử SP. Gửi phần dữ liệu mất 99% thời gian thực hiện truy vấn. Tôi đang làm thử nghiệm trên máy chủ cơ sở dữ liệu địa phương. Tôi không kết nối từ máy tính từ xa.

SP Hồ sơ Informations SP Profile Information

Query Hồ sơ Informations enter image description here

Tôi đã cố gắng tuyên bố lực lượng chỉ số như dưới đây trong sp của tôi. Nhưng cùng một kết quả.

SELECT * FROM table1 FORCE INDEX (col1_col2_combined_index) WHERE [email protected] AND [email protected] 

Tôi đã thay đổi sp như dưới đây.

EXPLAIN SELECT * FROM table1 FORCE INDEX (col1_col2_combined_index) WHERE col1=param1 AND col2=param2 

này cho kết quả này:

id:1 
select_type=SIMPLE 
table:table1 
type=ref 
possible_keys:NULL 
key:NULL 
key_len:NULL 
ref:NULL 
rows:292004 
Extra:Using where 

Sau đó, tôi đã thực hiện các truy vấn dưới đây.

EXPLAIN SELECT * FROM table1 WHERE col1='test' AND col2='test' 

Kết quả là:

id:1 
select_type=SIMPLE 
table:table1 
type=ref 
possible_keys:col1_co2_combined_index 
key:col1_co2_combined_index 
key_len:76 
ref:const,const 
rows:292004 
Extra:Using where 

Tôi đang sử dụng tuyên bố FORCE INDEX trong SP. Nhưng nó nhấn mạnh vào việc không sử dụng chỉ mục. Bất kỳ ý tưởng? Tôi nghĩ rằng tôi gần kết thúc :)

+1

Có thể là sau khi thực hiện các SP, MySQL đã lưu trữ kết quả, và sau đó khi bạn thực hiện nó bên ngoài SP, nó chỉ đánh bộ nhớ cache hơn là thực hiện nó một lần nữa. –

+0

Nhân tiện, tại sao 10 bảng có cùng cấu trúc? Tại sao không kết hợp chúng thành 1 bảng? –

+0

thiết kế cơ sở dữ liệu là ra khỏi bàn tay của tôi tôi sẽ không bao giờ làm thiết kế như vậy :) đầu tiên tôi thực hiện các truy vấn với tham số khác nhau sau đó ngay lập tức tôi gọi sp với cùng một tham số. kết quả tương tự. có vẻ như sp thậm chí không lấy kết quả từ bộ nhớ cache. – bselvan

Trả lời

4

Có thể xảy ra sự cố tập hợp ký tự? Nếu bộ ký tự bảng của bạn khác với bộ ký tự cơ sở dữ liệu của bạn, điều này có thể gây ra sự cố.

Xem báo cáo lỗi này: http://bugs.mysql.com/bug.php?id=26224

[ngày 12 tháng 11 2007 21:32] Đánh Kubacki Vẫn không có may mắn với 5.1.22_rc - phím được ingored, truy vấn mất trong vòng một thủ tục 36 giây và ngoài 0.12s.

[12 Nov 2007 22:30] Mark Kubacki Sau khi thay đổi bộ ký tự thành UTF-8 (đặc biệt cho cả hai được sử dụng), được sử dụng cho kết nối anyways, các phím được đưa vào tài khoản trong quy trình được lưu trữ!

Câu hỏi tôi không thể trả lời là: Tại sao trình tối ưu hóa xử lý ký tự chuyển đổi theo cách khác trong và ngoài các thủ tục được lưu trữ? (Thật vậy, tôi có thể sai khi hỏi điều này.)

+0

cảm ơn bạn rất nhiều. Lý do chính xác là như nhau. Bảng nhân vật và bộ ký tự cơ sở dữ liệu của tôi khác nhau. Tôi đã thay đổi cả hai cùng một, sau đó chỉ số đã được sử dụng trong sp. – bselvan

8

Chỉ cần một đoán:

Khi bạn chạy các truy vấn bằng tay, khái niệm WHERE ('test' IS NULL or COL1 = 'test') có thể được tối ưu hóa khi truy vấn đang được phân tích cú pháp. Trình phân tích cú pháp có thể thấy rằng chuỗi 'test' không phải là rỗng, do đó, nó chuyển đổi thử nghiệm thành WHERE COL1 = 'test'. Và nếu có chỉ mục trên COL1 thì sẽ được sử dụng.

Tuy nhiên, khi bạn tạo thủ tục được lưu trữ, phân tích cú pháp xảy ra khi quy trình được tạo. Tại thời điểm đó, nó không biết @param sẽ là gì và phải triển khai truy vấn dưới dạng quét tuần tự của bảng.

Hãy thử thay đổi thủ tục của bạn để:

IF @param IS NULL 
THEN BEGIN 
    SELECT * FROM table1 
    UNION ALL 
    SELECT * FROM table2 
    ... 
END; 
ELSE BEGIN 
    SELECT * FROM table1 WHERE col1 = @param 
    UNION ALL 
    SELECT * FROM table2 WHERE col1 = @param 
    ... 
END; 
END IF; 

Tôi không có nhiều kinh nghiệm với các thủ tục MySQL lưu trữ, vì vậy tôi không chắc chắn đó là tất cả đúng cú pháp.

+0

Cảm ơn câu trả lời. Tôi sẽ thử. Trên thực tế, mệnh đề where của tôi không quá ngắn. (@ param1 là null hoặc col1 = @ param1) và (@ param2 là null hoặc col2 = @ param2) và .... đến param6. Để được rõ ràng, tôi đã không viết chi tiết. tôi nghĩ trong giải pháp này, giải pháp của bạn không bao gồm chính xác câu hỏi của tôi. – bselvan

+0

Sự khác biệt xuất phát từ việc sử dụng các biến là '@Var là null hoặc col1 = @ var'. Chạy GIẢI THÍCH trên cả hai: 'GIẢI THÍCH CHỌN * TỪ MY_VIEW' và' CHỌN * TỪ SP_MY_PROC'. Nó nên đưa ra lý do tại sao. Trong SQL Server có và tùy chọn RECOMPILE để sử dụng trong các trường hợp như vậy để biên dịch lại kế hoạch thực hiện. Tôi đang tìm kiếm nhưng không tìm kiếm một cái gì đó như thế trong MySQL. – Stoleg

+0

@Stoleg Đó là lời giải thích ban đầu của tôi, nhưng anh ấy đã gạt bỏ 'OR' trong phiên bản mới nhất của mình. Vì vậy, tôi không biết tại sao SP sẽ vẫn chậm. – Barmar

0

Câu hỏi thú vị, bởi vì tôi thích sử dụng các thủ tục được lưu trữ. Lý do là bảo trì và nguyên tắc đóng gói.

Đây là thông tin tôi tìm thấy: http://dev.mysql.com/doc/refman/5.1/en/query-cache-operation.html

Nó khẳng định rằng bộ nhớ cache truy vấn không được sử dụng cho các truy vấn mà 1. là một subquery đó thuộc về một truy vấn bên ngoài, và 2. được thực hiện trong cơ thể của một thủ tục, trình kích hoạt hoặc sự kiện được lưu trữ.

Điều này ngụ ý rằng nó hoạt động như được thiết kế.

0

Tôi đã thấy hành vi này, nhưng nó không liên quan đến bộ ký tự.

tôi đã có một bảng mà tổ chức tự tra cứu dữ liệu phân cấp (cha mẹ với con cái, và một số trẻ em có con của riêng mình, vv). Vì parent_id phải tham chiếu đến id chính (và cột đã chỉ định ràng buộc cho hiệu ứng đó), tôi không thể đặt id mẹ thành NULL hoặc 0 (zero) để hủy liên kết một đứa con khỏi cha/mẹ, vì vậy tôi chỉ đơn giản tham chiếu đến chinh no.

Khi tôi chạy một thủ tục được lưu trữ để thực hiện truy vấn đệ quy để tìm tất cả trẻ em (ở mọi cấp) của cha mẹ cụ thể, truy vấn mất từ ​​30 & 40 lần để chạy. Tôi thấy rằng thay đổi truy vấn được sử dụng bởi các thủ tục được lưu trữ để đảm bảo nó loại trừ bản ghi cha mẹ cấp cao nhất (bằng cách chỉ định WHERE parent_id! = Id) đã khôi phục hiệu suất của truy vấn.

Quy trình được lưu trữ tôi đang sử dụng dựa trên quy trình được hiển thị trong: https://stackoverflow.com/questions/27013093/recursive-query-emulation-in-mysql.

Các vấn đề liên quan