Một ví dụ kinh điển trong giao dịch
Chúng ta không thể kiểm tra hết tất cả các tính năng tiên tiến của một hệ cơ sở dữ liệu trong thời gian dài trước khi các giao dịch hòa lẫn vào với nhau. Giao dịch là một nhóm các câu truy vấn SQL được được xem như nguyên tử, là một đơn vị duy nhất trong công việc. Nếu các kỹ thuật cơ sở dữ liệu có thể áp dụng toàn bộ nhóm của các truy vấn cơ sở dữ liệu hay không, cơ sở dữ liệu có thể làm như vậy, nhưng nếu bất kỳ một trong chúng không thể thực hiện được bởi vì bị hỏng hoặc lý do khác, thì không giao dịch nào trong số chúng được áp dụng.
Một phần này chỉ rõ MySQL. Nếu chúng ta đã quen thuộc với các giao dịch ACID, chúng ta có thể tự bỏ qua phần "giao dịch trong MySQL".
Ứng dụng trong ngân hàng là ví dụ cổ điển về lý do tại sao giao dịch lại cần thiết. Hãy tưởng tượng một cơ sở dữ liệu của ngân hàng với hai bảng: trái phiếu và tiết kiệm. Để di chuyển $200 từ tài khoản trái phiếu của Jane vào tài khoản tiết kiệm của chính cô ta, chúng ta cần phải thực hiện ít nhất ba bước sau:
b1. Kiểm tra số dư tài khoản của cô đấy là lớn hơn $200. b2. Trừ $200 từ số dư tài khoản trái phiếu của cô đấy. b3. Thêm $200 vào tài khoản tiết kiệm của cô ấy.
Toàn bộ hoạt động nên được nằm trong một giao dịch, như vậy nếu có một trong những các bước không thành công, bất kỳ bước nào hoàn thành trước đó đều cần thực hiện lại.
Chúng ta bắt đầu một giao dịch với khối lệnh START TRANSACTION, và sau đó hoặc là thay đổi cố định với COMMIT hoặc loại bỏ các thay đổi với ROLLBACK. Vì vậy, các giao dịch mẫu trong SQL là:
1. START TRANSACTION;
2. SELECT balance FROM checking WHERE customer_id = 10233276;
3. UPDATE checking SET balance = balance - 200.00 WHERE customer_id = 10233276;
4. UPDATE savings SET balance = balance + 200.00 WHERE customer_id = 10233276;
5. COMMIT;
Tuy nhiên, nếu chỉ riêng việc giao dịch thì không phải là toàn bộ câu chuyện. Điều gì sẽ xảy ra nếu máy chủ cơ sở dữ liệu bị lỗi trong khi thực hiện dòng 4. Ai mà
biết được khách hàng có thể mất $200. Và những gì xảy ra nếu đang có quá trình xử lý khác nằm giữa các dòng 3 và 4, và xóa toàn bộ số dư tài khoản trái phiếu. Khi đó ngân hàng đã cho khách hàng một khoản tín dụng $200 mà không biết.
Giao dịch không đầy đủ trừ khi hệ thống vượt qua các kiểm tra ACID. ACID là viết tắt của Atomicity, Consistency, Isolation, và Durability. Đây là những tiêu chuẩn liên quan chặt chẽ mà một hệ thống xử lý giao dịch tốt cần phải đáp ứng:
Tính Atomicity
Một giao dịch phải được xem như chức năng của một đơn vị duy nhất trong công việc mà không thể chia nhỏ được nữa, vì thế toàn bộ giao dịch có thể được áp dụng hoặc là truy ngược lại. Khi các giao dịch là Atomicity, đây không phải như những thứ của một giao dịch hoàn thành một phần: đó là tất cả hoặc không có gì.
Tính nhất quán
Cơ sở dữ liệu nên luôn luôn di chuyển từ một trạng thái phù hợp nhất quán đến trạng thái tiếp theo. Trong ví dụ của chúng ta, tính nhất quán đảm bảo rằng lỗi giữa dòng 3 và 4 không gây ra thất thoát $200 từ tài khoản trái phiếu. Bởi vì việc giao dịch không bao giờ được committed, không có sự thay đổi nào của các giao dịch được phản ánh trong cơ sở dữ liệu.
Tính tách biệt
Các kết quả của một giao dịch thường là không thể trông thấy đối với các giao dịch khác cho đến khi giao dịch hoàn thành. Điều này đảm bảo rằng nếu một tài khoản ngân hàng chạy sau dòng 3 nhưng trước khi dòng 4 trong ví dụ của chúng ta, nó vẫn sẽ thấy $200 trong tài khoản trái phiếu. Khi chúng ta thảo luận về mức độ cách ly, chúng ta sẽ hiểu lý do tại sao chúng ta thường hay nói về sự không thể trông thấy.
Tính bền vững
Một lần committed, các thay đổi của giao dịch là cố định. Điều này có nghĩa là thay đổi phải được ghi lại sao cho dữ liệu sẽ không bị mất khi hệ thống có lỗi xảy ra. Độ bền vững là một khái niệm không được rõ ràng, tuy nhiên trên thực tế chúng có nhiều cấp độ khác nhau. Một số chiến lược về tính bền vững sẽ cung cấp sự đảm bảo về độ an toàn mạnh hơn những đặc tính khác, và không có gì là bền vững 100%.
Các giao dịch ACID đảm bảo rằng, các ngân hàng không làm mất tiền của chúng ta. Nói chung là cực kỳ khó khăn hoặc không thể làm điều này với ứng dụng logic. Một máy chủ cơ sở dữ liệu cùng với giao dịch ACID phải phân loại tất cả các vấn đề phức tạp, mà chúng ta có thể không nhận ra để cung cấp sự bảo đảm ACID.
Cũng như với việc tăng thêm phân mảnh khóa, nhược điểm của việc bảo mật thêm này là các máy chủ cơ sở dữ liệu phải làm nhiều công việc hơn. Một máy chủ cơ
sở dữ liệu với các giao dịch ACID thường đòi hỏi công suất của CPU, bộ nhớ, khoảng trống đĩa nhớ, nhiều hơn so với một server mà không có ACID.
Như chúng ta đã nói nhiều lần, đây là nơi kiến trúc của kỹ thuật lưu trữ trong MySQL thực hiện để mang lại lợi ích cho chúng ta. Chúng ta có thể quyết định xem ứng dụng của chúng ta cần giao dịch hay không. Nếu chúng ta không thực sự cần đến chúng, chúng ta có thể có được hiệu suất cao hơn với một kỹ thuật lưu trữ không giao có giao dịch ( kiểu lưu trữ MyISAM) cho một số loại truy vấn. Chúng ta có thể có thể sử dụng LOCK TABLES để đưa ra các mức độ bảo vệ chúng ta cần mà không cần có giao dịch. Tất cả đó là tùy thuộc vào chúng ta.
Các mức tách biệt
Tách biệt thì phức tạp hơn các khóa. Chuẩn SQL định nghĩa bốn mức tách biệt với các quy tắc cụ thể mà không nhìn ở bên trong và bên ngoài một giao dịch. Các mức tách biệt thấp hơn thường cho phép tính đồng thời cao hơn và có phí tài nguyên thấp hơn. Chúng ta hãy xem nhanh bốn mức tách biệt sau:
READ UNCOMMITTED
Ở cấp độ tách biệt READ UNCOMMITTED, các giao dịch có thể hiển thị kết quả của các giao dịch không committed. Ở cấp độ này, nhiều vấn đề có thể xảy ra trừ khi chúng ta thực sự biết những gì chúng ta đang làm và có một lý do tốt để làm việc đó. Mức độ này là hiếm khi được sử dụng trong thực tế bởi vì hiệu năng của nó không tốt hơn nhiều so với cấp độ khác những cấp đã có nhiều cải tiến. Đọc dữ liệu không committed cũng được biết đến như là một cách đọc rõ ràng.
READ COMMITTED
Mức độ tách biệt được mặc định cho hầu hết các hệ thống cơ sở dữ liệu (nhưng không phải MySQL!) là READ COMMITTED. Nó đáp ứng được định nghĩa đơn giản của tách biệt được sử dụng trước đó: một giao dịch sẽ chỉ thấy những thay đổi được thực hiện bởi các giao dịch mà đã thực sự committed khi nó bắt đầu, và những thay đổi của nó sẽ không được nhìn thấy bởi giao dịch khác cho đến khi nó đã committed. Mức này vẫn cho phép những gì được biết đến như việc đọc không thể lặp lại. Điều này có nghĩa là chúng ta có thể chạy cùng một khối lệnh hai lần và xem dữ liệu khác nhau.
REPEATABLE READ
REPEATABLE READ sẽ giải quyết vấn đề mà READ UNCOMMITTED cho phép. Điều này đảm bảo rằng bất kỳ hàng nào mà giao dịch đọc sẽ "trông giống nhau" trong các lần đọc sau, tuần tự trong cùng một giao dịch, nhưng theo lý thuyết nó vẫn cho phép một vấn đề khó khăn, đó là đọc ảo. Đọc ảo đơn giản là có thể xảy ra khi chúng ta chọn một dãy các hàng, một giao dịch khác là chèn một hàng mới vào khoảng
hàng đó, và sau đó chúng ta chọn một khoảng hàng một lần nữa, sau đó chúng ta sẽ thấy hàng ảo. InnoDB và Falcon giải quyết vấn đề đọc ảo bằng kiểm soát đồng thời đa phiên bản.
REPEATABLE READ
REPEATABLE READ là một mức mặc định về tách biệt giao dịch của MySQL. Các kỹ thuật lưu trữ InnoDB và Falcon phải áp dụng thiết lập này. Một số kỹ thuật lưu trữ khác cũng làm vậy, nhưng sự lựa chọn tùy thuộc vào kỹ thuật nào.
SERIALIZABLE
Mức cao nhất của đặc tính tách biệt, SERIALIZABLE sẽ giải quyết vấn đề đọc ảo bằng cách bắt buộc các giao dịch phải xếp theo hàng để chúng không thể gây xung đột. Tóm lại, SERIALIZABLE tự đặt một khóa trên mỗi dòng mà nó đọc. Ở cấp độ này, rất nhiều trường hợp timeout và khóa kết nối có thể xảy ra. Chúng ta hiếm khi thấy mọi người sử dụng mức tách biệt này, nhưng các nhu cầu của ứng dụng của chúng ta có thể buộc chúng ta phải chấp nhận tính đồng thời thiên vị cho dữ liệu ổn định mà nó tạo ra. Bảng 1 tóm tắt các mức cô lập khác nhau và những hạn chế liên quan tới mỗi mức.
Bảng 2.6 Các mức cô lập ANSI SQL 12
Các giao dịch trong MySQL
MySQL AB cung cấp ba kỹ thuật lưu trữ giao dịch: InnoDB, Cluster ndb, và Falcon. Một số kỹ thuật của bên thứ ba cũng có sẵn, các công cụ nổi tiếng nhất hiện nay là solidDB và PBXT.
12
Nguồn: Baron Schwartz, Peter Zaitsev, Vadim Tkachenko, Jeremy D. Zawodny, Arjen Lentz, and Derek J. Balling (2008), “High Performance MySQL, Giâyond Edition”, pp 9
AUTOCOMMIT
MySQL hoạt động dựa theo chế độ mặc định AUTOCOMMIT. Điều này có nghĩa rằng, trừ khi chúng ta đã bắt đầu rõ ràng một giao dịch, nó sẽ tự động thực thi mỗi truy vấn trong một giao dịch riêng biệt. Chúng ta có thể kích hoạt hoặc vô hiệu hóa chế độ AUTOCOMMIT cho kết nối hiện tại bằng cách thiết lập một biến:
Giá trị 1 và ON là tương đương, như là 0 và OFF. Khi chúng ta chạy với
autocommit = 0, chúng ta luôn luôn trong một giao dịch, cho đến khi chúng ta phát đi một tuyên bố COMMIT hoặc ROLLBACK.
MySQL> SHOW VARIABLES LIKE 'AUTOCOMMIT';
+---+---+ | Variable_name | Value | +---+---+ | autocommit | ON | +---+---+ 1 row in set (0.00 giây)
MySQL> SET AUTOCOMMIT = 1;
Sau đó, MySQL bắt đầu một giao dịch mới ngay lập tức. Việc thay đổi giá trị của lệnh AUTOCOMMIT không có tác dụng trên các bảng non-transactional, chẳng hạn như bảng MyISAM hoặc bảng bộ nhớ mà các bảng này chủ yếu hoạt động trong chế độ AUTOCOMMIT. Với các lệnh chắc chắn xảy ra khi có hiệu lực trong suốt một giao dịch mở, sẽ gây ra cho MySQL phải commit các giao dịch trước khi chúng thực hiện. Đây là những lệnh Data Definition Language (DDL) mà làm thay đổi đáng kể, chẳng hạn như ALTER TABLE, nhưng lệnh LOCK TABLES và một số khối lệnh khác cũng có hiệu ứng này.
MySQL cho phép chúng ta thiết lập mức độ tách biệt bằng cách sử dụng các lệnh SET TRANSACTION ISOLATION LEVEL, có hiệu lực thực thi khi giao dịch tiếp theo bắt đầu. Chúng ta có thể thiết lập mức độ tách biệt toàn bộ máy chủ trong file cấu hình hoặc chỉ cho phiên làm việc của chúng ta:
MySQL> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
MySQL công nhận tất cả bốn mức độ cô lập theo tiêu chuẩn ANSI và InnoDB hỗ trợ tất cả trong số chúng. Các kỹ thuật lưu trữ khác sẽ tùy biến hỗ trợ cho các mức độ tách biệt khác nhau.