Là một phần của Chuỗi ngày đào tạo cơ sở dữ liệu của Quest, Brent Ozar, Thạc sĩ được chứng nhận của Microsoft đã trình bày một hướng dẫn về “Tránh bế tắc với điều chỉnh truy vấn”. Chương trình tập trung vào ba sự cố đồng thời xảy ra trong SQL Server, ba cách để khắc phục chúng và một cách có vẻ để khắc phục chúng, nhưng thực sự thì không.
Sự cố đồng thời:Khóa, chặn và bế tắc trong SQL Server
Vấn đề đồng thời là gì? Chúng xảy ra khi các truy vấn cố gắng tránh xung đột với nhau trên các đối tượng cơ sở dữ liệu như bảng. Đó là:
- Khóa - Các truy vấn thực hiện điều này mọi lúc để ngăn các truy vấn khác sử dụng bảng cùng một lúc. Đây là một hoạt động cơ sở dữ liệu bình thường.
- Chặn - Điều này xảy ra khi một truy vấn có một khóa bình thường, nhưng một truy vấn khác cố gắng có được cùng một khóa. Truy vấn thứ hai phải đợi đến khi nào cần thiết để truy vấn đầu tiên giải phóng khóa. Tùy thuộc vào bản chất của truy vấn đầu tiên, truy vấn thứ hai có thể đợi trong thời gian rất ngắn hoặc rất lâu. Chính những sự chờ đợi lâu dài đó thực sự ảnh hưởng đến hiệu suất.
- Bế tắc - Chốt lại xảy ra khi một truy vấn có một khóa, một truy vấn khác thực hiện một khóa khác và sau đó mỗi truy vấn muốn có được khóa của người kia. SQL Server giải quyết vấn đề này bằng cách chỉ định một trong các truy vấn là nạn nhân và giết nó để phá vỡ bế tắc. Mặc dù một trong các truy vấn có thể tiếp tục nhưng điều này cũng có tác động đến hiệu suất.
Khắc phục sự cố đồng thời
Bất kể bạn đang gặp phải khối hoặc bế tắc trong SQL Server, vẫn có những cách để khắc phục sự cố đồng thời. Brent đã trình bày ba phương pháp này và dành phần lớn thời gian còn lại của phiên tập trung vào phương pháp thứ hai - sửa mã lỗi.
- Có đủ chỉ mục để làm cho các truy vấn của bạn nhanh hơn, nhưng không quá nhiều chỉ mục làm chậm mọi thứ bằng cách khiến các truy vấn giữ nhiều khóa hơn trong khoảng thời gian dài hơn
- Điều chỉnh mã giao dịch của bạn để các truy vấn hoạt động thông qua các bảng theo cùng một thứ tự có thể dự đoán được mỗi lần
- Sử dụng mức độ cách ly phù hợp với nhu cầu của ứng dụng của bạn
Khi tham gia vào phần thực hành của chương trình, Brent đã nhận xét về việc sử dụng các câu lệnh NOLOCK để chặn và khai thác bế tắc. Ông cảnh báo rằng NOLOCK không thực sự khắc phục được những vấn đề này bởi vì nó dựa vào "lượt đọc bẩn" - về cơ bản, nó bỏ qua các khóa hàng của các truy vấn khác.
Trong phần trình bày của mình về điều này bằng cách sử dụng cơ sở dữ liệu Stack Overflow, anh ấy đã tạo một truy vấn đơn giản để tìm kiếm và đếm những người có tên “Alex”. Sau đó, anh ấy tạo một truy vấn khác sẽ chạy bản cập nhật về những người không được đặt tên là Alex — không chèn hoặc xóa bản ghi. Một truy vấn không nên liên quan đến truy vấn kia. Tuy nhiên, việc chạy chúng cùng nhau dẫn đến kết quả khác nhau về số lượng người có tên Alex. Điều này là do NOLOCK cho phép bạn xem dữ liệu không được cam kết, dẫn đến kết quả ngẫu nhiên mà bạn không thể dự đoán. Nó chỉ xảy ra trong điều kiện đồng thời.
Rõ ràng, cần có một bản sửa lỗi tốt hơn để chặn và bế tắc trong SQL Server không dẫn đến kết quả ngẫu nhiên và không thể đoán trước.
Một giải pháp tốt hơn cho các bế tắc SQL
Brent sau đó đã trình bày cách khắc phục sự cố bằng cách thay đổi mã gây ra nó. Bản demo đầu tiên của anh ấy cho thấy một tình huống đơn giản liên quan đến hai chiếc bàn để khán giả có thể thấy sự bế tắc trong chuyển động chậm khi nó diễn ra. Vì SQL Server tìm kiếm các deadlock cứ sau 5 giây và loại bỏ truy vấn dễ khôi phục nhất, chúng tôi có thể thấy nạn nhân của deadlock xuất hiện.
Trong một tình huống đơn giản, lời khuyên chung nhất được áp dụng, đó là hãy chạm vào các bảng theo cùng một thứ tự mỗi khi tạo truy vấn. Điều này nói chung sẽ giúp các truy vấn không bị khóa lẫn nhau.
Còn những truy vấn phức tạp hơn thì sao? Đối với tình huống này, Brent đã sử dụng một tình huống thực tế hơn có thể dễ dàng phát sinh trên Stack Overflow nơi hai người đang trả lời câu hỏi của nhau. Vì cùng một người dùng tham gia vào cả hai giao dịch, điều này gây ra bế tắc.
Ở đây, mỗi lần làm việc qua từng bảng theo cùng một thứ tự là chưa đủ mà còn cần giảm thiểu số lần chạm vào mỗi bảng. Như Brent đã giải thích, bản sửa lỗi có thể liên quan đến một số mã xấu khiến các truy vấn bị chặn, nhưng ít nhất không phải là bế tắc. Trong trường hợp này, một khối thời lượng ngắn cho phép cả hai truy vấn chạy đến khi hoàn thành sẽ tốt hơn một khối bế tắc kết thúc một trong số chúng.
Không ai muốn thay đổi mã trong hàng trăm truy vấn, vì vậy hãy tập trung vào những truy vấn liên tục bị bế tắc, loại bỏ bất kỳ dòng nào không cần thiết khỏi giao dịch và đừng ngại giới thiệu một khối để tránh bế tắc.