Transaction 2 |学习笔记

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 快速学习 Transaction 2

开发者学堂课程【高校精品课-上海交通大学-企业级应用体系架构:Transaction 2 】学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址:https://developer.aliyun.com/learning/course/75/detail/15832


Transaction 2

 

内容介绍

、Managing Transactions in Spring

、Database Locks

、Transaction Isolation Levels

、Controlling isolation levels

、Updating Multiple Databases

 

一、Managing Transactions in Spring

Transactional 在一个事务里执行接收一个字符串类型的数组叫做可变常数组的参数可以传若干个字符串对于数组里的每一个数组都把当成一个人数据库里面插入一条记录插入的就是 person 的内容有一个名字,把替换成值插入到数据库里面,数据库里面有一个叫 bookings 的表

 image.png

bookings 表有 first_name有 id自动生成有约束长度只能是5,拿到的人一个一个的插入到表里面query

 image.png

写一个命令行 runner是命令行里运行的一个程序,run 调 bookingservice.book,传递进去alicebobcarol五个字符在 book 方法里都不会违反小于等于五个字符的约束,都可以到数据库里用一个断言判断如果findAllBookings 会回所有里面已经预定人的 first_name因为前面插入三条,所以 Size一定等于三再尝试处理,但是要注意,现在处理会抛异常book 现在调用的是 chris 和 samuelchris 是满足条件的能够写入到数据库里面是 samuel 是六个字母所以写不到数据库但是 book 方法是对于 person 里的所有人是一个事务,在一个事务里执行所以因为 samuel 不整个事务异常下面可以打印出消息是什么原因但是 samuel不成功chris 也不会成功所以 chris 满足要求只有五个能插入,但是因为俩在一个事务中执行所以 chris 进不

image.png

在执行完前面的操作之后,再看里面有哪些已经订阅的人看不到 chris 和 samuelsamuel 违反约束而chris是因为事务samuel在同一个事务插入,所以没有被插入到数据库里很明确的告诉在sping里面现在事务如何执行再预定一个再传一个buddy第二个为空会看到现在仍然会抛异常仍然是空,没法处理因为空和 buddy 在一个事务里执行仍然看不见 buddynull 违反数据库约束而 buddy 一个事务里执行所以回滚可以看到 alicebobcarol不是回滚看不见 chris 和 samuelsamuel 违反数据库约束chris回滚下面为 buddy 和空做操作但是下面因为异常抛出null 因为违反数据库约束buddy 是因为在同一事务里回执行这么多操作只有前面的三个成功关键在于每一次Book 的时候都是默认是在一个事务里执行调三次,第一次的三个人在一个事务里,整个提交三个人都在后面两次都因为有一个人每一次都传两个人,但都有一个人不符合要求,违反约束所以导致另外一个人一起都没有被进去告诉事务的用处数据库的脚本pom文件,spring-boot-starter-data-jdbc,spring 官网下载的例子很明显的告诉什么叫事务控制以及事务如何提交

或者回性的问题Atomic op 事务的边界在哪里,从哪里开始到哪里结束第二个问题是事务的隔离性问题

@Transactional isolation

事务隔离级别

说明

@Transactional(isolation = Isolation.READ_ UNCOMMITTED)

读取未提交数据(会出现脏读,不可重复读), 基本不使用

读取未提交数据(会出现脏读,不可重复读), 基本不使用

读取已提交数据(会出现不可重复读和幻读)

@Transactional(isolation = lsolation.REPEATABLE_ READ)

可重复读(会出现幻读)

@Transactional(isolation = Isolation.SERIALIZABLE)

串行化

解决三个问题脏读问题不可重复读问题幻读问题第一个级别是指三个问题都存在第二个级别是把脏读问题解决掉剩下两个问题不存在不可重复读幻读问题存在串行化是三个问题都解决掉,串行化意味着根本没有并发也意味着性能极差虽然安全性很高,但是性能极差所以事务隔离级别设的越高,如果定义越高数据的一致性可靠性越好,但是数据库被访问的效率会越通常可能在 read_committed 级别上比较多的都不太会出现,可能中间两个用的比较多另外两个不太会用,因为第一个太不安全,最后一个性能太差

在 spring 里面有描述Transactional 标签描述

@Transactional propagation 传播

