Tại sao InnoDB không lưu trữ số hàng?


19

Mọi người đều biết rằng, trong các bảng sử dụng InnoDB làm công cụ, các truy vấn như SELECT COUNT(*) FROM mytablerất không chính xác và rất chậm, đặc biệt là khi bảng trở nên lớn hơn và có các thao tác chèn / xóa hàng liên tục trong khi truy vấn đó thực thi.

Theo tôi hiểu, InnoDB không lưu trữ số hàng trong một biến nội bộ, đó là lý do cho vấn đề này.

Câu hỏi của tôi là: Tại sao lại như vậy? Sẽ rất khó để lưu trữ thông tin như vậy? Đó là một thông tin quan trọng cần biết trong rất nhiều tình huống. Khó khăn duy nhất tôi thấy nếu một số lượng nội bộ như vậy sẽ được thực hiện là khi các giao dịch được tham gia: nếu giao dịch không được cam kết, bạn có đếm các hàng được chèn bởi nó hay không?

Tái bút: Tôi không phải là chuyên gia về DB, tôi chỉ là người có MySQL như một sở thích đơn giản. Vì vậy, nếu tôi chỉ hỏi điều gì đó ngu ngốc, đừng quá quan trọng: D.


6
Chậm, có. Không chính xác, không. Nó chậm vì nó cho kết quả chính xác. Khi bạn có bảng hàng 200M và có thể nhiều giao dịch khác chèn / xóa vào cùng một bảng, có thể nhiều hàng mỗi giây, một câu hỏi khác là "bạn có cần số chính xác không?"
ypercubeᵀᴹ

@ypercube Tôi biết tôi đã thấy một vài lần trong phpmyadmin một số giá trị đếm hàng bị tắt. Thêm vào đó, có một bình luận có nội dung như "có thể không chính xác".
Radu Murzea

1
@RaduMurzea Người dùng phpMyAdmin một phương pháp thay thế để tính tổng số bảng cho các bảng InnoDB vì lý do tốc độ mà bạn biết. Đây là nơi mà sự thiếu chính xác mà bạn đề cập đến phát huy tác dụng. SELECT COUNT(*) FROM ...Truy vấn thực tế là chính xác. Nếu bạn thích, phpMyAdmin có thể được cấu hình để luôn sử dụng số lượng hàng chính xác với chi phí tốc độ. Thông tin thêm: stackoverflow.com/questions/11926259/
Khắc

Câu trả lời:


9

Tôi đồng ý với @RemusRusanu (+1 cho câu trả lời của anh ấy)

SELECT COUNT(*) FROM mydb.mytabletrong InnoDB hoạt động như một công cụ lưu trữ giao dịch nên. So sánh nó với MyISAM.

MyISAM

Nếu mydb.mytablelà bảng MyISAM, khởi chạy SELECT COUNT(*) FROM mydb.mytable;cũng giống như chạy SELECT table_rows FROM information_schema.table WHERE table_schema = 'mydb' AND table_name = 'mytable';. Điều này kích hoạt tra cứu nhanh số lượng hàng trong tiêu đề của bảng MyISAM.

InnoDB

Nếu mydb.mytablelà một bảng InnoDB, bạn sẽ nhận được nhiều thứ đang diễn ra. Bạn có MVCC đang diễn ra, quản lý như sau:

  • ib_logfile0 / ib_logfile1 (Nhật ký làm lại)
  • ibdata1
    • Hoàn tác Nhật ký
    • Rollback
    • Thay đổi từ điển dữ liệu
  • Quản lý bể đệm
  • Cách ly giao dịch (4 loại)
    • Đọc lặp lại
    • Đọc cam kết
    • Đọc không cam kết
    • Nối tiếp

Yêu cầu InnoDB cho số lượng bảng yêu cầu điều hướng thông qua những điều đáng ngại này. Trong thực tế, người ta không bao giờ thực sự biết nếu SELECT COUNT(*) from mydb.mytableđếm chỉ đọc lặp lại hoặc bao gồm các lần đọc đã được cam kết và những lần không được cam kết.

Bạn có thể cố gắng ổn định mọi thứ một chút bằng cách bật innodb_stats_on_metadata .

Theo Tài liệu MySQL trên innodb_stats_on_meta_data

Khi biến này được bật (là mặc định, như trước khi biến được tạo), InnoDB cập nhật số liệu thống kê trong các câu lệnh siêu dữ liệu như SHOW TABLE STATUS hoặc SHOW INDEX hoặc khi truy cập vào bảng thông tin BẢNG BIỂU TƯỢNG hoặc THỐNG KÊ. (Các cập nhật này tương tự như những gì xảy ra với ANALYZE TABLE.) Khi bị tắt, InnoDB không cập nhật số liệu thống kê trong các hoạt động này. Vô hiệu hóa biến này có thể cải thiện tốc độ truy cập cho các lược đồ có số lượng bảng hoặc chỉ mục lớn. Nó cũng có thể cải thiện tính ổn định của các kế hoạch thực hiện cho các truy vấn liên quan đến các bảng InnoDB.

Vô hiệu hóa nó có thể hoặc không thể cung cấp cho bạn số lượng ổn định hơn về mặt thiết lập kế hoạch GIẢI THÍCH. Nó có thể ảnh hưởng đến hiệu suất SELECT COUNT(*) from mydb.mytabletheo cách tốt, cách xấu hoặc hoàn toàn không. Hãy dùng thử và xem !!!


16

