Introduction to concurrency control and transaction management
by K. Yue
1. Concepts of Concurrency Control
Example:
Transfer $200 from account 6000 to account 8000
-- Task t1
-- Assume: account 6000: $1,000, account 8000: $500
-- consistent state; total of two accounts: $1,500
UPDATE Account SET amount=amount-200 WHERE account_number=6000; -- step [1]
--
inconsistent state at this point; total of two accounts: $1,300
UPDATE Account SET amount=amount+200 WHERE account_number=8000; -- step [2]
-- consistent state again; total of two accounts: $1,500
Intended sequence #1:
(1) Task t1 step (1)
(2) Task t1 step (2)
Sequence #2: read-write anomaly
(1) Task t1 step (1)
(2) Task t2 account reports:
account 6000: $800, account 8000: $500
(3) Task t1 step (2)
Sequence #3: System crash and recovery
(1) Task t1 step (1)
(2) System crashes; task t1 aborts after step (1)
Sequence #4: write-write anomaly
(1) Task t1 step (1)
(2) Task t2 reads account amounts, calculate interest and update accounts. Interests will be calculated based on account 6000: $800, account 8000: $500
(3) Task t1 step (2)
1.1 ACID Properties
START TRANSACTION;
UPDATE Account SET amount=amount-200 WHERE account_number=6000;
UPDATE Account SET amount=amount+200 WHERE account_number=8000;
IF ERRORS=0 COMMIT;
IF ERRORS<>0 ROLLBACK;
1.2 Application programmer' responsibility in terms of ACID
Example: The following transaction can be atomic but inconsistent.
START TRANSACTION;
UPDATE Account SET amount=amount-200 WHERE account_number=6000;
UPDATE Account SET amount=amount+400 WHERE account_number=8000;
IF ERRORS=0 COMMIT;
IF ERRORS<>0 ROLLBACK;
2. MySQL Transaction Management
Example:
This is not a realistic example but it shows you an example of transaction management.
Suppose we have an ActiveStudent table:
CREATE TABLE IF NOT EXISTS activeStudent(
stuId INT NOT NULL,
fname VARCHAR(30) NOT NULL,
lname VARCHAR(30) NOT NULL,
numCourses INTEGER(4) DEFAULT 0
);
SELECT * FROM activeStudent;
INSERT INTO activeStudent(stuId, fName, lName, numCourses)
SELECT s.stuId, s.fName, s.lName, COUNT(e.classId) as numCourses
FROM Student AS s LEFT JOIN Enroll AS e ON (s.stuId = e.stuId)
GROUP BY s.stuId, s.fName, s.lName;
SELECT * FROM activeStudent;
When we add the enrollment (100000, 10006, NULL, 0), we need to perform two tasks:
We can write a procedure to do so:
DROP PROCEDURE IF EXISTS enroll;
DELIMITER //
CREATE PROCEDURE enroll
(IN stuid VARCHAR(6),
IN classId VARCHAR(8),
IN grade VARCHAR(2),
IN n_alerts INT)
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
RESIGNAL;
END;
START TRANSACTION;
INSERT INTO enroll
VALUES (stuid, classId, grade, n_alerts);
UPDATE activestudent AS a
SET a.numCourses = a.numCourses + 1
WHERE a.stuid = stuid;
COMMIT;
END //
DELIMITER ;
SELECT * FROM Enroll;
SELECT * FROM ActiveStudent;
CALL enroll(100000, 10006, NULL, 0);
CALL enroll(100009, 10006, NULL, 0);
SELECT * FROM Enroll;
SELECT * FROM ActiveStudent;
DROP TABLE ActiveStudent;
DROP PROCEDURE enroll;
DELETE FROM enroll WHERE stuId = 100000 AND classId = 10006;
DELETE FROM enroll WHERE stuId = 100009 AND classId = 10006;
SELECT * FROM Enroll;