Các bản cập nhật MySQL đồng thời bị treo với InnoDB (trên Amazon RDS)


7

Tôi đang gặp vấn đề trong đó nhiều bản cập nhật MySQL được thực hiện cùng một lúc sẽ bị khóa và mất vài phút để hoàn tất. Tôi đang sử dụng InnoDB, vì vậy tôi bối rối không biết tại sao điều này có thể xảy ra vì mỗi bản cập nhật chỉ cập nhật 1 hàng. Tôi cũng đang sử dụng một ví dụ RDS m2.4xlund (lớn nhất họ đến).

Cập nhật bị kẹt

Đây là những gì tôi đang làm: Tôi có một bảng có khoảng 100 triệu hàng trong đó, với "lượt xem" là một cột (được lập chỉ mục) và tôi muốn cập nhật lượt xem trên khoảng 1 triệu hàng. Trên một số máy chủ khác nhau, tôi có một vòng lặp như thế này trong đó mỗi máy chủ có bộ hàng được cập nhật riêng (mã giả):

mysql("set autocommit=0");
mysql("start transaction");

foreach($rows as $row) {
    mysql("update table set views=views+1 where id=$row[id]");
}

mysql("commit");

Vòng lặp này thông qua tất cả các hàng cần được cập nhật. Nó hoạt động hoàn hảo khi số lượng máy chủ nhỏ, khoảng 4, nhưng khi nó tăng lên 10+ thì các bản cập nhật bắt đầu bị treo ở trạng thái "Cập nhật" cùng một lúc. Không có gì nói rằng nó đang chờ khóa, nó chỉ là "Đang cập nhật". Điều này xảy ra trong khoảng 5 phút, nơi cuối cùng nó sẽ thực hiện các bản cập nhật và tiếp tục qua vòng lặp và cuối cùng lại xảy ra.

Tôi không tìm cách thay thế để thực hiện cập nhật. Có những thứ như bảng tmp và

update table,tmp_table set table.views = table.views+tmp_table.views where
  table.id = tmp_table.id

khóa tất cả các hàng đang được cập nhật cho đến khi tất cả kết thúc (có thể là hàng giờ), điều này sẽ không hiệu quả với tôi. Họ PHẢI ở trong những vòng lặp khủng khiếp này.

Tôi đang tự hỏi tại sao họ có thể bị kẹt trong trạng thái "Đang cập nhật" và tôi có thể làm gì để ngăn chặn điều đó.

tldr; Có hơn 10 vòng "cập nhật" cuối cùng sẽ khóa tất cả các cập nhật đang được thực hiện, đồng thời, vì một lý do không xác định cho đến khi cuối cùng họ quyết định thực hiện cập nhật và tiếp tục qua các vòng lặp, chỉ để nó xảy ra một lần nữa sau đó.

HIỂN THỊ BIỂU TƯỢNG: http://pastebin.com/NdmAeJrz

HIỂN THỊ TÌNH TRẠNG TÌNH TRẠNG ĐỘNG CƠ: http://pastebin.com/Ubwu4F1h


1
Tại sao bạn không làm điều này trong một truy vấn duy nhất?

@ IgnacioVazquez-Abrams Bạn không thể làm điều đó với một truy vấn duy nhất, mà không sử dụng bảng tmp như mô tả.

2
Tại sao bạn sử dụng các giao dịch cho một bản cập nhật đơn giản chỉ có một bảng được tham gia? Điều đó chỉ cần thêm rất nhiều xử lý không cần thiết, điều này sẽ giải thích ít nhất một số vấn đề về hiệu suất của bạn.

@JohnGardeniers Theo hiểu biết của tôi, các giao dịch không lập chỉ mục lại các hàng được cập nhật cho đến khi chúng được cam kết.

Là cột xem là một chuỗi? Tôi thấy rất nhiều trong số này: cập nhật hình ảnh. Đặt lượt xem = lượt xem + '2',
Aaron Brown

Câu trả lời:


11

Tôi không tìm cách thay thế để thực hiện cập nhật. Có những thứ như bảng tmp [sẽ] khóa tất cả các hàng đang được cập nhật cho đến khi tất cả hoàn thành (có thể là hàng giờ), điều này sẽ không hiệu quả với tôi. Họ PHẢI ở trong những vòng lặp khủng khiếp này.

Tôi không đồng ý.

Sức mạnh của RDBMS là trong việc thực hiện các hoạt động tập hợp như "cập nhật tất cả các hàng plz". Với điều này, trực giác của bạn sẽ cho bạn biết rằng những "vòng lặp khủng khiếp" này không phải là cách tốt nhất để đi trừ những trường hợp rất hiếm.

Hãy xem logic cập nhật hiện tại của bạn và hiểu những gì nó đang làm.

Off đầu tiên, các set autocommit=0dòng trong kịch bản của bạn là không cần thiết . Bởi vì bạn rõ ràng mở một giao dịch ngay sau đó start transaction, autocommit tự động bị vô hiệu hóa cho đến khi bạn kết thúc giao dịch với COMMIThoặc ROLLBACK.

Bây giờ là phần cốt lõi của logic: Bạn đã gói tất cả các cập nhật riêng lẻ này vào trong vòng lặp trong một giao dịch lớn. Nếu ý định của bạn đằng sau các bản cập nhật lặp đi lặp lại là để giảm khóa và tăng tính đồng thời, giao dịch được gói sẽ đánh bại ý định đó. MySQL phải duy trì các khóa trên mỗi hàng mà nó cập nhật cho đến khi giao dịch được thực hiện để nó có thể khôi phục lại tất cả chúng cùng một lúc nếu giao dịch thất bại hoặc bị hủy. Hơn nữa, thay vì biết trước rằng nó sắp khóa phạm vi hàng này (điều này sẽ cho phép MySQL phát hành khóa với độ chi tiết phù hợp) , động cơ buộc phải phát hành một số lượng lớn khóa cấp hàng trong tình trạng cháy nhanh. Cho rằng bạn đang cập nhật 1 triệu hàng, đây là một gánh nặng lớn cho động cơ.

