2009-12-08 16 views
5

Tôi đang tìm một chiến lược tốt về xử lý các deadlocks cơ sở dữ liệu từ bên trong một ứng dụng Java 6; một số chủ đề song song có thể, có khả năng, viết vào cùng một bảng cùng một lúc. Cơ sở dữ liệu (Ingres RDMBS) sẽ ngẫu nhiên giết một trong các phiên nếu nó phát hiện một bế tắc.Đa luồng - Tránh và xử lý các bế tắc cơ sở dữ liệu

Điều gì sẽ là một kỹ thuật có thể chấp nhận để xử lý tình trạng bế tắc, với các yêu cầu sau đây?

  • tổng thời gian trôi qua nên được giữ nhỏ như một cách hợp lý có thể
  • giết chết một phiên sẽ phải chịu một đáng kể (đo lường) rollback
  • đề thời gian không có cách nào để
    giao tiếp với nhau tức là chiến lược phải là tự trị

Cho đến nay, chiến lược tôi đưa ra là một cái gì đó dọc theo các dòng sau:

short attempts = 0; 
boolean success = false; 
long delayMs = 0; 

Random random = new Random(); 
do { 
    try { 
     //insert loads of records in table 'x' 
     success = true; 
    } catch (ConcurrencyFailureException e) { 
     attempts++; 
     success = false; 
     delayMs = 1000*attempts+random.nextInt(1000*attempts); 

     try { 
       Thread.sleep(delayMs); 
      } catch (InterruptedException ie) { 
     } 
    } 
} while (!success); 

Nó có thể được cải thiện theo bất kỳ cách nào không? ví dụ. chờ một số tiền cố định (số ma thuật) giây. Có một chiến lược khác sẽ tạo ra kết quả tốt hơn không?

Lưu ý: Một số kỹ thuật cấp cơ sở dữ liệu sẽ được sử dụng để đảm bảo deadlocks, trên thực tế, rất hiếm. Ngoài ra, ứng dụng sẽ cố gắng tránh lập kế hoạch các luồng ghi vào cùng một bảng cùng một lúc. Tình huống trên sẽ chỉ là "kịch bản xấu nhất".

Lưu ý: Bảng trong đó các bản ghi được chèn vào được tổ chức dưới dạng bảng phân đoạn heap và không có chỉ mục; mỗi luồng sẽ chèn các bản ghi vào phân vùng riêng của nó.

Trả lời

10

Cách tiếp cận thường được sử dụng là một số dạng trả về theo hàm mũ. Thay vì 1000*attempts+random aproach, hãy trì hoãn hàm mũ của số lần thử. Điều này đảm bảo độ trễ tối thiểu trong một hoặc hai lần thử đầu tiên, nơi nó có thể chỉ là may mắn mà bạn bế tắc, nhưng cho bạn sự chậm trễ lớn hơn nhiều sau đó, khi rõ ràng là kết nối thực sự bị tắc nghẽn.

Tất nhiên, cách tiếp cận khác sẽ là cố gắng sắp xếp truy cập cơ sở dữ liệu của bạn để các khóa chết ít có khả năng xảy ra hơn. Nhưng mà không biết các truy vấn của bạn làm gì (và cách thức và khi chúng được thực hiện), không thể nói nếu điều đó có thể được thực hiện

+0

Hàm mũ âm thanh tốt - Tôi sẽ cố gắng mô phỏng nó! Như tôi đã đề cập trong ghi chú, thiết kế ứng dụng sẽ nhằm mục đích tránh deadlocks, bao gồm việc sắp xếp truy cập cơ sở dữ liệu một cách thuận lợi. Nhưng không có gì đảm bảo rằng, trong các trường hợp góc, một số lượng lớn các bế tắc sẽ xảy ra. – Adrian

+0

Bạn có thể cung cấp bất kỳ tham chiếu kỹ thuật nào cho kỹ thuật "trả về số mũ" mà bạn đề cập không? – Adrian

+2

Nó thường được sử dụng trong các giao thức mạng để tránh tắc nghẽn. Kiểm tra bài viết wiki: http://en.wikipedia.org/wiki/Exponential_backoff Nhưng ý tưởng cơ bản thì đơn giản. Bạn chỉ cần sử dụng một số loại hàm mũ để xác định độ trễ tại mỗi lần thử lại. Các chi tiết chính xác có thể được điều chỉnh cho phù hợp với mục đích của bạn. Tất nhiên việc thực hiện đơn giản nhất có thể là trì hoãn 2^n ms trong đó n là số lần thử lại cho đến nay.Nhưng có lẽ bạn nghĩ rằng phát triển quá chậm để bắt đầu, hoặc bắt đầu quá thấp, hoặc phát triển quá nhanh. Sau đó, bạn chỉ cần thêm một số nhân, hoặc thêm một cái gì đó để n – jalf

1

Đó là cách chúng tôi đã làm. Lặp lại và thử lại giao dịch cho đến khi giao dịch kết thúc.

Chúng tôi không gây rối với sự chậm trễ ngẫu nhiên.

Ngoài ra, chúng tôi đã thực hiện cam kết bên trong khối try và khôi phục trong trình xử lý ngoại lệ.

Khi bạn có nhiều tài nguyên có thể khóa và nhiều giao dịch đồng thời, bế tắc là không thể tránh khỏi. Đó là một hệ quả hợp lý của tranh chấp cho khóa.

