Làm thế nào tôi có thể tăng tốc độ cho thấy cột cột trên mạng MySQL?


7

Ứng dụng của tôi phụ thuộc vào việc chạy "hiển thị cột" cho các bảng nhất định. Phải mất khoảng 60ms để chạy, trong khi tất cả các truy vấn khác của chúng tôi mất dưới một ms. Truy vấn information_schematrực tiếp thậm chí còn chậm hơn.

Cơ sở dữ liệu chứa khoảng 250 cơ sở dữ liệu, với 100 đến 200 bảng cho mỗi cơ sở dữ liệu (tổng cộng khoảng 20 nghìn bảng).

  • Làm thế nào tôi có thể tìm ra lý do tại sao các hoạt động này rất chậm?
  • Có lẽ có một số cài đặt tôi có thể thay đổi để làm cho nó chạy nhanh hơn hoặc để lưu trữ bên SQL?

(Ứng dụng thực hiện khoảng 14 truy vấn như vậy trên mỗi lần tải trang - Tôi biết rõ rằng mã kế thừa này cần được dọn sạch, nhưng tìm kiếm các tùy chọn có thể trong khi tôi làm việc với bản sửa lỗi dài hạn.)


1
Không quan tâm, trong kịch bản nào 60ms sẽ quá chậm để kiểm tra các cột của bảng? Đó không phải là điều bạn nên làm theo mọi yêu cầu

1
Bạn có ý nghĩa gì cho thấy cột? Garbing tên cột từ một bảng hoặc in toàn bộ một cột? Nếu tên của nó .. tại sao bạn không lấy nó một lần và lưu trữ trong ứng dụng của mình ... nếu điều đó là không thể, tại sao bạn không tạo một bảng khác chứa tất cả các cột dựa trên bảng?

@Jaitsu: Không, đó không phải là việc chúng ta nên làm, nhưng, đó là như vậy. Mã di sản. Cho đến khi tôi có thời gian để dọn dẹp và thực hiện đúng cách, tôi muốn xem liệu tôi có thể tăng tốc nó không. Tôi đã có khoảng 14 trong số này chạy mọi tải trang.

@FlorinStingaciu: Có, tên cột. Đặt chúng vào một bảng khác có thể tăng tốc mọi thứ, nhưng nó sẽ không đồng bộ, điều này đánh bại toàn bộ mục đích hỏi trực tiếp bảng.

1
@Mat: Một ý kiến ​​không tồi. Bình chọn để di chuyển đến dba.

Câu trả lời:


12

MySQL tính toán lại số liệu thống kê bảng cho các hoạt động nhất định truy cập INFORMATION_SCHEMAcác bảng ( SHOW COLUMNSchỉ là bí danh thuận tiện cho truy vấn INFORMATION_SCHEMA.COLUMNS). Đặt innodb_stats_on_metadata thành false, điều này sẽ ngăn việc tính toán lại này xảy ra khi bạn yêu cầu siêu dữ liệu từ bảng.

SET GLOBAL innodb_stats_on_metadata=0;

và thêm vào như sau my.cnf

[mysqld]
innodb_stats_on_metadata = 0

Tôi nên đã đề cập rằng tôi thực sự sử dụng MyISAM. Đã thử thiết lập điều này dù sao, nhưng nó không mang lại bất kỳ lợi ích.
mở

Bạn đã xem ALTER TABLE foo Engine = InnoDB chưa? :) Có lý do chính đáng để sử dụng MyISAM không?
Aaron Brown

Lý do di sản chủ yếu, tôi nghĩ. Tôi sợ những gì có thể xảy ra nếu tôi thử điều đó; không chắc chắn tất cả các FK sẽ xếp hàng. Tôi sẽ cho nó một số suy nghĩ nhiều hơn mặc dù.
mở

@AaronBrown +1 cho câu trả lời này vì bất kỳ ai phải đối mặt với tình huống này với cơ sở dữ liệu toàn bộ InnoDB đều cần thông tin này.
RolandoMySQLDBA

1
1 cho đặt [mysqld]ở đó. Có thể rõ ràng với nhiều người rằng cài đặt này sẽ đi theo mysqld, nhưng có thể không rõ ràng đối với những người sẽ hỏi câu hỏi này. Nhân tiện, cái này tăng tốc SELECT COUNT(*)trên một trong những cái information_schemabàn của tôi đến 6 giây từ hơn một phút. Vẫn chậm, nhưng một cải tiến rất lớn.
Butussy Butkus

3

Tôi đề nghị bạn tạo một cơ sở dữ liệu có các INFORMATION_SCHEMAbảng (hoặc chỉ những bảng bạn cần) làm bản sao. Lập chỉ mục chúng một cách thích hợp và bạn sẽ đạt được hiệu suất.

Vấn đề đồng bộ hóa giữa cơ sở dữ liệu này và INFORMATION_SCHEMAmặc dù khó khăn.

Bạn có thể có một quy trình đồng bộ hóa các bảng này mỗi giờ hoặc cứ sau 5 phút (cấu trúc của các bảng có thường xuyên thay đổi không?).

Một ý tưởng sẽ được sử dụng MySQL Proxy để đón bất kỳ ALTER TABLEbáo cáo (và CREATEDROPCREATE INDEXvà bất cứ báo cáo khác sửa đổi thông tin mà bạn cần) và sau đó đồng bộ hóa các giản đồ thông tin nhân rộng sau khi các báo cáo thành công.


