Lab4-事务与并发编程实现
实验四 事务与并发编程实现
实验目的:
学习MySQL中事务编程方法与技术,初步理解并发操作中锁的使用方式。
实验内容:
1、编写一个基于事务的存储过程。
2、 练习理解并发操作中锁的使用。
实验过程及要求:
1、 删除ustudent表中的触发器,编写存储过程,使用事务实现:在学生表中更新(增、删、改)学生记录时,同时更新班级表中班级的人数
2、 创建表test(c1 int primary key,c2 varchar(5),c3 varchar(5)),插入如下记录(1,’a’,’k1’), (2,’b’,’k2’), (3,’c’,’k3’), (4,’d’,’k4’), (5,’a’,’k2’), (6,’b’,’k1’), (7,’c7’,’k4’), (8,’d’,’k3’), (9,’a’,’k4’), (10,’b’,’k3’), (11,’c’,’k2’), (12,’d’,’k1’)
打开两个客户端,分别执行Set autocommit=0命令后,在一个客户端执行一条命令,另一客户端分别执行所有命令时,观察结果。(本题只观察结果即可)
(1) Select * from test where c2=’b’ and c1=6
(2) Select * from test where c2=’b’ and c1=6 for update
(3) Select * from test where c2=’b’ and c3=’k1’
(4)Select * from test where c2=’b’ and c3=’k1’ for update
具体实现操作
1、 删除ustudent表中的触发器,编写存储过程,使用事务实现:在学生表中更新(增、删、改)学生记录时,同时更新班级表中班级的人数
创建一个触发器存储过程 UpdateClassStudentCount
DELIMITER //
CREATE TRIGGER UpdateClassStudentCount
AFTER INSERT ON ustudent
FOR EACH ROW
BEGIN
DECLARE class_id VARCHAR(2);
DECLARE student_count INT;
-- class id
SET class_id = NEW.gid;
-- count of students in the class
SELECT COUNT(*) INTO student_count FROM ustudent WHERE gid = class_id;
-- ugrade table
UPDATE ugrade SET gyear = student_count WHERE gid = class_id;
END //
DELIMITER ;
删除触发器 UpdateClassStudentCount
drop TRIGGER if exists UpdateClassStudentCount;
在高并发的环境下,多个事务可能会尝试同时修改相同的数据。为了保证数据的一致性,在事务中使用 SELECT ... FOR UPDATE 或 SELECT ... LOCK IN SHARE MODE 语句来显式地获取锁。
编写存储过程,使用事务实现:在学生表中更新(增、删、改)学生记录时,同时更新班级表中班级的人数
DELIMITER //
CREATE PROCEDURE UpdateStudentAndClassCount(IN student_id VARCHAR(9), IN class_id VARCHAR(2), IN action_type VARCHAR(10))
BEGIN
DECLARE student_count INT;
-- 开始事务
START TRANSACTION;
-- 根据操作类型进行相应的操作
IF action_type = 'INSERT' THEN
-- 插入学生记录
INSERT INTO ustudent(Sid, gid) VALUES (student_id, class_id);
ELSEIF action_type = 'DELETE' THEN
-- 删除学生记录
DELETE FROM ustudent WHERE Sid = student_id;
ELSEIF action_type = 'UPDATE' THEN
-- 更新学生记录
UPDATE ustudent SET gid = class_id WHERE Sid = student_id;
END IF;
-- 更新班级表中的学生人数
SELECT COUNT(*) INTO student_count FROM ustudent WHERE gid = class_id;
UPDATE ugrade SET gnum = student_count WHERE gid = class_id;
-- 提交事务
COMMIT;
END //
DELIMITER ;
2、 创建表test(c1 int primary key,c2 varchar(5),c3 varchar(5)),插入如下记录(1,’a’,’k1’), (2,’b’,’k2’), (3,’c’,’k3’), (4,’d’,’k4’), (5,’a’,’k2’), (6,’b’,’k1’), (7,’c7’,’k4’), (8,’d’,’k3’), (9,’a’,’k4’), (10,’b’,’k3’), (11,’c’,’k2’), (12,’d’,’k1’)
打开两个客户端,分别执行Set autocommit=0命令后,在一个客户端执行一条命令,另一客户端分别执行所有命令时,观察结果。(本题只观察结果即可)
(1) Select * from test where c2=’b’ and c1=6
先建立数据表 插入必要的测试数据
create Table if not exists test(
c1 int primary key ,
c2 VARCHAR(10),
c3 VARCHAR(10)
);
INSERT INTO test VALUES
(1,'a','k1'),(2,'b','k2'),(3,'c','k3'),(4,'d','k4'),
(5,'a','k2'),(6,'b','k1'),(7,'c7','k4'),(8,'d','k3'),
(9,'a','k4'),(10,'b','k3'),(11,'c','k2'),(12,'d','k1');
打开两个sql窗口
set autocommit=0;
(2) Select * from test where c2=’b’ and c1=6 for update
如果在第二个客户端执行了与第一个客户端相同的 FOR UPDATE 查询,并且一直在等待,这很可能是因为第一个客户端已经锁定了这些记录,并且还没有提交或回滚事务。
在使用 FOR UPDATE 锁定记录后,这些锁会一直保持,直到你执行 COMMIT 或 ROLLBACK 来结束事务。在这期间,其他任何尝试锁定相同记录的事务都会被阻塞。
这个时候,另外一个是持续等待
(3) Select * from test where c2=’b’ and c3=’k1’
先提交事务再继续操作
执行查询但不加锁
在第二个客户端,执行不加锁的查询:
所有 c2 为 'b' 且 c3 为 'k1' 的记录
(4)Select * from test where c2=’b’ and c3=’k1’ for update
使用了 FOR UPDATE,这意味着这些记录将被锁定,直到你提交或回滚事务。在这段时间里,其他任何尝试更改这些记录的事务都将被阻塞