Nếu bạn tránh tranh chấp về khóa (ví dụ: khóa bảng mức bi quan) thì bạn cũng có xu hướng ngăn chặn sự tương tranh. Nếu bạn có thể xác định giao dịch mà không tranh chấp cho khóa, bạn có thể tránh bế tắc. Truy cập đồng thời vào cùng một bảng, tuy nhiên, là khá nhiều định nghĩa về bế tắc.

Khi tải, chèn (đặc biệt là trong bảng HEAP) có thể (thường) tiến hành song song mà không có nhiều vấn đề tranh chấp. Nếu bạn trì hoãn việc xây dựng các chỉ mục, thì sẽ không có các cập nhật khác xảy ra trong quá trình chèn. Vì vậy, bạn có thể tránh được bằng cách xóa các chỉ mục, thay đổi tổ chức thành một đống, tải với nhiều quy trình đồng thời (hoặc luồng, thường nhanh hơn để có nhiều quy trình), sau đó xây dựng chỉ mục của bạn (và có thể sắp xếp lại bảng), bạn có thể tránh được những deadlocks.

Khi thực hiện cập nhật hoặc xóa, không giúp được gì nhiều.

+0

Trong mô phỏng của tôi (20 chủ đề), độ trễ bằng không sẽ kích hoạt một số lượng lớn các lần bế tắc; một sự chậm trễ lớn sẽ thực sự cải thiện đáng kể thời gian trôi qua tổng thể. autocommit được đặt thành bật - nhưng bế tắc cơ sở dữ liệu cũng sẽ liên quan đến việc khôi phục tự động – Adrian

+0

@Adrian: Chúng tôi đã xử lý lỗi khá phức tạp, vì vậy chúng tôi đã thực hiện khôi phục "chỉ để chắc chắn". Ngoài ra chúng tôi đã làm việc trong C, do đó, nó là một "ngoại lệ ảo". Cuối cùng, chúng tôi đã có một số C/Ingres n00bs làm mã hóa, vì vậy chúng tôi đã làm điều đó trong trường hợp họ đã ẩn các lỗi logic nhỏ. –

+0

@Adrian: Trong thực tế thực tế của chúng tôi, sự vắng mặt của sự chậm trễ đã không thực hiện bất kỳ sự khác biệt thực tế. Một hệ điều hành được tải nặng giới thiệu sự chậm trễ ngẫu nhiên của riêng nó. YMMV. Tôi không tranh luận về mô phỏng của bạn. Tôi đang nói với bạn những gì chúng tôi đã làm *. Chúng tôi đã không gây rối với sự chậm trễ ngẫu nhiên. –

1

Nếu bạn không cần truy cập đồng thời vào cơ sở dữ liệu, một giải pháp đơn giản có thể là xóa nó và sử dụng hàng đợi xử lý nhiệm vụ để cập nhật cơ sở dữ liệu, thay thế truy cập vào cơ sở dữ liệu qua hàng đợi. Tôi nhận thấy điều này sẽ giới thiệu một phần tử không đồng bộ cho ứng dụng của bạn và vì vậy sẽ không phù hợp với hầu hết các ứng dụng do người dùng khởi tạo hoặc ứng dụng web trực tuyến, nhưng có thể đáng xem xét cho một ứng dụng loại hàng loạt/ngoại tuyến (tôi nhận ra có thể không phải là câu trả lời mà bạn đang tìm kiếm cho dù).

+0

Hàng đợi xử lý sẽ làm chậm ứng dụng đáng kể - tránh deadlocks bằng mọi giá mà không phải là một lựa chọn. Máy chủ sản xuất là một máy chủ Unix 4 bộ vi xử lý, nên xử lý một số chủ đề song song dễ dàng – Adrian

0

Với cơ sở dữ liệu như Ingres, bạn sẽ luôn nhận được một số bế tắc, vì vậy bạn phải thừa nhận rằng bất kỳ chèn, cập nhật hoặc xóa sẽ thất bại và có một chiến lược thử lại tại chỗ (như trong ví dụ của bạn). Bạn nên thiết kế cơ sở dữ liệu của bạn để tranh chấp được giảm thiểu và deadlocks chỉ xảy ra hiếm khi. Nếu bạn liên tục nhận được các giao dịch không thành công ngay cả sau một vài lần thử lại, thì đây là dấu hiệu cho thấy bạn phải thực hiện một số thiết kế lại cơ sở dữ liệu lớn (hoặc chuyển sang hệ thống như Oracle, nơi thường có thể thiết kế ứng dụng để tránh deadlocks) khóa cấp hàng).

+0

không có vấn đề gì, nhưng tôi sẽ thêm các giao dịch serialising đó có thể nhanh hơn tùy chọn nếu bạn đang nhận được quá nhiều deadlocks/rollbacks/retries –

+0

Chuyển sang RDBMS khác như Oracle không phải là một lựa chọn cho dự án này. Việc tạo chuỗi quảng cáo đã được thử và chạy chậm hơn một lần chạy "bình thường", với số lượng khóa chết thấp – Adrian

0

thế nào?

short attempts = 0; 
boolean success = false; 
long delayMs = 0; 

Random random = new Random(); 
do { 
try { 
    synchronized(ClassName.class) { 
     //insert loads of records in table 'x' 
     } 

    success = true; 
} catch (ConcurrencyFailureException e) { 
    attempts++; 
    success = false; 
    delayMs = 1000*attempts+random.nextInt(1000*attempts); 

    try { 
        Thread.sleep(delayMs); 
      } catch (InterruptedException ie) { 
    } 
    } 
} while (!success);