Hiệu suất MySQL COUNT (*)


7

Tôi có một cái bàn với hơn 15m hàng. Tôi cần tổng số hàng. Vì thế:

SELECT COUNT(*) FROM thetable;

Mất khoảng 50 giây để hoàn thành. Giải thích cho tôi Select tables optimized away. Tôi cho rằng điều này có nghĩa là kết quả chỉ có thể được tìm thấy bằng cách sử dụng một chỉ mục, vậy tại sao nó vẫn mất nhiều thời gian như vậy? Dưới đây là một số thông tin về chỉ mục trên idcột (Không thể rỗng):

Loại chỉ mục: BTREE (cụm)

Cardinality: 14623100

Duy nhất: CÓ

Làm cách nào tôi có thể cải thiện hiệu suất của truy vấn này? Cảm ơn.

Lưu ý: Cơ sở dữ liệu là MySQL 5.7.1 và sử dụng công cụ InnoDB.

BIÊN TẬP:

Tạo tuyên bố:

CREATE TABLE `properties` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `address` varchar(255) DEFAULT NULL,
  `locality` varchar(50) DEFAULT NULL,
  `latitude` decimal(13,9) DEFAULT NULL,
  `longitude` decimal(13,9) DEFAULT NULL,
  `state` varchar(10) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  .....
  PRIMARY KEY (`id`),
  KEY `index_properties_on_address` (`address`),
  KEY `index_properties_on_latitude` (`latitude`),
  KEY `index_properties_on_longitude` (`longitude`),
  KEY `index_properties_on_state` (`state`),
  KEY `index_properties_on_created_at` (`created_at`),
  .....
) ENGINE=InnoDB AUTO_INCREMENT=28267712 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPRESSED;

Lưu ý: Tôi đã bỏ qua một số dòng, có 44 cột.

Giải thích kế hoạch:

+ ---- + ------------- + ------- + ------------ + ------ + - ------------- + ------ + --------- + ------ + ------ + ----- ----- + ------------------------------ +
| id | chọn_type | bàn | phân vùng | loại | có thể_key | chìa khóa | key_len | tham khảo | hàng | lọc | Thêm |
+ ---- + ------------- + ------- + ------------ + ------ + - ------------- + ------ + --------- + ------ + ------ + ----- ----- + ------------------------------ +
| 1 | ĐƠN GIẢN | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Chọn bảng được tối ưu hóa đi |
+ ---- + ------------- + ------- + ------------ + ------ + - ------------- + ------ + --------- + ------ + ------ + ----- ----- + ------------------------------ +

@lunr Số lượng InnoDB mà không có bất kỳ điều kiện nào vốn đã chậm hơn vì nó phải đi và đếm từng hàng một và kiểm tra mức độ hiển thị giao dịch cho từng loại.
jkavalik

Câu trả lời:


6

Quay lại khi mysql không có âm thanh giao dịch theo mặc định (khi mọi người thường sử dụng các bảng myISAM thay vì InnoDB vì đó là mặc định hoặc quay ngược thời gian, vì nó chưa tồn tại) "CHỌN * TỪ some_table" mà không có bất kỳ mệnh đề lọc nào là một trong những loại truy vấn mà mọi người nói về việc myQuery nhanh hơn nhiều so với các công cụ cơ sở dữ liệu khác.

Trong một môi trường an toàn giao dịch nói chung, công cụ cơ sở dữ liệu sẽ cần kiểm tra từng hàng và đảm bảo rằng nó sẽ hiển thị cho phiên hiện tại (nghĩa là nó không phải là một phần của giao dịch chưa được cam kết (hoặc không được cam kết tại bắt đầu phiên giao dịch hoạt động này) hoặc hiện đang được khôi phục) - kiểm tra mọi hàng ngụ ý cần thực hiện quét bảng hoặc (trong đó có một) quét chỉ mục cụm.

Nó sẽ là tốt cho động cơ để theo dõi số lượng hàng có thể nhìn thấy trong mỗi đối tượng cho mỗi phiên hoạt động / giao dịch, nhưng có lẽ các nhà thiết kế đã không đánh giá này có giá trị chế biến thêm tham gia vì vậy tôi cho rằng nó không thường được coi thực tiễn- Tôi có thể tưởng tượng sẽ có một số yêu cầu khóa khá phức tạp để xử lý đồng thời có thể gây hại cho hiệu suất của các hoạt động khác quá nhiều. Bạn có thể tự thực hiện việc này bằng cách giữ một bảng được ghi lại số lượng hàng trong bảng quan tâm và có tất cả mã của bạn duy trì một cách tỉ mỉ giá trị đó, nhưng điều này sẽ khá rắc rối và có thể dễ bị lỗi do lỗi có nghĩa là số lượng sẽ trôi đi từ thời gian thực (và có lẽ bạn đang thêm một nguồn bế tắc tiềm năng và / hoặc khóa cổ chai ở lớp ứng dụng).

