DM数据库事务管理

发布于:2022-12-30 ⋅ 阅读:(855) ⋅ 点赞:(0)

一、事务简介

事务就是用户定义的一系列数据库操作,这些操作可以视为一个完成的逻辑处理工作单元,要么全部执行,要么全部不执行,是不可分割的工作单元。

在数据库的课堂上,经常被拿来举例子的就是转账:A转账给B100块,这个转账会涉及到两个关键的步骤,A的余额减少100块,B的余额增加100块。事务要保证这关键的步骤,要么成功,要么失败。

--创建一个测试表
create table "TEST1"
(
	"ID" INT not null ,
	"monkey" NUMBER(10, 4),
	"name" VARCHAR2(50),
	primary key("ID")
)

---插入测试数据
insert into "TEST1"("ID", "monkey", "name") 
VALUES(001, 5000,'A');

insert into "TEST1"("ID", "monkey", "name") 
VALUES(002, 50000,'B');

insert into "TEST1"("ID", "monkey", "name") 
VALUES(003, 100000,'C');

insert into "TEST1"("ID", "monkey", "name") 
VALUES(004, 6000,'D');

insert into "TEST1"("ID", "monkey", "name") 
VALUES(005, 7000,'E');

commit;

1.A给B用户转账100块

update "TEST1" set "monkey"="monkey"-100 where ID=001;

2.B的余额增加100

update "TEST1" set "monkey"="monkey"+100 where ID=002;

3.提交事务

commit;
在上面的例子中,需要考虑两种情况:如果两条 SQL 语句全部正常执行,使账户间的平衡得以保证,那么此事务中对数据的修改就可以应用到数据库中;如果发生诸如资金不足、 账号错误、硬件故障等问题,导致事务中一条或多条 SQL 语句不能执行,那么整个事务必须被回滚掉才能保证账户间的平衡。DM 数据库提供了足够的事务管理机制来保证上面的事务要么成功执行,所有的更新都会写入磁盘,要么所有的更新都被回滚,数据恢复到执行该事务前的状态。无论是提交还是回滚,DM 保证数据库在每个事务开始前、结束后是一致的。

数据库事务,具有四大特性(ACID)

1.原子性:A  事务是最小的执行单位,不允许单独分开。

举例:转账业务必须由上面两个步骤组成,任何一个步骤失败,业务必须回滚。

2.一致性:C 执行事务前后,数据保持一致。

举例:转账前后,两者所在金额总和是恒定值,不会因为转赠操作而导致金额丢失。

3.隔离性:I 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的。

举例:用户A、B转账和用户C、D转账没有任何影响,隔离开。

4.持久性:D

一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有影响。

举例:A和B转账成功后,没有其他操作的情况下,A的资金会一直是原先的金额-转账的金额,B的资金是原先的金额+收到的金额。

二、事务带来的并发问题

 1. 脏读(DirtyRead)
 所谓脏读就是对脏数据的读取,而脏数据所指的就是未提交的已修改数据。也就是说,一个事务正在对一条记录做修改,在这个事务完成并提交之前,这条数据是处于待定状态的(可能提交也可能回滚),这时,第二个事务来读取这条没有提交的数据,并据此做进一步的处理,就会产生未提交的数据依赖关系,这种现象被称为脏读。如果一个事务在提交操作结果之前,另一个事务可以看到该结果,就会发生脏读。

举例:小张本月奖金500块,财务错误地加在小王工资上,还未提交,小王通过事务查询自己的工资多了500,后续财务核算进行修改,将事务进行回滚,这样员工B通过事务看到的是脏读。
 2. 不可重复读(Non-RepeatableRead)
 一个事务先后读取同一条记录,但两次读取的数据不同,我们称之为不可重复读。如果一个事务在读取了一条记录后,另一个事务修改了这条记录并且提交了事务,再次读取记录时如果获取到的是修改后的数据,这就发生了不可重复读情况。

举例:在事务A中,读取到张三的工资为5000,操作没有完成,事务还没提交。 与此同时, 事务B把张三的工资改为8000,并提交了事务。 随后,在事务A中,再次读取张三的工资,此时工资变为8000,两次读取到的结果不一致。
 3. 幻像读(PhantomRead)
 一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为幻像读。

举例:目前公司员工有10人,老板通过事务A读取公司所有人数为10人。此时招聘部通过事务B插入一条新员工的记录。 这时,事务A再次查看公司的员工人数记录,记录为11人。此时产生了幻读。

三、事务隔离级

在SQL-92标准,定义了四种隔离级别:读未提交、读提交、可重复读和串行化,以下列出四种隔离级别下系统允许/禁止哪些类型的读数据现象。

脏读 不可重复读 幻像读
读未提交 YES YES YES
读提交