Nếu bạn chỉ cần tên cột và không có bất kỳ thông tin nào khác, như kiểu dữ liệu, độ dài hoặc chỉ mục có sẵn, có lẽ bạn có thể thay thế việc sử dụng các SHOW COLUMNStruy vấn (nhanh) chỉ trả về 1 hàng, bằng LIMIT 1hoặc không có gì, bằng LIMIT 0hoặc:

SELECT * FROM TableName WHERE FALSE ;

Mặc dù lời khuyên chung chống lại việc sử dụng SELECT *, đây có thể là một trường hợp hợp pháp trong đó không có gì khác hữu ích. (mọi thứ khác nhưng *có thể dẫn đến lỗi!)


2

Trong trường hợp cụ thể này, tôi nghĩ rằng đó INFORMATION_SCHEMAlà một cá trích đỏ. Từ các thử nghiệm SHOW COLUMNShiệu năng của riêng tôi , innodb_stats_on_metadatabiến này dường như không tạo ra bất kỳ sự khác biệt nào trên các bảng MyISAM hoặc InnoDB.

Tuy nhiên, từ hướng dẫn sử dụng MySQL 5.0 ...

Một số điều kiện ngăn việc sử dụng bảng tạm thời trong bộ nhớ, trong trường hợp đó, máy chủ sử dụng bảng trên đĩa thay thế:

[...]

  • Các câu lệnh SHOW COLUMNSvà Các DESCRIBEcâu lệnh sử dụng BLOBlàm kiểu cho một số cột, do đó bảng tạm thời được sử dụng cho các kết quả là một bảng trên đĩa.

Điều này dường như đã bị xóa khỏi hướng dẫn sử dụng từ MySQL 5.5, nhưng vẫn xuất hiện để áp dụng trong phiên bản đó ...

mysql> SHOW VARIABLES LIKE 'version';
+---------------+-------------------------+
| Variable_name | Value                   |
+---------------+-------------------------+
| version       | 5.5.41-0ubuntu0.14.04.1 |
+---------------+-------------------------+
1 row in set (0.00 sec)

mysql> SHOW STATUS LIKE 'Created_tmp_disk_tables';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 0     |
+-------------------------+-------+
1 row in set (0.00 sec)

mysql> SHOW COLUMNS FROM mysql.user;
[...snip...]
42 rows in set (0.00 sec)

mysql> SHOW STATUS LIKE 'Created_tmp_disk_tables';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 1     |
+-------------------------+-------+
1 row in set (0.00 sec)

Thông tin trường được trả về với tập kết quả truy vấn chứa cùng thông tin được trả về SHOW COLUMNS, do đó, SELECT * FROM my_table LIMIT 0cần đạt được điều tương tự mà không cần tạo bảng tạm thời trên đĩa cho mỗi truy vấn.

Một ví dụ nhanh để lấy tên trường trong PHP ...

$mysql = new mysqli('localhost', 'root', '', 'my_database');
$field_names = array();
$result = $mysql->query("SELECT * FROM my_table LIMIT 0");
$fields = $result->fetch_fields();
foreach ($fields as $fields)
{
    $field_names[] = $field->name;
}
var_dump($field_names);

Lấy thông tin trường theo cách này là một chút khó khăn hơn để giải mã. Bạn sẽ phải tham khảo mô tả MYSQL_FIELDcấu trúc cơ bản để lấy ra các kiểu dữ liệu và cờ, nhưng nó chạy nhanh hơn khoảng 7 lần trên hệ thống của tôi.


1

Tôi thích đề xuất đầu tiên trong câu trả lời của @ yerpcube (+1), nhưng tôi muốn đề xuất một cái gì đó

  • tạo một thể hiện cơ sở dữ liệu khác trên cổng 3307
  • mysqldump cơ sở dữ liệu sản xuất thành tệp văn bản SQL bằng các tùy chọn sau:
    • --no-data
    • --routines
    • --triggers
    • --all-databaseshoặc --databasestheo sau là danh sách các cơ sở dữ liệu bạn muốn
  • Tải tệp Văn bản SQL vào cổng 3307 Trường hợp MySQL

Vì vậy, bạn mysqldump sẽ trông như sau:

mysqldump --no-data --routines --triggers --all-databases > ImportFile.sql

Đó là nó. Trong tương lai, tất cả những gì bạn cần làm là kết nối với thể hiện cơ sở dữ liệu cổng 3307 này và chạy bất kỳ truy vấn nào liên quan đến lược đồ đến nội dung trái tim của bạn. Nếu bạn biết bất kỳ bảng nào trong cơ sở dữ liệu sản xuất thay đổi, chỉ cần mysqldump lược đồ từ sản xuất và tải lại nó vào ví dụ cổng 3307.

CẢNH BÁO: Nếu bạn cài đặt một phiên bản mysql trên cùng một máy như sản xuất, hãy đảm bảo chắc chắn rằng bạn kết nối với phiên bản đó bằng cách sử dụng

mysql -u... -p... -h127.0.0.1 -P3307 < ImportFile.sql

Nếu bạn thực thi

mysql -u... -p... -P3307 < ImportFile.sql

Nó sẽ sản xuất vòi. Vì vậy, HÃY CẨN THẬN !!!!

Một cách khác là chỉ sử dụng một máy chủ DB riêng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.