Các tình huống sử dụng bảo mật cấp hàng phức tạp hơn nữa - cũng như cần kiểm tra trạng thái của một hàng / trang liên quan đến giao dịch hiện tại, sau đó công cụ cũng cần kiểm tra lại người dùng hiện tại và vì các quy tắc bảo mật động sẽ là không thực tế để lưu trữ thông tin này đòi hỏi phải quét thêm mỗi lần chỉ trong trường hợp. Bảo mật cấp hàng đang được thêm vào MS SQL Server trong phiên bản tiếp theo ( https://msdn.microsoft.com/en-us/l Library / dn65653131.aspx ) và đã có mặt trong postgres ( http: //www.postgresql .org / docs / 9.5 / static / ddl-rowsecurity.html ), tôi không biết về trạng thái của nó trong các RDBMS khác.


3

Bổ sung câu trả lời @ david-spillett, bạn có thể thay đổi truy vấn của mình chỉ bằng cách thay thế count(*)bằng một count(id)truy vấn của bạn, trở thành:

SELECT COUNT(id) FROM thetable;

idcột không rỗng, được lập chỉ mục (thực ra đó là khóa chính), điều đó có nghĩa là nó không rỗng đối với tất cả các hàng và, do đó, có nhiều ids như có các hàng.

Nhưng, ngay cả khi bạn thay thế count(*)bằng count(0), hoặc count("Hi, I'm a row")bạn sẽ có cùng hiệu suất, bởi vì bên trong chúng dẫn đến cùng một hoạt động. Bạn có thể kiểm tra nó so sánh kết quả của một EXPLAIN EXTENDED ...trên tất cả các truy vấn:

EXPLAIN EXTENDED SELECT COUNT(*) FROM thetable;
EXPLAIN EXTENDED SELECT COUNT(id) FROM thetable;
EXPLAIN EXTENDED SELECT COUNT(0) FROM thetable;
EXPLAIN EXTENDED SELECT COUNT("Hi, I'm a row") FROM thetable;

Hiện tại đối với InnoDB, select count(<whatever>) from table_name ;không có bất kỳ điều kiện nào, không phải là cách thực hành tốt nhất.

Loại truy vấn này hoạt động tốt hơn khi:

  1. Chỉ mục nhỏ nhất của bạn trên bảng nằm trên một cột rất nhỏ (chẳng hạn như một phần nhỏ) thay vì chỉ mục tổng hợp hoặc trên một cột lớn (như a varchar(200)), nhưng đừng thêm nó chỉ để cải thiện loại lựa chọn này. Đó là bởi vì với một chỉ mục nhỏ hơn, InnoDB có ít dữ liệu để quét hơn;
  2. Bạn thêm một WHEREtiêu chí, thu hẹp các hàng để đếm. Đây là lựa chọn tốt nhất của bạn.

3
Không. Kiểm tra EXPLAIN EXTENDED select count(*) from table_name; show warnings;và bạn sẽ thấy nó count(*)được chuyển đổi count(0)tương đương với count(PK)(hoặc chỉ mục không null khác). Trên thực tế, bất kỳ chỉ mục InnoDB thứ cấp nào cũng có thể được sử dụng cho truy vấn đó (vì tất cả chúng đều chứa các giá trị PK) và nó thường nhanh hơn so với sử dụng PK trực tiếp (ít dữ liệu để đọc).
jkavalik

Học hỏi mỗi ngày. Nhưng điều gì đặc biệt sai với câu trả lời của tôi?
Nuno Pereira

1
Chà, sửa đổi không phải là để cải thiện bất cứ điều gì và bản thân imho count(*)không phải là một thực hành xấu. Nhưng đọc lại nếu bạn muốn nói count without conditions(dù là *hay id) thì đó sẽ là một điều tồi tệ :)
jkavalik

Cảm ơn bạn rất nhiều ý kiến ​​của bạn. Tôi đã cải thiện phản hồi của mình, hoặc ít nhất là tôi đã thử, đưa ra ý kiến ​​của bạn.
Nuno Pereira

2
Một điều nữa rất dễ kiểm tra trong InnoDB. Tạo một bảng rộng (nói với một số VARCHARcột), table_id int PRIMARY KEYsau đó thêm một chỉ mục khác vào (table_id). Các truy vấn đếm sẽ sử dụng chỉ mục đó thay vì chỉ mục PK - bởi vì theo định nghĩa, đó là chỉ mục hẹp nhất bạn có thể có trong bảng InnoDB.
ypercubeᵀᴹ

1

Tạo một bảng mới (property_count (id, Count)) và sử dụng kích hoạt để chèn (đếm tăng) và để xóa (đếm giảm).

Sau đó, bạn có thể sử dụng: chọn đếm từ property_count.


0

nếu bạn có thể cấu hình truy vấn này thì chúng tôi có thể có thêm thông tin về vấn đề này. Một điều chắc chắn, vì công cụ lưu trữ là InnoDB, bộ đệm innodb có tác độ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.