NO

YES

YES
可重复读 NO NO YES
串行化 NO NO NO

其中,DM数据库支持三种事务隔离级别:读未提交、读提交和串行化,(另外DM数据库还支持只读事务,只读事务只能访问数据,但不能修改数据)读提交是DM数据库默认使用的事务隔离级别。用户在事务开始时,使用以下语句可设定事务隔离级别。

1.查看事务的隔离级别

select isolation from v$trx;--0:读未提交,1:读提交(默认)、2:可重复读、3:串行化

2.设定读未提交隔离级:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
假设:事务甲读,事务乙写,两个事务互不干扰,互不等待,这样事务乙还没提交的事务,事务甲就读到,这样虽然并发性最高,但是会造成脏读。会造成用户A在半夜的时候,发现自己的账号莫名多了巨款,一整夜想着跑车洋楼,却是美梦一场。
--事务甲
select "monkey" from "TEST1"
where ID=001;
--事务乙
update "TEST1" set "monkey"="monkey"+100000 where ID=001;

--事务甲
select "monkey" from "TEST1"
where ID=001; ---可以查看到乙未提交的数据
3.设定读提交隔离级(达梦数据库的默认级别)
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
假设.事务甲读,事务乙写的时候,只要未提交,甲再读的时候,数据还是不会变。但是只要事务乙写完提交后,事务甲读的时候,获取的记录会读到该表,这就是不可重复读。
---事务乙:
update "TEST1" set "monkey"="monkey"+100000 where ID=001;
update "TEST1" set "monkey"="monkey"+100 where ID=002;
---事务甲:
select * from "TEST1";
---事务乙:
commit;
---事务甲:
select * from "TEST1";
4.设定 串行化隔离级:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
串行化隔离级是最高的隔离级别,所有的事务依次执行完毕,各个事务之间完成不可能产生干扰。该级别可以防止脏读、不可重读以及幻读具体来说,当一个串行化事务试图更新或删除数据时,而这些数据在此事务开始后被其他事务修改并提交时,DM 数据库将报“串行化事务被打断”错误。
假设:事务甲读,事务乙写的时候,不管事务是否提交,甲再读的时候,数据还是不会变。
---事务甲
select * from "TEST1";
---事务乙
insert into "TEST1"("ID", "monkey", "name") 
VALUES(025, 7000,'V');
commit;
---事务甲
select * from "TEST1";----此时查询到的记录和之前一致,并没有因为乙添加了记录发生改变

假设:甲和乙修改同一条数据

---事务甲
update "TEST1" set "monkey"="monkey"+100 where "name"='B';
commit;
---事务乙
update "TEST1" set "monkey"="monkey"+100 where "name"='B';---执行失败,提示"串行化事务被打断"

设定事务为只读事务:

SET TRANSACTION READ ONLY;

在实际的生产业务中,DM和Oracle、SqlServer相同,大多数数据库选择读提交作为默认的隔离级别,使用读提交隔离级别可以满足大多数应用需要。如:在网上购买余数不多的高铁票时,首页显示为数不少的票数,在付款时,却提示余票0,虽然有不可重读的情况,考虑到节假日一票难求,这也是符合实际。读未提交,对于数据的严谨性,明显不符合逻辑,在访问只读表和视图的事务,以及某些执行 SELECT 语句的事务(只要其他事务的未提交数据对这些语句没有 负面效果)时,可以使用读未提交隔离级。既然串行化隔离级是最高的隔离级别,如果选择串行化隔离级别,效率会非常低,需要充分考虑到并发性。如果银行和高铁使用串行化隔离级,那几乎所有的人都要到窗口办理业务。

至于Mysql为什么选择可重复读作为默认的隔离级别?至于MySQL默认隔离级别是可重复读,只要还是因为历史原因,5.1版本之前,Mysql的binlog(二进制)类型Statement是默认格式,即依次记录系统接受的SQL请求;5.2版本之后,MySql提供了Row,Mixed,Statement三种Binlog格式,使用读已提交隔离级别,会出现bug,因此MySql将可重复读作为默认的隔离级别。

四、事务的封锁机制

1.锁概念

在高并发的情况下,数据库多个事务同时存取同一数据时,若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性。DM采用了多版本并发控制(MVCC) 和封锁机制,防止并发事务修改同一数据,保护数据一致性。

2.锁模式

锁模式指定并发用户如何访问锁定资源。

DM8数据库使用4种不同的锁模式:共享锁、排他锁、意向锁(意向共享锁和意向排他锁),值得注意的是,在ORACLE中,还存在共享意向排他锁。

共享锁(简称S锁)用于只读操作。这种模式允许并发事务同时读取相同的资源,但是不允许任何事务修改这个资源。