Đối với bộ khởi động, không có thứ gọi là 'số hiện tại' để lưu trữ trong một biến. Một truy vấn giống như SELECT COUNT(*) FROM ...tuân theo mức cô lập hiện tại và tất cả các giao dịch đang chờ xử lý đồng thời. Tùy thuộc vào mức độ cô lập, truy vấn có thể thấy hoặc không thấy các hàng được chèn hoặc xóa bằng cách chờ xử lý các giao dịch không được cam kết. Cách duy nhất để trả lời là đếm các hàng hiển thị cho giao dịch hiện tại.

Lưu ý rằng tôi thậm chí không chạm vào chủ đề thậm chí còn gai góc hơn của các giao dịch đồng thời bắt đầu hoặc kết thúc trong khi đếm. Chưa kể đến việc rollback ...


1
Ok, vì vậy nó phụ thuộc vào mức độ cô lập, điều đó có ý nghĩa. Nhưng nó vẫn có thể được thực hiện.
Radu Murzea

@SoboLAN Có rất nhiều lý do tại sao nó không nên & không thể, hầu hết trong số đó được liệt kê ở trên. Bạn có triển khai nó bằng cách duy trì danh sách số lượng trên mỗi bảng cho mỗi lần bắt đầu giao dịch (bất kể SCN của Oracle có trong MySQL) không? Việc quản lý số lượng như vậy sẽ là một chi phí khổng lồ - hãy nghĩ về một cơ sở dữ liệu với 100 hoặc 1000 phiên đồng thời, mỗi phiên thực hiện một lượng lớn CHỨNG / XÓA trên cùng một bảng. Không thể duy trì.
Philᵀᴹ

Thực hiện điều này là khá khó khăn. Chỉ cần nghĩ rằng số lượng phải được duy trì trong DB, điều đó có nghĩa là ở đâu đó trong siêu dữ liệu và số này phải được duy trì bởi mọi giao dịch chèn hoặc xóa một hàng. Làm thế nào bạn sẽ khóa siêu dữ liệu đó? Và làm thế nào bạn sẽ xử lý rollback? Là xa tầm thường. Và kết quả sẽ có thể sử dụng được cho một tập hợp con các truy vấn rất hẹp.
Remus Rusanu

3
@JackDoumund Thú vị. Từ những gì tôi thấy trong các COUNT(*)truy vấn trước đây hiếm khi cần trong thực tế & thường là kết quả của sự thiếu kinh nghiệm của nhà phát triển (đếm các hàng trước khi chúng tôi chọn chúng!) Hoặc thiết kế ứng dụng xấu.
Philᵀᴹ

1
@SoboLAN - không, nó sẽ không. Có một dịch vụ cập nhật một số loại bảng thống kê theo các khoảng thời gian được xác định trước sẽ tốt hơn nhiều. Hãy tưởng tượng có một cơ sở dữ liệu lớn và một số quản trị viên truy vấn hầu hết các bảng SELECT COUNT(*), thêm một bảng không được tối ưu hóa WHEREvào bảng và bạn sẽ có một vài người dùng đưa db đến đầu gối của mình cho một số bộ đếm thống kê hữu ích.
NB

0

Mặc dù về mặt lý thuyết có thể giữ một số lượng chính xác số lượng hàng cho một bảng nhất định với InnoDB, nhưng nó sẽ phải trả giá bằng việc khóa rất nhiều, điều này sẽ ảnh hưởng tiêu cực đến hiệu suất. Nó cũng sẽ khác nhau dựa trên mức độ cô lập.

MyISAM đã thực hiện khóa cấp bảng, do đó không có thêm chi phí ở đó.

Tôi hiếm khi yêu cầu số lượng hàng cho một bảng, mặc dù tôi sử dụng COUNT (*) khá nhiều. Tôi thường có một mệnh đề WHERE kèm theo. Sử dụng một chỉ mục hiệu quả trên một tập kết quả nhỏ, tôi thấy rằng chúng đủ nhanh.

Tôi không đồng ý rằng số lượng không chính xác. Tổng số đại diện cho một ảnh chụp nhanh của dữ liệu và tôi luôn thấy chúng là chính xác.

Nói tóm lại, MySQL để bạn thực hiện điều này cho InnoDB. Bạn có thể lưu trữ số lượng và tăng / giảm sau mỗi truy vấn. Mặc dù vậy, giải pháp dễ dàng hơn có lẽ là chuyển sang MyISAM.


2
Đó là không thể giữ một số chính xác về lượng hàng trong một hệ thống giao dịch. Bởi vì có nhiều hàng khác nhau (và chính xác) như các giao dịch đang hoạt động.
a_horse_with_no_name

5
Tôi đã đưa ra -1 ở đây cho 'Mặc dù, giải pháp dễ dàng hơn có lẽ là chuyển sang MyISAM.' Tôi sẽ không bao giờ khuyên bạn nên chuyển sang MyISAM chỉ để lấy số hàng.
Derek Downey

@a_horse_with_no_name, vì vậy bạn đồng ý rằng sẽ có một hàng "chính xác" cho mỗi giao dịch. Có vẻ như có thể với tôi.
Marcus Adams

1
@DTest, tôi chưa bao giờ nói "chỉ đơn giản là để có được số hàng".
Marcus Adams

@a_horse_with_no_name, Điều đó có vẻ không đúng. Chắc chắn chúng ta chỉ đang đếm số lượng hàng khi giao dịch được cam kết đúng không?
Pacerier
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.