事务传播行为

说明

@Transactional(propagation=Propagation.REQUIRED)

如果有事务,那么加入事务,没有新建一个(默认情况)

@Transactional(propagation=Propagation.NOT SUPPORTED)

容器不为这个方法开启事务

@Transactional(propagation=Propagation.REQUIRES_ NEW)

不管是否存在事务,都创建一个新的事务,原来的挂起,新的

@Transactional(propagation=Propagation.MANDATORY)

执行完毕,继续执行老的事务

@Transactional(propagation=Propagation.NEVER)

必须在一个已有的事务中执行,否则抛出异常

@Transactional(propagation=Propagation.SUPPORTS)

必须在一个没有的事务中执行,否则抛出异常(与

image.png

数据库隔离是靠数据库锁实现的有两个分离的客户端要访问同一个数据库执行的代码访问数据库select 操作得到里面的结果,假设两个客户端执行的方法定义的事务属性访问同一个数据库,通过自己的方法访问两个方法的事务属性都是required要求必须在数据库执行客户端a和客户端B都要调用 listavailablecabins 方法预定 book预定船里面的仓位邮轮仓位先把仓位里面现有的船仓位拿出,在上面再买一个所以要告诉订多少个床数据库里提交结果做处理一个动作如果有两个客户端在执行会出现什么情况都要在事务里执行,事务都是 require看上面有哪些船船舱可以定正在看时有另外一个事务也要做订票动作现在有哪一些船舱可以用订两张票两张床能不能订到在做定的操作操作刚开始定比如船上现在有两张床位要开始定在没有两张床还没有给问问能不能订到两张床位于是数据库里现在二还没有分配给 client1,于是告诉可以手速比较快事务提交掉client1犹豫半天比较慢或者是因为网络的原因,当最后付款时候两张床位已经被client2预定所以事务只能回滚脏读问题现在有没有两张床client1还没有提交的数据被 client2读到读到之后会产生问题解决问题就要锁住数据库里的数据读走的数据直接被解决所以没办法再操作数据被读着事务还没有提交的时候被别人给改写掉

image.png

第二个问题不可重复读问题现在有没有两张床发现有两张做动作注意前面是表示时间的顺序,把床位给设成三张,因为有人可能释放了一张床位现在可以设置三张做一系列动作之后,决定要定两张床保险期间再看看有没有两张床于是会觉得选哪三张不可重复是要看的东西,没有改在数据库里面插入符合搜索条件的记录每一次看都会看到不同的符合条件的记录也不知道基于谁做相应的后续操作想读的数据加锁锁住,不能改但是可以插入新的数据数据也符合的搜索条件,所以每一次搜的时候会发现里面数据会在增多增多,变化所以不知道该如何

image.png

读问题开启一个事务有两张床,做操作读走数据,进行订阅的动作把数据给写进去会有订阅的消息写进在客户端执行了一部分之后下订单,左边操作跟之前的没有区别隔一段时间之后,下两个订单右边在里面可能改写一个数据,数据会使边数据发生变化读问题是做操作事务提交之后会发现在里面开始确实读到一些数据发现数据没或者会发现增加两个方向都会有第一个问题是读到事务读走的数据,并且人正在进行修改把中间状态给改第二个把数据给但是不读可以往里面插入符合条件数据,会发现数据在不断的变多幻读问题是当里查询条件的时候,事务的数据但是真正做操作的时候,可能又回

image.png

 

二、Database Locks

1、Read locks

Read locks prevent other transactions from changing data read during a transaction until the transaction ends, thus preventing nonrepeatable reads.

Other transactions can read the data but not write to it. The current transaction is also prohibited from making changes.

2、Write locks

Write locks are used for updates. A write lock prevents other

transactions from changing the data until the current transaction is complete but allows dirty reads by other transactions and by the current transaction itself.

In other words, the transaction can read its own uncommitted changes.

一个事务在读走之后的数据在没有提交之前允不允许别人看如果允许别人看会有脏读问题在上面进行锁,在数据库里有数据原始数据客户端读回变成a在中间状态修改的时候别人读数据仍然只能读到A不能读到a别人在开一个事务再进行处理 a 的数据处理成的时候,数据还没有如果让别人读到会出问题a的账户是十块钱读走之后要做转账操作,变成十五如果十五能够被别人人想做转账操作也是转五块读到15块会转个20块因为某种原因事务不是提而是 roll back因为会把15中间状态读走加锁操作变成20所以写回来的时候变成20全局一个 rollback相当于做一次转账操作里面应该是15而不是20所以会发现数据确实是有问题不能够读别人正在处理的东西会在数据库上加一个锁告诉别人在事务没有结束不管提交还是回滚之前都没有办法把数据中间状态给读走只能读走不能写只能读走现在里面状态十块钱不能把15直接给写回,一直要到事务回滚或者提交掉之后才能做动作事务读走做转账操作变成15在没有结束之前用户想把a十块钱比如加五块钱,做转账五块钱的操作,写不回因为事务在读的时候,加事务在读十回的时候只能读不能改,防止在改所以最多把十读走想改成15回写写不因为锁着如果是rollback把锁放开,15可以写进,没问题,如果15不进行 rollbackcommit 变成15,把锁释放掉得到锁但是在提交15的时候,不能提交,因为当时读走的全是十块钱现在已经变成15要写回的时候检测出读走的值和现在的值是不一样,是基于一个已经旧的值在做转账的操作加五块钱过时的值,所以不允许写回加锁加上事务提交时比较事务里面的当前值事务读走之后的值不会有两个事务在一起写一个数据,能检到有一些事务读取过右边事务想把数据写回的时候之前已经有人把数据库的值给改写掉,可以检测出要做的事情写锁是一直到当前的事务完成是不允许其事务修改表但是允许其事务当前的状态事务读完之后不允许往回写锁读锁弱一点不能把数据写回和读一样但是当事务在读的时候还是可以读到未提交的状态在防止不可重复读对的问题自然也没有脏问题防止可以读到别人没有提交的修改,但是不让写,会发现有问题,也是会读到别人的提交问题,但是基于做的修改是没有办法写的所以也可以保证事务的隔离

3、Exclusive write locks :

Exclusive write locks are used for updates. An exclusive write lock prevents other transactions from reading or changing the data until the current transaction is complete.

It also prevents dirty reads by other transactions.

独占锁如果有一个事务读数据其它的数据既不能读,也不能改数据独占把数据锁住只能自己能读能写其他人不能读不能写

4、Snapshots

A snapshot is a frozen view of the data that is taken when a transaction begins. Some databases get around locking by providing every transaction with its own snapshot.

Snapshots can prevent dirty reads, nonrepeatable reads, and phantom reads. They can be problematic because the data is not real-time data; it is old the instant the snapshot is taken.

把数据做视图,视图冻结住在别人在读数据的时候,只能读当前的数据只有当前的事务可以做相应的处理锁的机制是数据库里面

 

三、Transaction Isolation Levels

四个隔离级别

1、Read Uncommitted

The transaction can read uncommitted data (i.e., data changed by a different transaction that is still in progress).

Dirty reads, nonrepeatable reads, and phantom reads can occur.

Bean methods with this isolation level can read uncommitted changes.

可以读到对方事务里面还没有提的数据如果没有提交数据的事务最后回滚意味着当前的事务基于操作完全没有意义因为数据压根不存在,回滚掉了

2、Read Committed

The transaction cannot read uncommitted data; data that is being changed by a different transaction cannot be read.

Dirty reads are prevented; nonrepeatable reads and phantom reads can occur.

Bean methods with this isolation level cannot read uncommitted data.

不能读没有提交的数据,只有提交的数据才可以读不会读到一些压根不存在的数据,没有加任何只是对当前正在操作数据,数据库没有提交意味着正在开始不是在客户端,cache 持久化到硬盘上commit只有提交掉才能读到正在处理中间状态如果只存数据,别人不是在改写读走的数据,而是在数据库里面直接插入其想要符合条件记录

3、Repeatable Read

The transaction cannot change data that is being read by a different transaction.

Dirty reads and nonrepeatable reads are prevented; phantom reads can occur.

Bean methods with this isolation level have the same restrictions as those in the Read Committed level and can execute only repeatable reads.

可以重复表上面做一些写的操作另外一个 transaction 即使不做写操作,只是读也要加一个锁上让其的数据不能做修改不能往表里面做相应的插入动作进行操作的时候数据现在被拿走进行操作另外一个事务在里面只能读到提交的事务数据提交的数据写入到硬盘里才可以,没有办法直接进去不可重复读的问题,设置成隔离级别之后的样

设置成 Repeatable Read,数据只读没有办法改写的值只是读不更新里面的内容也不让其它的事务在里面进行改写在后面插入新的值再读时会发现还有不同条件出现加锁把表锁住 

4、Serializable

The transaction has exclusive read and update privileges; different

transactions can neither read nor write to the same data.

Dirty reads, nonrepeatable reads, and phantom reads are prevented.

This isolation level is the most restrictive.

但要锁表还要把所有的数据都锁住隔离级别会更高,加锁机制更强性能更差


四、Controlling isolation levels

1、You to specify the transaction isolation level using the

database's API.

2、For example:

DataSource source = (javax . sql .DataSource)

jndiCntxt . lookup("java: comp/ env/jdbc/titanDB");

Connection con = source . getConnection( );

con. setTransactionIsolation(Connection. TRANSACTION_ SERIALIZABLE);

如何做隔离级别的设置begin 和 commit rollback 再划分事务的边界没有看到过 isolation无论是source方式还是其他方式一个数据源上获取连接的时候在连接上可以设事务隔离级别隔离级别设的四种之一用spring可以在声明文件里面设,TRANSACTION_ SERIALIZABLE 对性能的损耗非常大力度非常强的锁把数据库表锁死

 

五、Updating Multiple Databases

 image.png

做转账操作X 是中国工商银行的数据库y 是中国银行的数据库事务控制该如何控制事务两个数据库服务器即便是在同一台数据库服务器比如都是 orade建两个 schema,两个库,也有同样的问题两个库俩内存是隔离的它有它的缓存在缓存事务执行的中间状态从中国工商银行的账户里面存一笔钱,中国银行的账户里面取一笔钱在中国工商银行的账户里面存一笔钱在中国银行的账户里面存钱时是操作缓存在工商银行账户里面取钱的时候提交或者回事务它俩之间不通,左边告诉可以提交右边提交不了必须回滚但是俩之间又不通所以要做判断是否可以提交分布式事务是在事务当中涉及到对两个数据库的操作两个资源管理器之间,数据库是 jms sever数据库里写数据之后再邮件服务器上顺便发个邮件出也是一个事务事务对两个资源管理进行操作只要是对两个或者两个以上的资源管理操作,分布式事务问题在于资源管理器之间不能直接通信整个事务提交还是回滚要靠代码进行实现

 image.png

一个 bean 操作两个数据源另外一个操作第三个数据源整个在一个事务里面执行靠事务的propagation传播六个事务属性之一进行事务的传播,导致俩在一个事务执行涉及到三个数据交互只要有一个不能成功,其它都成功,要让整个事务进行回滚

image.png

两阶段提交协议在第一个阶段是问所有的资源管理器不再说是数据库第一个阶段叫准备阶段能不能提交准备提交,如果 unprepared 没准备好会给两边都发 rollback都回滚可以保证事务是一致的,不会出现一个提交,一个没提交情况如果上面提交下面没提交,不满足事务的原则性,变成一部分操作提交部分操作没提交不管是否顺备好做出一个决策提交或者回,发请求出去不管是提高还是回比如工商银行正确的做一个动作给下面发的时候网络断了永远发不过下面收不到准备好等着第二个动作过等不到因为网络断事务上有 timeout要么提交要么回滚把债务资源释放掉准备好认为能提交或者告诉没准备好认为不能提交做一个猜测,有50%的概率猜错比如没提交好回滚猜测的概率比较小,如果准备好经过处理做出的决策是rollback结果 rollback 发不过按照自己的想法提交,于是猜错会有50%的概率会猜错一旦猜错事务就不完整了两阶段提交协议也可能会碰上样的问题在第二阶段的时候再有一方通信或者几方通信,断掉最终提交或者回滚动作完全是基于自己的想法,做一个启发式的动作动作有一定概率失败猜错数据不完整人工干预相当于拿着一张银行卡到提款机上比如在中国银行提款机上插入一张工商银行的卡再进行取钱发现涉及到两个数据库发现有一条断发现的钱被扣但是钱没有吐出会打印一个条点几分钱没提出,拿着条找银行,银行会处理程序没办法自动处理启发动作,也不知道要人检查两阶段提交协议里面解决不了的问题潜在有缺陷

Concurrency

Since we use O/R mapping, and it is an offline way to access database, we should manage the concurrent access to data.

The core is how to prevent confliction between business

transactions.

Locking is the solution

What kind of lock?

事务要处理并发有大量的事务在并发的执行如何做隔离以及如何划分事务边界如何加锁

Optimistic Offline Lock

Optimistic Offline Lock solves this problem by validating that the changes

about to be committed by one session don't conflict with the changes of another session.

加锁分两种一种叫乐观锁有数据库有两个会话都在操作数据并且都在操作129的客户martin读走数据在进行编辑的过程当中david 也读走数据david 也进行编辑david 的手快先提交martin在提交时会发现现在提交的用户数据在读走的那一刻和现在数据库里的数据不一致,基于一个陈旧的数据在做修改所以事务只能回滚没有在 martin 读走129之后,没有对129做锁定的动作下产生的问题乐观是如果对129做写操作david读不走129,性能太差如果系统里面大量的操作都是读操作,为什么不让 david 读走里面都在写乐观是认为冲突发生的比较少所以不应该这样写martin 读走之后davi 仍然能把它读取仍然能写,在数据库里不加锁在当前的场景产生冲突的概率比较小大量操作都是读操作概率小加一个乐观只要检测出来martin写回数据的时候当时读走的129状态和系统的129状态不一样可以

Optimistic Offline Lock- How It Works

An Optimistic Offline Lock is obtained by validating that, in the

time since a session loaded a record, another session hasn't

altered it.

It can be acquired at any time but is valid only during the system transaction in which it is obtained.

Thus, in order that a business transaction not corrupt record data it must acquire an Optimistic Offline Lock for each member of its change set during the system transaction in which it applies changes to the database.

The most common implementation is to associate a version

number with each record in your system.

With an RDBMS data store the verification is a matter of adding the version number to the criteria of any SQL statements used to update or delete a record.

Our data is stored in a relational database,

so each table must also store version and modification data.

Here's the schema for a customer table as well as the standard CRUD

SQL necessary to support the Optimistic Offline Lock:

table customer...

create table customer(id bigint primary key, name varchar, createdby varchar, created datetime, modifiedby varchar, modified datetime, version int)

SQL customer CRUD...

INSERT INTO customer VALUES (?,?,?,?,?,?,?)

SELECT * FROM customer WHERE id= ?

UPDATE customer SET name = ?, modifiedBy= ?, modified= ?, version= ? WHEREid= ? and version = ?

DELETE FROM customer WHERE id= ? and version = ?

解决问题,在 customer 表里面增加几个字段,第一个版本号只要被改写过,累加一还可以做得更细致一点,被谁改写上一次改写的时间是什么这是可选的但是版本号必须要有,当把一个数据往回写的时候,首先看版本号是否一致读走c ustomer 版本号1,现在写回传回来版本号是1,检查数据库里的版本号是不是1,如果也是1,认可读走之后到现 在写回来之前,没有人改写过把写的数据写进版本号写成2,david发生的事情david改成2之后martin 改完版本号变成1,12之间不匹配martin 返回的数据里面版本号是1而现在数据库里面版本号是2,所以有冲突告诉 martin 有问题数据库不加锁,靠版本号检测真正的锁是当martin读走129用户的时候做改写过程当中david 读不走,只要一读报错冲突的概率比较大不频繁发生冲突问题,加锁让读不走不会产生写冲突,悲观锁

Pessimistic Offline Lock-How It Works

You implement Pessimistic Offline Lock in three phases:

determining what type of locks you need, ,

building a lock manager,

and defining procedures for a business transaction to use locks.

Lock types:

exclusive write lock

exclusive read lock

read/write lock

Read and write locks are mutually exclusive.

Concurrent read locks are acceptable.

In choosing the correct lock type think about maximizing

system concurrency, meeting business needs, and minimizing

code complexity.

Also keep in mind that the locking strategy must be understood by domain modelers and analysts.

悲观写锁独占解锁独占读锁悲观锁靠设置数据库隔离级别实现通过版本号的方式实现用哪一个取决于数据模式只要在性能和可用性冲突之间做权衡有一个 customer有一对多有多个地址,比如家庭住址等是否把 Customer 数据以及所有的地址加入比如加载一个 Customer对应的所有的 address都应该被锁住同时反过如果在读走某一个地址的时候,Customer 关联的其address 全部写入靠数据库实现不数据库在单独一张表,一次性全锁住粗略的锁

With Optimistic Offline Lock, having each item in a group share a version creates the single point of contention, which means sharing the same version, not an equal version.

Incrementing this version will lock the entire group with a shared lock.

Set up your model to point every member of the group at the shared version and you have certainly minimized the path to the point of contention.

粗略锁有乐观和悲观之分,如果是乐观没有customer address 共享相同的版本号无论是从customer或者address 过来改写哪一端比如改写 address 就把 version 加1,customer 就是新的模式版本号对象是共享可以解决问题并发的乐观的

A shared Pessimistic Offline Lock requires that each member of the group share some sort of lockable token, on which it must then be acquired.

As Pessimistic Offline Lock is often used as a complement to Optimistic Offline Lock, a shared version object makes an excellent candidate for the lockable token role.

image.png

悲观是在数据库里真有一张通过外界关联 customer address正在锁的时候,把version表锁住当获取customer 或者 address 必须同时把相应 version 里面取不出来就报错悲观的粗略的锁粗略的锁要解决问题就是一次性要把多张表的数据锁住乐观锁是不加锁靠版本号解决粗略的锁要靠额外的一张版本表,或者一个版本号对象再把多表里面的数据同时锁定两个东西都不是数据库默认直接能提供的要靠大家实现

References 参考文

Web Applications: What are They? What of Them?

http://www.acunetix.com/websitesecurity/web-applications

The Java EE 8 Tutorial - Transactions

https://javaee.github.io/tutorial/transactions.html# BNCIH

Managing Transactions

https://spring.io guides/gs/managing-transactions/

spring 例子在官网上有

Transaction 六种属性一般对应哪些应用不是对应哪些应用是对应具体的某一个需求比如转账的时候要记日志本身记日志会不会影响到事务完全取决于自己的设计比如日志系统做得非常稳定,希望日志成为事务的一部分把它加入到事务如果系统本身不太稳定,有可能是一个性能瓶颈异步记日志本身不要成为转账事务的一部分它变成 REQUIRES_ NEW每一个应用里面可能在不同的功能点上对应着不同的事务数字事务属性是利用服务器或者它们管理事务的边界或者人为的通过编码的方式做也可以session 上面开一个事务操作完之后把事务提交掉六个属性基本上已经能覆盖所有能想象到的场景至于具体哪个场景要如何用,取决于应用本身功能点上是如何设计的

相关文章
|
6月前
|
SQL 关系型数据库 MySQL
十四、事务Transaction
十四、事务Transaction
70 0
|
5月前
|
Go
【已解决】SendTransactionVM Exception while processing transaction: Transaction‘s maxFeePerGas (200000000
【已解决】SendTransactionVM Exception while processing transaction: Transaction‘s maxFeePerGas (200000000
40 0
|
4月前
|
算法 关系型数据库 MySQL
transaction
【7月更文挑战第21天】
61 7
|
设计模式 Java 数据库连接
Transaction接口|学习笔记
快速学习Transaction接口
195 0
Transaction接口|学习笔记
|
消息中间件 缓存 Oracle
Transaction 1 |学习笔记
快速学习 Transaction 1
116 0
|
SQL 存储 安全
MySQL数据库(27):事务安全 transaction
MySQL数据库(27):事务安全 transaction
142 0
|
Python
sqlalchemy报错Please use '@@transaction_isolation' instead")
sqlalchemy报错Please use '@@transaction_isolation' instead")
143 0
|
Java 数据库连接
JDBC(七)事务Transaction
事务Transaction是一组要作为单一的原子动作进行的行为。 要么执行所有的操作,要么都不执行。 我们可以通过它来调用事务: connection.setAutoCommit(false); 如果在事务中间出现失败,就需要对事务进行回滚 connection.rollback(); 如果所有操作都没有失败,那最终需要提交。
886 0
|
Java 关系型数据库 数据库连接