Introduction to concurrency control and transaction management

by K. Yue

1. Concepts of Concurrency Control

Example:

Transfer $200 from account 1234 to 2345

http://en.wikipedia.org/wiki/SQL#Transaction_controls

Task t1:
-- Assume: account 1234 = $1,000, account 2345: $500
UPDATE Account SET amount=amount-200 WHERE account_number=1234; -- (1) inconsistent state at this point.
UPDATE Account SET amount=amount+200 WHERE account_number=2345; -- (2)

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 1234 = $800, account 2345: $500
(3) Task t1 step (2)

Sequence #3: System crashes and recovery

(1) Task t1 step (1)
(2) System crashes; task t1 aborted after step (1)

Sequence #4: write-write anomaly

(1) Task t1 step (1)
(2) Task t2 read account amounts, calculate interest and update accounts. Interest will be calculated based on account 1234 = $800, account 2345: $500
(3) Task t1 step (2)

ACID

START TRANSACTION;
  UPDATE Account SET amount=amount-200 WHERE account_number=1234;
  UPDATE Account SET amount=amount+200 WHERE account_number=2345;

IF ERRORS=0 COMMIT;
IF ERRORS<>0 ROLLBACK;

Programmer' responsibility In terms of ACID:

START TRANSACTION;
  UPDATE Account SET amount=amount-200 WHERE account_number=1234;
  UPDATE Account SET amount=amount+400 WHERE account_number=2345;

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      varchar(6),
   lastName   varchar(20)  NOT NULL,
   firstName    varchar(20)  NOT NULL,
   numCourses  integer(4) DEFAULT 0
);

select * from activeStudent;

insert into activeStudent(stuId, lastName, firstName, numCourses)
select s.stuId, s.lastName, s.firstName, count(e.classNumber) as numCourses
from Student s natural join Enroll e
group by s.stuId, s.lastName, s.firstName;

select * from activeStudent;

When we add the enrollment ('S1010', 'HST2015A', null), we need to perform two tasks:

  1. insert ('S1010', 'HST2015A', null) into enroll.
  2. increment numCourses for 'S1010' by 1 in activeStudent

When we add the enrollment ('S1013', 'HST2015A', null), we need to perform two tasks:

  1. insert ('S1013', 'HST2015A', null) into enroll.
  2. insert a new row for 'S1013' into activeStudent since it is not yet there.

We can write a procedure to do so:

drop procedure if exists enroll;

delimiter //

create procedure enroll
   (in stuid varchar(6),
    in classNumber varchar(8),
    in grade varchar(2))
begin

declare already_in int default 0;
declare exit handler for sqlexception rollback;

start transaction;
insert into enroll
values (stuid, classNumber, grade);

select count(*) into already_in
from activestudent a
where a.stuid = stuid;

if (already_in > 0) then
   update activestudent a
   set a.numCourses = a.numCourses + 1
   where a.stuid = stuid;
else
   insert into activestudent
   select s.stuid, s.lastName, s.firstName, count(e.classNumber)
   from student s, enroll e
   where s.stuid = e.stuid
   and s.stuid = stuid;
end if;

commit;

end //

delimiter ;

call enroll('S1010', 'HST205A', null);
call enroll('S1013', 'HST205A', null);

-- clean up
delete from enroll
where stuid = 'S1010'
and classNumber = 'HST205A';

delete from enroll
where stuid = 'S1013'
and classNumber = 'HST205A';