精华内容
下载资源
问答
  • 一致性(Consistency):这个一致性的意思是如果执行一个事务,那么事务执行之前和执行之后必须是从一个一致性状态转变到另一个一致性状态。那什么是一致性状态?举个例子,假如小明从银行卡转了1000元钱到支付宝,...

    a2d42840a1642c4b8d5fdfb62f1cfad8.png

    图片来源:pexels.com

    事务的基本概念

    事务是数据库里面的一个基本概念。

    所谓事务,就是一系列的数据库操作。注意是一系列的操作,这些操作要么全做,要么一个也不做,这是一个不能被分割的单位。事务处理可以维护数据库的完整性。

    在关系数据库中,一个事务可以是:

    • 一条SQL语句
    • 一组SQL语句
    • 整个程序

    事务和程序是两个概念。一般来讲,一个程序中往往会包含多个事务。

    SQL中,定义事务的语句一般有三条:

    • BEGIN TRANSACTION;
    • COMMIT;
    • ROLLBACK;

    MySQL中,是如下三条:

    • START TRANSACTION;
    • COMMIT;
    • ROLLBACK;

    事务往往是从BEGIN TRANSACTION开始,以COMMIT或者ROLLBACK结束。

    COMMIT代表提交操作,即提交事务的所有操作,也就是说将事务中那些对数据库的更新同步写到磁盘上去。对于一般的SQL语句来讲,都是直接针对数据库表执行和编写的,也就是说SQL语句执行后会自动同步更新到磁盘上,这也被称为隐含提交(implicit commit),而在事务中,提交不会自动完成,为了进行明确的提交,需要使用COMMIT语句。

    ROLLBACK代表回滚操作,即在事务的执行过程中如果遇到问题,不能够正常执行下去,那么就回滚到事务执行前的状态。这也就体现了事务的原子性:要么全部执行,要么全执行。

    事务的特性

    事务具有4个特性:

    • 原子性(Atomicity):原子性就是说明事务中包括的那些所有操作,要么都做,要么都不做,省的发生问题。
    • 一致性(Consistency):这个一致性的意思是如果执行一个事务,那么事务执行之前和执行之后必须是从一个一致性状态转变到另一个一致性状态。那什么是一致性状态?举个例子,假如小明从银行卡转了1000元钱到支付宝,简单抽象为两个操作,将银行数据库中小明的余额减少1000,然后将支付宝数据库中小明的余额增加1000。这是两个操作,但是一个事务。如果转钱成功,那么就从一个一致性状态转移到了另一个一致性状态,是没有问题的。而如果银行那边少了1000元,支付宝这边却没有加上1000,那就不一致了,就遇到了问题。这就是一种不正确的状态,此时就需要ROLLBACK回滚,回滚到银行那边没有减钱时的状态。
    • 隔离性(Isolation):一个事务的执行不能被其他事务干扰。也就是说一个事务的内部操作及使用的数据对其他并发事务是隔离的,互不影响。
    • 持续性(Durability):持续性指的是,一个事务一旦提交,它对数据库中的数据改变就是持续性或者永久性的。接下来的其他操作或者故障不应该对这个执行结果有影响。比如你转了1000元到了支付宝,就应该立马可以使用支付宝花出去了。

    这四个特性简称为ACID特性(ACID properties)

    事务管理的重要任务就是指保证事务的ACID特性。因为事务的这四大特性也会可能遭到破坏的,例如:

    • 多个事务并行运行时,不同事务的操作交叉执行。当然,不同事务的操作可以交叉执行,但是不能影响事务的原子性。不能说一个事务正在执行,而同时有其他事务对这个事务造成了一些影响,导致这个事务只执行了一半。
    • 事务在执行过程中遇到不可抗力被强行停止。比如正在往数据库写入数据,突然断电了,数据只写了一半,事务被强行停止。

    遇到如上的情况时候,就需要对数据进行恢复了,将数据库恢复到正确的状态。

    数据库恢复

    在实际应用中,数据库可能会出现各种各样的问题,有可能数据库本身遭到破坏,也有可能数据库没有受到破坏,但是数据出现不正确。当出现这些问题的时候,我们肯定需要将数据库恢复到正常,能够正常使用,而实际上,数据库恢复的基本原理很简单,用一个词就能概括:冗余。也就是建立冗余数据,说的好听点就是搞一点正常情况下多余的数据,用来帮助恢复数据库。比如对数据库直接进行备份,那么如果数据库遇到问题,直接把备份数据加载使用就行。或者登记一些日志文件,这些日志文件记录进行的操作、遇到的问题等。恢复的时候通过分析日志文件进行数据库恢复。在实际数据库系统中,这两种方法往往是一起使用的。其中对数据进行备份也叫做数据转储,数据转储是数据库恢复中采用的基本技术,也就是数据库管理员定期的将整个数据库的数据复制到硬盘等存储介质上保存起来的过程,这些备用的数据也叫做副本。但是这些数据库恢复方法都只能将数据库恢复到故障发生前的某一个确定状态。当数据库遭到破坏的时候,可以将后备副本重新装入数据库使用,但是重装后只能将数据库恢复到存储时的状态,要想恢复到故障发生时的状态,必须重新运行自转储以后的所有更新事务,这个时候就会结合日志文件。因为在很多企业等,数据量都是十分庞大的,因此转储是十分耗费时间和资源的,不能频繁的进行。因此需要确定适当的转储周期。而转储分为静态转储动态转储。静态转储比较好理解,就是在系统没有运行任何事务的时候,对数据进行转储。即转储操作开始的时候数据库处于一致性状态。同时转储期间不允许对数据库进行任何存取修改活动。动态转储就是指转储期间允许对数据库进行存取和修改。即转储和用户事务可以并发执行。但是动态转储后的数据不一定能保证正确有效,因此在动态转储的时候,需要建立执行的各事务对数据库的修改活动的日志文件。这样转储成功后可以结合日志文件将数据库恢复到某一时刻的正确状态。同时数据转储又有两种方式,为海量转储增量转储。前者指直接备份数据库的所有数据,后者指只更新改变的数据。如果文章对你有帮助,来个三连鼓励一下~推荐阅读二叉树及其遍历(Java)你好,操作系统35张图!超详细Maven入门文章!今天的你导jar包了吗?今天,我把某Q音乐爬了!惊,我还不会算时间复杂度!

     • end • 

    走在路上

    goldsunC

    5f58334870b6b22362094481d415da1b.png
    展开全文
  • 变大的空间在后续的transaction中可以被复用,所以不是什么大问题。 小结 以上红色标注的三个关键点巧妙地保证了事务的原子性。在同步数据到文件的时候,会分配新的页面,而老的页面虽然被释放了,但只有到下一个...

    前几篇文章主要介绍了bbolt数据文件的存储及索引格式。本文主要分析bbolt是如何实现事务的。

    由于事务具有ACID属性,所以一个事务作为一个整体,要么全部成功,要么全部失败回滚,永远不存在部分成功。关于事务的概念,网上有很多资料,就不啰嗦了。零君以前也写过一个系列的相关文章:

    分布式数据系统(系列一):基本概念

    由于事务的存在,极大的简化了上层应用开发的复杂度,因为复杂的逻辑都被数据库处理了。事务绝对是二十世纪数据库领域最伟大的发明之一。

    但是事务并不是一个天然就存在的东西,归根结底,还是需要底层数据库去实现支持事务这个概念,否则这个概念只是空中楼阁。本文就是要深入探讨bbolt是如何实现事务这个概念的,其实主要是分析bbolt是如何实现事务之间的隔离性,并保证事务的原子性。本文是这个系列的第四篇文章。

    文件锁和DB读写锁

    bbolt的事务分为只读(read-only)事务和读写(read-write)事务。顾名思义,只读事务只能查询,不能修改数据;而读写事务则既可以查询,也可以修改数据。

    使用DB.View函数可以启动一个只读事务:

    err := db.View(func(tx *bolt.Tx) error {  ...  return nil})

    使用DB.Update则可以启动一个读写事务:

    err := db.Update(func(tx *bolt.Tx) error {  ...  return nil})


    多个只读事务可以同时并行执行,甚至不同进程的只读事务也可以并行执行。但任何时刻只允许一个读写事务执行,不管是一个进程内,还是跨进程,都不允许多个读写事务针对同一个bbolt数据库同时执行。

    bbolt是如何保证在跨进程的场景下只允许一个读写事务呢?换句话说,如果一个进程正在执行一个读写事务,bbolt是如何阻止另一个进程针对同一个bbolt文件执行读写事务?答案其实很简单,就是文件排他锁(exclusive lock)

    如果需要更改bbolt中的数据,那么在打开数据文件时,需要获取文件的排他锁。排他锁需要借助于内核对象,所以只能使用系统调用来完成

    err := syscall.Flock(int(fd), syscall.LOCK_NB | syscall.LOCK_EX)

    当另一个进程打开同一个数据文件后,再次调用syscall.Flock尝试获取该文件的排他锁时,则会被阻塞,一直到其它进程释放占有的排他锁。

    注:通过flock系统调用锁只是建议锁(advisory lock),不具有强制性。另一个进程可以直接修改正在被锁的文件中的数据。

    如果只是查询数据库中的数据,则应该获取数据文件的共享锁:

    err := syscall.Flock(int(fd), syscall.LOCK_NB | syscall.LOCK_SH)

    另外,为了防止同一个进程内的多个goroutine(协程)修改同一个DB实例中的数据,bbolt则是通过DB读写锁来控制的。当用户程序通过bbolt.Open(...)打开数据文件时,会返回一个DB实例,其中就包含一个DB读写锁:

    type DB struct {  ......  rwlock   sync.Mutex   // Allows only one writer at a time.   ......

    在使用DB.Update启动一个读写事务的过程中,会获取该锁:

    // Obtain writer lock. This is released by the transaction when it closes.// This enforces only one writer transaction at a time.db.rwlock.Lock()

    在读写事务结束时,会释放该锁:

    tx.db.rwlock.Unlock()

    这里小结一下,bbolt通过两个锁(文件排他锁以及DB读写锁)实现了读写事务之间的隔离,保证了每一个事务在其执行期间总能看到一致的数据视图

    Copy-on-write
    在事务的执行期间,不管对数据做过什么修改,最后提交事务(commit)时,修改的数据才会持久到存储介质。这就意味着,在提交这个时间点之前的所有操作,都是在内存中进行,对持久存储介质上的数据没有任何影响。

    这里用到的关键技术就是Copy-on-write。当在读取数据的时候,开始其实还是直接从硬盘上读取。但是一旦要修改数据时,则会将数据所在的页面全部加载到内存中,随后的操作都是在内存中的node上进行。

    在bbolt中,page和node是两个紧密联系的概念。page是指数据文件中的一个页面,而node是page在内存中的代表,并且是反序列化后的页面。关于这一点,在后续的文章还会提到。

    因为在提交(commit)之前所做的任何修改,都只存在于内存中,所以提交之前如果发生任何错误,只需要reset内存,即可轻松rollback事务

    显然Copy-on-write对于保证事务的原子性起到了很重要的作用。但是别高兴得太早,仅仅实现了Copy-on-write还不能完全保证原子性,因为事务提交(commit)时如果发生错误处理不当,同样会破坏事务的原子性。

    接下来的一节就是讲述bbolt如何巧妙地保证了事务提交(commit)时,遇到任何情况都不会破坏原子性。

    事务提交(Commit)

    本节将揭开神秘的commit的面纱,看看它到底是如何保证了事务的原子性(atomicity)。首先来看看Commit到底做了哪些事情:

    c1ff20bd78832e603b64b95a06e99959.png

    第一步就是rebalance。当对node中的数据做了大量删除之后,那么就会尝试合并几个node为一个node。这里一个基本判断标准就是:当一个node中的数据还不足一个页面的1/4,就会尝试与别的node合并。

    第二步是spill。这一步主要是将大node分裂成一个page能容纳的小node,并将每个node序列化成最终的page格式,为后续的同步数据到文件做准备。因为bbolt采用Copy-on-write的方式,每一个内存中的node必然对应一个文件中的page。因为每一个node最终会保存到文件中的一个新的位置(page),所以会释放node之前所在的page ID,并申请一个新的page ID。这么做是非常巧妙的一个设计,是保证事务原子性的第一个关键点。具体原因后文会讲到。

    第三步是commitFreelist。其实第三步的处理逻辑与第二步完全一样,只是处理的对象不同。第二步处理的是key-value数据,而这一步处理的是freelist。

    关于freelist的具体定义以及作用,请参考本系列的前一篇文章。

    到目前为止,前面所有的步骤操作的都是内存中的数据,对数据库文件中的数据没有任何影响。

    第四步是write。这一步会依次同步内存中所有的页面到文件中。这一步会实际修改数据库文件中的数据。让我们来看一个示意性的例子。假设数据库文件中B+树的结构如下图所示。现在leaf page 8中有数据被修改了,那么从root page到当前leaf page这一条路径上的page都会被加载到内存中的node。

    f723a0462066fbe79a419e637989bba4.png

    由于每一个node会分配新的page ID,老的page ID会被释放,所以最后文件中B+树的结构会变成下面这样。背景为灰色的page被释放了,而背景为橙色的page是新分配的。

    9bdf28db89cc1e22a69cb7f37d6bef83.png

    第五步是writeMeta。就是同步meta数据到文件中。metadata中的root pageID会指向新的root page ID, freelist也会指向新的freelist pageID。

    最后一步就是关闭事务,释放DB读写锁,reset内存,从而完成了整个commit过程。

    原子性

    不难看出,commit整个过程的前三步都是操作内存中的数据,是安全的,从第四步开始会修改数据文件的内容。

    看到这里,估计很多人开始产生疑问了,在第四步中,如果由于某种意外的原因(比如断电)导致同步失败怎么办?或者一些页面同步成功,而另一些页面同步失败,又怎么办?其实前面讲到的第一个关键点保证了在第四步发生任何意外,都不会破坏硬盘文件中的数据。因为修改的页面都会保存到新的位置,也就意味着老的页面没有被覆盖,而且metadata中的root pageID和freelist pageID还是保存的老的值

    由于所有的数据会保存到新的位置,所以老的page ID都会被释放。但是,所有释放的page ID不会直接加入freelist,而只会被加入到pending列表。这就意味着,这些释放的page ID在当前transaction中还不能被复用,只有到了下一个transaction才能被重新分配使用。这就有效保证了老数据在当前transaction中不会被覆盖。这就是保证事务原子性的第二个关键点

    你可能会追问,就算第四步是安全的,那第五步呢?如果在同步metadata的时候发生任何意外怎么办?在本系列的第一篇文章中就讲过,bbolt的文件header中有两个metadata page,也就是说page 0和page 1都是metadata page。metadata page中包含txid,也就是transaction ID,这个值是单调递增的。开始的时候,两个page中的txid的值分别是0和1。

    每当创建一个读写事务时,会加载其中一个metadata page到内存中。具体加载两个中的哪个呢?很简单,选择txid较大的哪一个。但是如果这个metadata page的checksum错误,就意味着这个页面被破坏了,这时会选择另一个metadata page。但是最后同步内存中的metadata数据到文件时,又会同步到txid较小的那个metadata page。 所以会交替保存metadata数据到两个page。大致示意如下:

    In the beginning, page 0 txid: 0, page 1 txid: 1txid 3: load from page 1, save to page 0; Result txid: 2, 1txid 4: load from page 0, save to page 1; Result txit: 2, 3txid 5: load from page 1, save to page 0; Result txit: 4, 3...

    看到这里,是不是恍然大悟?因为会交替保存metadata数据到两个metadata page。所以如果同步metadata数据过程中发生任何意外,那么当前metadata的checksum肯定错误。那么后续起作用的是另外一个checksum正确的metadata page。这就是保证事务原子性的第三个关键点

    Rollback(回滚)

    不管哪一步发生错误,只需要重新加载freelist即可。之前释放的page都会被撤销。因为老数据没有被覆盖,metadata page又是双保险,所以保证了事务的原子性。唯一的副作用就是数据文件的大小可能会变大。变大的空间在后续的transaction中可以被复用,所以不是什么大问题

    小结

    以上红色标注的三个关键点巧妙地保证了事务的原子性。在同步数据到文件的时候,会分配新的页面,而老的页面虽然被释放了,但只有到下一个transaction才能被复用,所以这就保证了在当前transaction中,老的页面永远不会被覆盖。

    而metadata又有两个page,交替使用,如果最后同步metadata失败,下一次使用另一个checksum正确的page即可。

    --END--

    相关文章

    key-value数据库bbolt深入分析:简介、文件头格式

    key-value数据库bbolt深入分析:B+树

    key-value数据库bbolt深入分析:Bucket & Freelist

    展开全文
  • +-----------------------+----------------+| Variable_name | Value |+-----------------------+----------------+| transaction_isolation | READ-COMMITTED |+-----------------------+----------------+ 总结来...

    提到事务,你肯定不陌生,和数据库打交道的时候,我们总是会用到事务。最经典的例子就是转账,你要给朋友小王转 100 块钱,而此时你的银行卡只有 100 块钱。

    转账过程具体到程序里会有一系列的操作,比如查询余额、做加减法、更新余额等,这些操作必须保证是一体的,不然等程序查完之后,还没做减法之前,你这 100 块钱,完全可以借着这个时间差再查一次,然后再给另外一个朋友转账,如果银行这么整,不就乱了么?这时就要用到“事务”这个概念了。

    简单来说,事务就是要保证一组数据库操作,要么全部成功,要么全部失败。在 MySQL 中,事务支持是在引擎层实现的。你现在知道,MySQL 是一个支持多引擎的系统,但并不是所有的引擎都支持事务。比如 MySQL 原生的 MyISAM 引擎就不支持事务,这也是 MyISAM 被 InnoDB 取代的重要原因之一。

    今天的文章里,我将会以 InnoDB 为例,剖析 MySQL 在事务支持方面的特定实现,并基于原理给出相应的实践建议,希望这些案例能加深你对 MySQL 事务原理的理解。

    隔离性与隔离级别

    提到事务,你肯定会想到 ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性),今天我们就来说说其中 I,也就是“隔离性”。

    当数据库上有多个事务同时执行的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,就有了“隔离级别”的概念。

    在谈隔离级别之前,你首先要知道,你隔离得越严实,效率就会越低。因此很多时候,我们都要在二者之间寻找一个平衡点。SQL 标准的事务隔离级别包括:读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(serializable )。

    下面我逐一为你解释:

    1. 读未提交是指,一个事务还没提交时,它做的变更就能被别的事务看到。
    2. 读提交是指,一个事务提交之后,它做的变更才会被其他事务看到。
    3. 可重复读是指,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
    4. 串行化,顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。

    其中“读提交”和“可重复读”比较难理解,所以我用一个例子说明这几种隔离级别。假设数据表 T 中只有一列,其中一行的值为 1,下面是按照时间顺序执行两个事务的行为。

    mysql> create table T(c int) engine=InnoDB;insert into T(c) values(1);
    fda9e78bf57a90e43d6d2ed0057aef74.png

    我们来看看在不同的隔离级别下,事务 A 会有哪些不同的返回结果,也就是图里面 V1、V2、V3 的返回值分别是什么。

    • 若隔离级别是“读未提交”, 则 V1 的值就是 2。这时候事务 B 虽然还没有提交,但是结果已经被 A 看到了。因此,V2、V3 也都是 2。
    • 若隔离级别是“读提交”,则 V1 是 1,V2 的值是 2。事务 B 的更新在提交后才能被 A 看到。所以, V3 的值也是 2。
    • 若隔离级别是“可重复读”,则 V1、V2 是 1,V3 是 2。之所以 V2 还是 1,遵循的就是这个要求:事务在执行期间看到的数据前后必须是一致的。
    • 若隔离级别是“串行化”,则在事务 B 执行“将 1 改成 2”的时候,会被锁住。直到事务 A 提交后,事务 B 才可以继续执行。所以从 A 的角度看, V1、V2 值是 1,V3 的值是 2。

    在实现上,数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准。在“可重复读”隔离级别下,这个视图是在事务启动时创建的,整个事务存在期间都用这个视图。在“读提交”隔离级别下,这个视图是在每个 SQL 语句开始执行的时候创建的。这里需要注意的是,“读未提交”隔离级别下直接返回记录上的最新值,没有视图概念;而“串行化”隔离级别下直接用加锁的方式来避免并行访问。

    我们可以看到在不同的隔离级别下,数据库行为是有所不同的。Oracle 数据库的默认隔离级别其实就是“读提交”,因此对于一些从 Oracle 迁移到 MySQL 的应用,为保证数据库隔离级别的一致,你一定要记得将 MySQL 的隔离级别设置为“读提交”。

    配置的方式是,将启动参数 transaction-isolation 的值设置成 READ-COMMITTED。你可以用 show variables 来查看当前的值。

    mysql> show variables like 'transaction_isolation';+-----------------------+----------------+| Variable_name | Value |+-----------------------+----------------+| transaction_isolation | READ-COMMITTED |+-----------------------+----------------+

    总结来说,存在即合理,每种隔离级别都有自己的使用场景,你要根据自己的业务情况来定。我想你可能会问那什么时候需要“可重复读”的场景呢?我们来看一个数据校对逻辑的案例。

    假设你在管理一个个人银行账户表。一个表存了账户余额,一个表存了账单明细。到了月底你要做数据校对,也就是判断上个月的余额和当前余额的差额,是否与本月的账单明细一致。你一定希望在校对过程中,即使有用户发生了一笔新的交易,也不影响你的校对结果。这时候使用“可重复读”隔离级别就很方便。事务启动时的视图可以认为是静态的,不受其他事务更新的影响。

    事务隔离的实现

    理解了事务的隔离级别,我们再来看看事务隔离具体是怎么实现的。

    这里我们展开说明“可重复读”。在 MySQL 中,实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值。

    假设一个值从 1 被按顺序改成了 2、3、4,在回滚日志里面就会有类似下面的记录。

    dfeef7b35195d47cf6c375b3956ce2f5.png

    当前值是 4,但是在查询这条记录的时候,不同时刻启动的事务会有不同的 read-view。如图中看到的,在视图 A、B、C 里面,这一个记录的值分别是 1、2、4,同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)。对于 read-view A,要得到 1,就必须将当前值依次执行图中所有的回滚操作得到。

    同时你会发现,即使现在有另外一个事务正在将 4 改成 5,这个事务跟 read-view A、B、C 对应的事务是不会冲突的。

    你一定会问,回滚日志总不能一直保留吧,什么时候删除呢?答案是,在不需要的时候才删除。也就是说,系统会判断,当没有事务再需要用到这些回滚日志时,回滚日志会被删除。

    什么时候才不需要了呢?就是当系统里没有比这个回滚日志更早的 read-view 的时候。

    基于上面的说明,我们来讨论一下为什么建议你尽量不要使用长事务。

    长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间。

    在 MySQL 5.5 及以前的版本,回滚日志是跟数据字典一起放在 ibdata 文件里的,即使长事务最终提交,回滚段被清理,文件也不会变小。我见过数据只有 20GB,而回滚段有 200GB 的库。最终只好为了清理回滚段,重建整个库。

    除了对回滚段的影响,长事务还占用锁资源,也可能拖垮整个库,这个我们会在后面讲锁的时候展开。

    事务的启动方式

    如前面所述,长事务有这些潜在风险,我当然是建议你尽量避免。其实很多时候业务开发同学并不是有意使用长事务,通常是由于误用所致。MySQL 的事务启动方式有以下几种:

    1. 显式启动事务语句, begin 或 start transaction。配套的提交语句是 commit,回滚语句是 rollback。
    2. set autocommit=0,这个命令会将这个线程的自动提交关掉。意味着如果你只执行一个 select 语句,这个事务就启动了,而且并不会自动提交。这个事务持续存在直到你主动执行 commit 或 rollback 语句,或者断开连接。

    有些客户端连接框架会默认连接成功后先执行一个 set autocommit=0 的命令。这就导致接下来的查询都在事务中,如果是长连接,就导致了意外的长事务。

    因此,我会建议你总是使用 set autocommit=1, 通过显式语句的方式来启动事务。

    但是有的开发同学会纠结“多一次交互”的问题。对于一个需要频繁使用事务的业务,第二种方式每个事务在开始时都不需要主动执行一次 “begin”,减少了语句的交互次数。如果你也有这个顾虑,我建议你使用 commit work and chain 语法。

    在 autocommit 为 1 的情况下,用 begin 显式启动的事务,如果执行 commit 则提交事务。如果执行 commit work and chain,则是提交事务并自动启动下一个事务,这样也省去了再次执行 begin 语句的开销。同时带来的好处是从程序开发的角度明确地知道每个语句是否处于事务中。

    你可以在 information_schema 库的 innodb_trx 这个表中查询长事务,比如下面这个语句,用于查找持续时间超过 60s 的事务。

    select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60

    小结

    这篇文章里面,我介绍了 MySQL 的事务隔离级别的现象和实现,根据实现原理分析了长事务存在的风险,以及如何用正确的方式避免长事务。希望我举的例子能够帮助你理解事务,并更好地使用 MySQL 的事务特性。

    我给你留一个问题吧。你现在知道了系统里面应该避免长事务,如果你是业务开发负责人同时也是数据库负责人,你会有什么方案来避免出现或者处理这种情况呢?

    你可以把你的思考和观点写在留言区里,我会在下一篇文章的末尾和你讨论这个问题。感谢你的收听,也欢迎你把这篇文章分享给更多的朋友一起阅读。

    上期问题时间

    在上期文章的最后,我给你留下的问题是一天一备跟一周一备的对比。

    好处是“最长恢复时间”更短。在一天一备的模式里,最坏情况下需要应用一天的 binlog。

    比如,你每天 0 点做一次全量备份,而要恢复出一个到昨天晚上 23 点的备份。

    一周一备最坏情况就要应用一周的 binlog 了。系统的对应指标就是 RTO(恢复目标时间)。

    当然这个是有成本的,因为更频繁全量备份需要消耗更多存储空间,所以这个 RTO 是成本换来的,就需要你根据业务重要性来评估了。

    展开全文
  • 在日常项目中肯定需要用到数据源,那么数据源是什么,当遇到分布式事务的场景时数据源与非分布式事务场景的数据源又有什么不同呢,在J2EE中分布式事务又是如何实现的呢,希望本文可以解答您的疑惑。 二、 数据源 2.1...

    一、概述

    在日常项目中肯定需要用到数据源,那么数据源是什么,当遇到分布式事务的场景时数据源与非分布式事务场景的数据源又有什么不同呢,在J2EE中分布式事务又是如何实现的呢,希望本文可以解答您的疑惑。

    二、 数据源

    2.1 数据源是什么

    通俗来讲,数据源是存储数据的地方。例如,数据库是数据源,其他系统也可以是数据源。在J2EE里,数据源是代表物理数据存储系统的实际Java对象。通过这些对象,J2EE应用可以获取到数据库的JDBC连接。

    2.2 数据源的设计

    db1a5bae36c33a38ea92231f93fc6f6d.png

    从UML图上可以看出,CommonDaraSource是对数据源概念的顶层抽象,约束了数据源必须实现的方法。数据源有三种类型的实现,分别是:

    • DataSource,基本实现,用于生成标准Connection对象
    • ConnectionPoolDataSource,连接池实现,这个数据源并不会直接创建数据库物理连接,而是一个逻辑实现,它的作用在于池化数据库物理连接。由于数据库物理连接是一个重量级的对象,频繁的创建销毁很影响性能,将物理连接池化后可降低创建和销毁的频率,复用连接以充分利用连接资源。
    • XAConnection,分布式事务实现,为支持分布式事务而诞生,这个数据源直接生产出的不是数据库物理连接Connection,而是一个支持XA的XAConnection对象,XAConnection对象可以直接生产数据库物理连接,同时生产XAResource用于支持XA事务,通常XAConnection对象生产出的数据库物理连接Connection需要和该XAConnection生产出的XAResource对象配合使用以完成XA事务处理。并且XAConnection继承PooledConnection,那就也具备连接池的实现。

    三、为什么需要XA数据源

    3.1 XA数据源是什么

    XA数据源指的是支持XA规范的数据源,支持分布式事务。

    3.2 XA规范是什么

    1ad726c44d316e16c63e8921068d697e.png

    XA规范是一种分布式事务解决方案。X/OPEN组织定义的分布式事务处理模型(DTP),其包含3种角色和两个协议:

    • AP(Application,应用程序)
    • RM(Resources manager,资源管理器),通常指数据库
    • TM(Transaction manager,事务管理器),通常指事务协调者,负责协调和管理事务,提供给AP接口以及管理资源
    • XA协议是事务管理器与资源管理器之间的通信接口
    • TX协议是应用程序与事务管理器之间的通信接口

    该模型中应用程序将一个全局事务传送到事务管理器,事务管理器将每个全局事务分解为多个分支(分支事务),并将分支事务分配给单独的资源管理器进行服务,事务管理器通过XA接口将每个分支事务与适当的资源管理器进行协调。

    3.3 分布式事务具备有什么样的作用?

    如果仅在同一个事务上下文中需要协调多种资源(即数据库以及消息主题或队列等等),这个事务中的所有操作都必须成功,否则所有操作都将在失败的情况下回滚。这就是XA数据源的作用。

    3.4 那什么样的场景需要使用XA?

    • 您的JavaEE应用程序必须使用单个事务将数据存储在两个数据库中
    • 您的应用程序需要通过单个事务发送JMS消息并将信息存储在数据库中
    • 您希望使用PVP将您自己项目的域信息存储在一个不同的数据库中,而这个数据库是被jBPM用来存储它自己的数据。

    四、 那怎么使用分布式事务呢?

    4.1 J2EE的分布式事务

    Java事务编程接口(Java Transaction API,JTA)和Java事务服务(Java Transaction Service,JTS)为J2EE平台提供了分布式事务服务。

    JTA事务是XA规范的Java实现,JTA事务有效的屏蔽了底层事务资源,使应用可以以透明的方式参与到事务处理中。分布式事务包括事务管理器和一个或多个支持XA协议的资源管理器。

    JTA是面向应用或应用服务器与资源管理器的高层事务接口。

    JTS是一组约定JTA中角色之间交互细节的规范。

    JTA提供了以下四个接口

    • javax.transaction.UserTransaction,是面向开发人员的接口,能够编程地控制事务处理。UserTransaction.begin方法开启一个全局事务,并且将该事务与调用线程关联起来。
    • javax.transaction.TransactionManager,允许应用程序服务器来控制代表正在管理的应用程序的事务。它本身并不承担实际的事务处理功能,它更多的是充当UserTransaction接口和Transaction接口之间的桥梁。
    • javax.transaction.Transaction,代表了一个物理意义上的事务,在开发人员调用UserTransaction.begin()方法时TransactionManager会创建一个Transaction事务对象,
    • javax.transaction.xa.XAResource,面向提供商的实现接口,是一个基于X/Open CAE Specification的行业标准XA接口的Java映射。提供商在提供访问自己资源的驱动时,必须实现这样的接口。

    开发者调用UserTransaction.begin方法时,因为UserTransaction的实现类持有TransactionManager,TransactionManager充当UserTransaction和Transaction之间的桥梁,所以在调用UserTransaction的begin方法时,TransactionManager会创建Transaction事务对象,并把此对象通过ThreadLocal关联到当前线程。当调用UserTransaction其他方法时,会从当前线程取出事务对象Transaction对象,并通过Transaction对象找到与其关联的XAResource对象,然后进行commit、rollback等操作。其基本流程如以下代码:

    // 创建一个Transaction,挂到当前线程上
    UserTransaction userTx = null; 
    Connection connA = null; 
    Connection connB = null; 
    try{
        userTx.begin();
        // 将Connection对应的XAResource挂到当前线程对应的Transaction
        connA.exec("xxx")
        connB.exec("xxx")
        // 找到Transaction关联的XAResource,让它们都提交
        userTx.commit();
    }catch(){
        // 找到Transaction关联的XAResource,让它们都回滚
        userTx.rollback();
    }

    4.2 如何使用J2EE的分布式事务

    • WebLogic、Websphere、JBoss等主流的应用服务器提供了JTA的实现和支持。
    • Tomcat中没有提供JTA的实现的,这就需要借助第三方的框架Jotm、Automikos等来实现。

    五、总结

    本文主要介绍了数据源和XA数据源,以及分布式事务基本原理、作用和场景以及如何使用J2EE分布式事务,但这种是属于基于资源层的底层分布式事务解决方案,在业内,用来解决分布式事务的方案还有柔性事务,柔性事务包括几种类型:两阶段型、补偿型、异步确保型和最大努力通知型,有兴趣可以更深入的了解一下。

    六、参考

    • https://dzone.com/articles/datasources-what-why-how
    • https://searchoracle.techtarget.com/news/918695/Data-sources-What-how-and-why
    • https://docs.oracle.com/database/timesten-18.1/TTCDV/xa_dtp.htm#TTCDV328
    • https://docs.oracle.com/javase/7/docs/api/javax/sql/CommonDataSource.html
    • https://blog.csdn.net/conquer0715/article/details/50925674/
    • https://www.ibm.com/developerworks/cn/java/j-lo-jta/
    • https://www.dazhuanlan.com/2019/12/17/5df7d87d35561/

    作者 | Karina Varela · Jun 翻译 | 小青菜 来源 | https://dzone.com/articles/datasources-what-why-how 本文有spring4all技术翻译组完成,更多国外前沿知识和干货好文,欢迎关注公众号:后端面试那些事儿。

    展开全文
  • 那么,什么样的销售方式让客户感觉很舒服呢?首先我们分析一下,什么样的销售方式让客户感觉不舒服,找出问题来,自然就找到答案了,对不对?我总结了一下,让客户感觉很不舒服的销售沟通方式有这样4种:一;直接和客户...
  • MySQLdump之single-transaction详解single-transaction开启general log选项查看目前general log的情况mysql> show variables like '%general_log%';+------------------+---------------------------------------...
  • window.indexedDB=window.indexedDB || window.... var tx= e.target.transaction; var oldVersion= e.oldVersion; var newVersion= e.newVersion; alert("数据库更新成功"+oldVersion+"——"+newVersion); } }
  • 本文主要向大家介绍了MySQL数据库之MySQLdump之single-transaction详解 ,通过具体的内容向大家展现,希望对大家学习MySQL数据库有所帮助。MySQLdump之single-transaction详解single-transaction开启generallog选项...
  • System.out.printf("%-35s%-15s%-15s%-15s\n", transaction.getDate(), transaction.getType(), transaction.getAmount(), transaction.getBalance()); } } } class Account1 { private java.util.ArrayList ...
  • 官方解释:为什么 tcc 事务切面中对乐观锁与socket超时异常不做回滚处理,只抛异常? 针对 SocketTimeoutException :try 阶段,本地参与者调用远程参与者( 远程服务,例如 Dubbo,Http 服务),远程参与者 try 阶段...
  • 挖矿是什么意思

    2019-12-10 16:53:56
    挖矿是什么意思?矿工都做了什么? 挖矿节点计算机在挖矿时要做两个任务: 第一个任务是把比特币网络中未确认的交易按梅克尔树组装成候选区块,未被纳入的交易则往下顺延。 在创建候选区块时,除了普通的交易之外...
  • 这篇文章从本质上讲解了矿工挖矿是什么意思,需要读者具备以下基础知识: 比特币是如何转账的? 比特币的分布式账本和去中心网络 UTXO(未使用的交易输出) 比特币区块链的数据结构 工作量证明共识机制 挖矿节点...
  • UTXO(Unspent Transaction Outputs)是未花费的交易输出,它是比特币交易生成及验证的一个核心概念。交易构成了一组链式结构,所有合法的比特币交易都可以追溯到前向一个或多个交易的输出,这些链条的源头都是挖矿...
  • 币圈最专业的十种挣钱办法,加微信拉你进群一起交流最佳答案:UTXO(Unspent Transaction Outputs)是未花费的交易输出,它是比特币交易生成及验证的一个核心概念。交易构成了一组链式结构,所有合法的比特币交易都...
  • 声明式事务(declarative transaction management)是Spring提供的对程序事务管理的方式之一。Spring的声明式事务顾名思义就是采用声明的方式来处理事务。这里所说的声明,就是指在配置文件中申明。用在Spring配置文件...
  • @@trancount什么意思

    2014-05-16 15:55:17
    在处理事务的时候,一般都用RollBack Transaction来回滚,但是如果在嵌套事务中这样使用的话,就会出现错误。 在SqlServer里,嵌套事务的层次是由@@TranCount全局变量反映出来的。每一次Begin Transaction都会引起@...
  • OLAP是什么意思?

    千次阅读 2016-12-20 22:15:20
    什么是联机分析处理(OLAP) 联机分析处理 (OLAP) 的概念最早是由关系数据库之父E.F.Codd于1993年提出的,他同时提出了关于OLAP的12条准则。OLAP的提出引起了很大的反响,OLAP作为一类产品同联机事务处理 (OLTP) ...
  • @@trancount什么意思 https://www.cnblogs.com/monian/archive/2012/08/24/2654392.html 在处理事务的时候,一般都用RollBack Transaction来回滚,但是如果在嵌套事务中这样使用的话,就会出现错误。在Sql...
  • [12/13/07 10:09:05:171 CST] 000002d6 RegisteredRes E WTRN0078E: An attempt by the transaction manager to callstart on a transactional resource has resulted in an error. The error code was XAER_RMERR.....
  • mysql 参数前带 loose 是什么意思了? 插件相关的参数只能在插件加载之后设置,如果想启动时设置这些参数并且启动后加载插件到配置文件中,可以在参数前加上“loose-”前缀。 ########################## # group ...
  • 一、事务定义Transaction事务:一个最小的不可再分的工作单元;通常一个事务对应一个完整的业务(例如银行账户转账业务,该业务就是一个最小的工作单元)一个完整的业务需要批量的DML(insert、update、delete)语句共同...
  • TPS英文全称是Transaction PerSecond,应用在数字货币,TPS指的是系统吞度量,也是每秒系统处理的数量。假如TPS每秒并发太低,很容易造成网络拥堵严重,从而使得区块链在高价值的高并发业务领域无法落地。比如,由于...
  • java什么是事务意思概念2018/6/19 22:27:00 阅读:744次java什么是事务意思概念来自简单日记网精选推荐。事务,一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元...
  • 在创建候选区块时,除了普通的交易之外,矿工在其中增加一个特殊的交易:币基交易(coinbase transaction)。如果挖矿成功,则币基交易会凭空转出新区块的奖励比特币到矿工的钱包地址中,从而发行这些比特币出来。这...
  • 假设我定义了一个新的持久类帐户,如中...在import persistent, ZODB, ZODB.FileStorage, import transactionclass Account(persistent.Persistent):def __init__(self):self.balance = 0.0def deposit(self, amount...
  • mysql 参数前带 loose 是什么意思了?插件相关的参数只能在插件加载之后设置,如果想启动时设置这些参数并且启动后加载插件到配置文件中,可以在参数前加上“loose-”前缀。########################### group ...
  • 当我们在Bitcoin 上生一个transaction 时,会拿到一个raw transaction,长长一串的不知道代表什么意思,我们拿去呼叫decoderawtransaction RPC 才会看到他的样貌,在拿的到utxo 的情况下实作本地端创建raw ...

空空如也

空空如也

1 2 3 4 5 ... 8
收藏数 149
精华内容 59
关键字:

transaction什么意思