Introduction to concurrency control and transaction management
by K. Yue
1. Concepts of Concurrency Control
Example:
Use case: transfer $200 from account 1000 to account 2000.
-- Task t1
-- Assumption: account 1000: $1,000, account 2000: $500
-- Initial consistent state: total of two accounts: $1,500
-- Step [1]:
UPDATE Account SET amount=amount-200 WHERE account_number=1000;
-- Between step [1] and step [2]:
--
Inconsistent state at this point; total of two accounts: $1,300
-- Step [2]:
UPDATE Account SET amount=amount+200 WHERE account_number=2000; -- step [2]
-- After completion of step [2]: consistent state again; total of two accounts: $1,500
Intended sequence #1 for task #1:
(1) Task t1 step [1]
(2) Task t1 step [2]
Sequence #2: read-write anomaly.
(1) Task t1: step [1]
(2) Task t2 reads the inconsistent state to produce an account report:
account 1000: $800, account 2000: $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 and account amounts, calculate interest and update accounts. Interests will be calculated based on account 1000: $800, account 2000: $500.
(3) Task t1: step [2]
1.1 ACID Properties
Example:
Use case: transfer $200 from account 1 to account 2000.
START TRANSACTION;
UPDATE Account SET amount=amount-200 WHERE account_number=1000;
UPDATE Account SET amount=amount+200 WHERE account_number=2000;
IF ERRORS=0 COMMIT;
IF ERRORS<>0 ROLLBACK;
1.2 Application programmer's 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=1000;
UPDATE Account SET amount=amount+400 WHERE account_number=2000;
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 on top of the Student table in toyu. The column numCourses is the number of courses a student has enrolled in. It is a derived column obtained by counted the number of classId the student in the enroll 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;
-- Populating activeStudent initially.
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; -- pass on the error with no change.
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;