排它锁(简称X锁)用于修改数据的操作,如插入、更新和删除。当某个事务占用某个资源上的排他锁时,其他事务就不能修改此数据,这种锁模式防止并发用户同时更新相同的数据,否则会造成数据不一致或者不正确的现象。

共享锁和排他锁的相容性是比较差的。一般锁定的粒度越大,需要锁定的对象就越少,可选择性就越小,并发度就越低,开销就越小;反之,锁定的粒度越小,需要锁定的对象就越多,可选择性就越大,并发度就越大,开销就越大。意向锁解决了共享锁和排他锁相容性差的问题。

意向锁的含义:如果对一个结点加意向锁,则说明该结点的下层节点正在被加锁;对任一结点加锁时,必须先对它的上层结点加意向锁。如下图:如果对行1~行n中的某一行上锁时,必须先对表1 加意向锁,然后再对改行加锁。这样一来,事务对表加锁时,就不用再检查表中每行记录的锁标志了,提高了系统效率(图摘自Oracle)

意向共享锁(Intent Shared Lock,简称IS锁):一般在只读访问对象时使用。

意向排他锁(Intent Exclusive Lock,简称IX锁):一般在修改对象数据时使用

3.锁的兼容性
在执行SELECT、INSERT、DELETE、UPDATE等DML语句时, 隐式上意向锁,查询上 IS锁,插入、删除和更新行存储表上 IX锁,列存储表上X锁。DM 的 隐式封锁足以保证数据的一致性,但用户可以根据自己的需要 手工显式锁定表
给表 "TEST1" 加共享锁
LOCK TABLE "TEST1" IN SHARE MODE;
给表 "TEST1" 加排他锁
LOCK TABLE "TEST1" IN EXCLUSIVE MODE;
给表"TEST1"加意向共享锁
LOCK TABLE "TEST1" IN INTENT SHARE MODE;
给表"TEST1"加意向排他锁
LOCK TABLE "TEST1" IN INTENT EXCLUSIVE MODE;

前面说到共享锁和排他锁的相容性是比较差的,关于锁的兼容性如下(“Y"代表相容,”N”代表不相容),可以从以下图锁的兼容性看到,当表TEST1上了排他锁(X锁),其他事务将无法查询(IS),插入、删除和更新(IX表TEST1。

2.锁粒度

锁定对象的大小被称为封锁的粒度,Oracle中重点关注行级锁(TM锁)和列级锁(TX锁),根据锁定对象的不同,DM锁可以分为 TID 锁和对象锁。

TID 锁:以事务号为封锁对象,为每个活动事务生成一把 TID 锁, 代替了其他数据库行锁的功能。
防止多个事务同时修改同一行记录。 每个事务启动时创建一把独占的TID锁,锁模式为X锁,并持有到事务结束。 DM8实现的是行级多版本,每一行记录隐含一个 TID 字段,用于事务可见性判断,协调行级的多版本并发控制
对象锁:DM 新引入的一种锁,通过统一的对象 ID 进行封锁,将对数据字典的封锁和表锁合并为对象锁,以达到减少封锁冲突、提升系统并发性能的目的。
3.多版本控制(MVCC)
多版本控制,对数据的每次更新形成数据的新版本,老版本保存在回滚段中。 读操作将不需要上锁,读操作与写操作不会相互阻塞,并发度大幅度提高。 如果数据库仅通过锁机制来实现并发控制,数据库对读操作上共享锁,写操作上排他锁,这种锁机制虽然解决了并发问题,但影响了并发性。例如,当对一个事务对表进行查询时,另一个对表更新的事务就必须等待。DM 数据库的多版本实现完全消除了行锁对系统资源的消耗,查询永远不会被阻塞也不需要上行锁,并通过 TID 锁机制消除了插入、删除、更新操作的行锁。数据库的读操作与写操作不会相互阻塞,并发度大幅度提高。
4.相关动态视图
显示会话的具体信息:V$SESSIONS 
显示所有活动事务的信息:V$TRX
显示事务等待信息:V$TRXWAIT
显示活动事务视图信息:V$TRX_VIEW
显示当前系统中锁的状态:V$LOCK
显示死锁的历史信息:V$DEADLOCK_HISTORY

英国物理学家威廉·汤姆孙,在1900年4月27日的英国皇家学会新年庆祝会的演讲中说过:“物理学的大厦已经基本建立,未来的物理学家只需要做些修修补补的工作就行了”,随即爱因斯坦相对论的提出打破了这一结构。面对高并发,数据库是否有新的应对方式,值得期待。

(如有错误,欢迎指正)

更多内容可查看达梦技术文档 (dameng.com)https://eco.dameng.com/document/dm/zh-cn/start/index.html

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到