实验目的

  1. 掌握事务机制,学会创建事务。
  2. 理解事务并发操作所可能导致的数据不一致性问题。
  3. 理解锁机制,学会采用锁与事务隔离级别解决数据不一致的问题。
  4. 了解数据库的事务日志。

实验内容

假设学校允许学生将银行卡和校园卡进行绑定,在student数据库中有如下的基本表,其中校园卡编号cardid即为学生的学号:

create table icbc_card(
icbcid int,
sno varchar(8),
balance int
);
create table campus_card(
sno varchar(8),
balance int
);
insert into campus_card values ('20200032', 1);
insert into campus_card values ('20200033', 100);
insert into icbc_card values (1, '20200032', 300);
insert into icbc_card values (2, '20200033', 400);  

针对以上数据库按照要求完成下列实验:

  1. 编写一个事务处理实现如下的操作:某学号为20200032的学生从银行卡中转账200元到校园卡中,若中间出现故障则进行rollback。(10分)
CREATE PROCEDURE transfer()
BEGIN
	DECLARE icbc_balance INT DEFAULT 0;
    SELECT balance
    FROM icbc_card
    WHERE sno = '20200032'
    INTO icbc_balance;
    IF icbc_balance > 200
    THEN
		UPDATE icbc_card
        SET balance = balance - 200
        WHERE sno = '20200032';
        UPDATE campus_card
        SET balance = balance + 200
        WHERE sno = '20200032';
        COMMIT;
    ELSE
		ROLLBACK;
    END IF;
END
  1. 针对本题的数据库和表,分别用具体的例子展现几种数据不一致问题:如丢失修改、读脏数据、不可重复读和幻读(删除和插入),注意如有无法展现的情况,请说明原因。(20分,每种数据不一致10分)

两个workbench打开的两个界面都要设置隔离级

set session transaction isolation level READ UNCOMMITTED;

丢失修改:
创建一个先读后写的存储过程

CREATE DEFINER=`root`@`localhost` PROCEDURE `read_and_write`()
BEGIN
	DECLARE campus_balance INT DEFAULT 0;
    SELECT balance
    FROM campus_card
    WHERE sno = '20200032'
    INTO campus_balance;
    
    SELECT sleep(10);
    
    UPDATE campus_card
	SET balance = campus_balance + 1
	WHERE sno = '20200032';
   
END

读写之间用sleep函数留出了10s的时间,你可以打开另一个程序去运行同样的存储过程,查看最后的修改结果。

不可重复读:
先执行1,然后迅速执行2可以看到1读出的balance两次值不同。

CREATE DEFINER=`root`@`localhost` PROCEDURE `non_repeatable_read_1`()
BEGIN
	SELECT balance
    FROM campus_card
    WHERE sno = '20200032';
    
    SELECT sleep(10);
    
    SELECT balance
    FROM campus_card
    WHERE sno = '20200032';
END
CREATE DEFINER=`root`@`localhost` PROCEDURE `non_repeatable_read_2`()
BEGIN
	UPDATE campus_card
    SET balance = balance + 1
    WHERE sno = '20200032';
END

读脏数据:

CREATE DEFINER=`root`@`localhost` PROCEDURE `dirty_read`()
BEGIN
	SELECT balance
    FROM campus_card
    WHERE sno = '20200032';
END
CREATE DEFINER=`root`@`localhost` PROCEDURE `dirty_write`()
BEGIN
	UPDATE campus_card
    SET balance = balance + 1
    WHERE sno = '20200032';
    
    SELECT SLEEP(10);
    
    ROLLBACK;
END
  1. 利用数据库的隔离级别或者锁机制等,设计方案分别解决上述丢失修改、读脏数据、不可重复读、幻读的数据不一致问题。(20分,每种数据不一致5分)
    (1) 丢失修改
set session transaction isolation level READ COMMITTED;

(2) 读脏数据

set session transaction isolation level READ COMMITTED;

(3) 不可重复读

set session transaction isolation level REPEATABLE READ;

(4) 幻读(删除和插入)

set session transaction isolation level SERIALIZABLE;
  1. 构造两个事务同时更新一条数据,尝试使用以下SQL命令查看和理解当前系统中事务以及锁的状态等信息。(10分)
    直接执行下面的语句就行。
  • show engine innodb status (MySQL 8.0 or 5.7)
  • select * from information_schema.innodb_trx (MySQL 8.0 or 5.7)
  • select * from performance_schema.data_locks; (MySQL 8.0)
  • select * from sys.innodb_lock_waits; (MySQL 8.0)
  • select * from information_schema.innodb_lock_waits (MySQL 5.7)
  • select * from information_schema.innodb_locks (MySQL 5.7)
  1. 构造一个出现死锁的情形。(10分)

防止查重起见,这个我只说一下思路

lock语句可以对表进行锁定,所以可以分两个进程,一个进程对A表先锁定后更新,另一个进程对B表先锁定后更新,然后两者交换,第一个进程更新B表,第二个进程更新A表,这时它们就会进入死锁状态。

  1. 构造含有savepoint的事物,并在某时刻回滚到某个savepoint。(10分)

这一段借鉴了学长的代码

start transaction;
insert into icbc_card values(3,'20200034',900);
insert into icbc_card values(4,'20200035',600);
savepoint s1;
insert into icbc_card values(5,'20200036',400);
insert into icbc_card values(6,'20200037',500);
savepoint s2;
insert into icbc_card values(7,'20200038',200);
insert into icbc_card values(8,'20200039',100);
select * from icbc_card;

执行后依次回滚到s2,s1,整个事务;

rollback to savepoint s2;
select * from icbc_card; 
rollback to savepoint s1;
select * from icbc_card;
rollback;
select * from icbc_card; 
  1. 通过实验查看mysql中的各种日志:查询日志、错误日志、慢查询日志。(10分)

输入以下命令后将给出日志的路径,具体内容可以到指定路径下查看
查询日志:

show variables like 'general_log_file';

错误日志:

show variables like 'log_error';

慢查询日志:

show variables like 'slow_query_log_file';
  1. 用mysqlbinlog查看数据库的事务日志,并尝试按照以下场景进行数据恢复。(10分)

首先创建日志文件:

flush logs;
show master status;
	create database db1;
	use db1;
	create table t1(id int);
	create table t2(id int);
	insert into t1 values(11);
	insert into t1 values(12);
	insert into t1 values(13);
	insert into t2 values(21);
	insert into t2 values(22);
	insert into t2 values(23);
	drop table t1;
	insert into t2 values(24);
show binlog events in 'binlog.000029';  
/*此处的in后面的内容由第一步的查询结果确定*/

使用命令行工具将binlog文件中的sql语句导出(需要使用管理员权限)

这个暂时没跑通,跑通了再更新一下

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