Tôi đề xuất hai giải pháp:

  1. Bật autocommitvà xóa trình bao bọc giao dịch. MySQL sau đó sẽ có thể phát hành mọi khóa hàng ngay sau khi hoàn thành cập nhật hàng. Nó vẫn bị buộc phải phát hành và phát hành một số lượng lớn các khóa trong một khoảng thời gian ngắn, vì vậy tôi nghi ngờ đây sẽ là một sửa chữa thích hợp cho bạn. Hơn nữa, nếu một số lỗi xảy ra giữa chừng trong vòng lặp, sẽ không có gì được khôi phục do công việc không bị ràng buộc giao dịch.

  2. Batch cập nhật của bạn trong một bảng tạm thời. Bạn đã đề cập và sau đó bác bỏ giải pháp này, nhưng tôi cá là nó sẽ hoạt động tốt nhất. Bạn đã thử nó chưa? Trước tiên tôi sẽ kiểm tra bản cập nhật hàng triệu đầy đủ. Nếu việc đó mất quá nhiều thời gian thì hãy phân chia công việc thành các phần nhỏ dần dần cho đến khi bạn tìm thấy điểm ngọt ngào: các lô đủ lớn để hoàn thành công việc nhanh chóng, nhưng không có lô riêng lẻ nào ngăn chặn các quy trình khác quá lâu. Đây là một kỹ thuật phổ biến mà các DBA sử dụng khi họ phải sửa đổi một số lượng lớn các hàng trong các hoạt động trực tiếp. Hãy nhớ rằng, vì mục tiêu của bạn là tối đa hóa sự đồng thời của bạn, hãy tiếp tục autocommitvà đừng bao bọc bất kỳ công việc nào này vào một giao dịch lớn để MySQL giải phóng các khóa của nó càng sớm càng tốt.

    Lưu ý rằng khi các lô trở nên nhỏ dần, giải pháp này cuối cùng xấp xỉ với giải pháp đầu tiên. Đó là lý do tại sao tôi tự tin giải pháp này sẽ hoạt động tốt hơn: Khi công cụ cơ sở dữ liệu có thể nhóm công việc của nó thành các khối, nó sẽ bay.


Cảm ơn rất nhiều cho câu trả lời sâu sắc! Tôi đã thử bảng tạm thời, và nó chỉ mất quá nhiều thời gian. Tuy nhiên, ý tưởng thực hiện nó theo lô là tuyệt vời. Tôi sẽ cho nó một cú bắn càng sớm càng tốt. Cảm ơn!
Alan

@Alan - Làm thế nào nó đi?
Nick Chammas

2
Tôi đồng ý với sự bất đồng của bạn. Yêu các chi tiết. +1 !!!
RolandoMySQLDBA

1

Luôn có mối đe dọa sắp xảy ra bế tắc, ngay cả với InnoDB. Trong trường hợp cụ thể này, tôi có thể thấy các hàng ngay cả trong InnoDB đang chạy đầu vào các tình huống bế tắc vì bạn đang cập nhật dữ liệu thông qua KHÓA CHÍNH của các bảng xem. Điều này sẽ bắt đầu khóa đồng ý trong chỉ mục được nhóm.

Bạn có thể thấy điều này bị khóa bằng cách sử dụng SHOW ENGINE INNODB STATUS\G

Tôi đã trả lời ba câu hỏi rất khó giải quyết một vấn đề tương tự.

Các truy vấn CHỌN / CẬP NHẬT có thể thực hiện các khóa trên gen_clust_index , còn gọi là Chỉ mục cụm khi cập nhật thông qua KHÓA CHÍNH.

Dưới đây là ba câu hỏi trao đổi ngăn xếp DBA tôi tích cực xem qua với @RedBlueThing , người đã hỏi những câu hỏi này. @RedBlueThing tìm thấy công việc xung quanh câu hỏi của mình.

Trong cả ba câu hỏi này, một khóa hàng liên quan đến một khóa tương ứng trong chỉ mục được nhóm của cùng một bảng. Các khóa lân cận của các hàng bị khóa có liên quan và do đó đã góp phần vào các vấn đề.

MORAL OF THE STORE: Bế tắc với InnoDB vẫn là một khả năng. Thiết lập một thuật toán thích hợp cho các khóa cấp hàng riêng lẻ và cá nhân cập nhật các hàng được đề cập sẽ an toàn hơn rất nhiều khi cập nhật hàng loạt qua nhiều khóa cấp hàng bất kỳ ngày nào.

Đảm bảo sử dụng autocommit=1khi cập nhật nhiều bảng theo cách này. Thậm chí, việc cập nhật một hàng trong InnoDB sẽ khiến tất cả các loại dữ liệu MVCC bị che khuất xung quanh các nội dung trước đó của hàng để cho phép các giao dịch đồng thời. Với bản chất của CẬP NHẬT, sẽ có rất nhiều dữ liệu MVCC được tạo ra.


0

Nhìn vào trạng thái innodb của bạn, tôi thấy bế tắc mới nhất với bảng xem cũng là do truy vấn này:

update low_priority reddit_new 
join images_new on images_new.hash = reddit_new.hash 
set reddit_new.score = images_new.views 
where date > date(now() - interval 1 day)

Được reddit_new.datelập chỉ mục? Các cột băm từ cả hai bảng được lập chỉ mục?

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.