原文:数据库事务、隔离级别和锁ACID的真实含义隔离级别和并发控制MySQL和PostgreSQL对比如何写代码 作者:大宽宽 转自:https://cloud.tencent.com/developer/article/1121737 数据库事务、隔离级别和锁ACID的真实含义隔离级别和并发控制MySQL和PostgreSQL对比如何写代码 这是个令大多数后端同学头疼的问题。部分是因为不同的文章、文档充斥着不相容的概念。高层抽象和底层实现混到一起令人傻傻的分不清楚。此外还有一部分是SQL标准和实现压根就不一致。本文期望在众多文献中找到一条容易理解知识线,帮助大家在实际工作中更加容易明白到底怎么使用数据库的事务、隔离级别和锁。 ACID的真实含义 一般都会用ACID来表达事务的特性。A、C、I、D分别代表“原子性”,“一致性”,“隔离性”和“持久性“。这是1983年(恰好是我出生的年份)ACM的一篇期刊文章Principles of Transaction-Oriented Database Recovery给出的。这四条特性指导了30多年来数据库的模型、设计和开发。ACID很重要,基本上每篇关于数据库的事务的书、文章都要把他们拿出来重新解释一遍。但是,似乎很多人看了很多遍这种文章还是不明白(包括我在内)。因为简单列举,解释这4个概念和现实中数据库的实现不那么容易接起来。 我是这样理解这几个概念的: Atomicity,原子性。很令人郁闷的是,这个词表达意思与常规语境下不太一样。对于一门支持并发的编程语言(比如Java,C++),原子性是指一组指令被执行时,不受其他指令的干扰。比如我们可以说“CAS是原子的;给一个整型变量赋值是原子的”等等。但是在ACID语境下,“不受干扰”这层意思其实是后边“隔离性“说的事情。在ACID语境下,原子性是指一组对数据库的改变,要么最终成功执行完成,要不就全部回滚。这就要求数据库系统要实现某种回滚的机制,比如redo/undo log)。所以,也许这里用术语”revertability“可能更适合。与事务性数据库相比,一些NoSQL的数据库也声称支持原子性,但是意义不同。比如Redis事务的原子性的意思可能更接近于“一组指令被执行时,不受其他指令的干扰”,而不是“可以回滚”。 Consistency, 一致性。这个术语的用词也颇为怪异。一般来讲,我们习惯用“一致性”来描述数据在某些条件下可以变成一样的。例如,在描述CPU工作方式时可以说需要主内存在CPU Core1里的缓存和CPU Core2里的缓存是“一致的“;或者,一个分布式数据系统中,A节点从B节点复制数据,A的数据要和B的数据”严格一致”或者“最终一致”。而ACID下的一致性指的是,在事务完成前后,数据都是要在业务意义上是”正确的“,所以也许术语”correctness“更适合这里的意思。但如果这样定义的话,数据库的位置就很尴尬了,因为保证业务是否正确是要业务代码来最终保证的,数据库能做的非常有限。目前数据库里实现的约束检查,比如唯一约束、外键约束、一些enum测检查、一些数据类型/长度/有效数字的检查等等,对于简单的场景还可以使用。对于复杂的业务约束检查,很难或者不可能实现。有一类数据正确性问题正是由于下面隔离性的使用不当而带来的。 真实复杂业务的数据正确性维护一般用正确的业务代码 + 合法性job来定时执行 + 数据库自身的简单合法性防护一起实现。 Isolation,隔离性。是指一组对数据库的并发修改互相不影响。这个概念表面上看来并不是能说得通,因为如果并发修改的是互不相干的数据,那么自然隔离性可以得到满足;如果并发修改的是相关联的,或者就是同一份数据,就必然会相互影响。那么,此时可以做的就是区分哪个修改优先级更加高。而高优先级的修改应该覆盖掉低优先级的修改。但是,现实往往更复杂,因为并发的修改并不一定能够讲明白先来后到的(要不怎么叫并发呢),此时谁应该生效无法很好的定义。另外一种情况是“先读取,再基于读取结果对数据进行修改”这样业务逻辑。比如,先找到可用的库存,有则扣减,没有则提示缺货;再比如先读取当前的计数值,再往上加1。这时保证隔离性的主要问题不在于隔离本身,而在于如果将读取作为对数据修改的前提条件,之后在对数据进行修改的一刹那,读取时的前提条件还是否满足。毕竟读取和写入是两个分开的指令,而在这两个指令中间可能夹杂其他事务对数据的修改。保持隔离性的一个简单做法是保证对关联数据的修改串行化,对应事务性数据库的“Serializable”隔离级别。保证串行化的一种方案是锁,通过锁定可以彻底避免竞争条件。但是大家都能明白加锁对数据库并发的性能负面影响很大,所以就衍生出了几种弱一些的隔离性保证——READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ。此外MVCC能够解决一部分锁带来的问题。这些内容在下文中会详细的讲解。 Duration,持久性。是指对数据的修改,一旦完成,该结果就应当永远不丢失。这是这4个术语中我唯一觉得表面上和实际上意思差不多的一条。在现实当中,一般通过持久性存储设备(比如磁盘/SSD)写入并刷新来保证数据的持久性。如果觉得一个节点不靠谱,可以增加多个副本(replica)一起来保证持久;如果觉得这样还不够靠谱,可以在不同的地理位置的另一个数据中心做备份。实际上绝对的持久性是不存在的,因为整个存储层面有很多不确定因素,比如文件系统本身fsync指令实现有bug,磁盘的固件有bug,供电出现问题造成数据错乱,异步的数据复制没有生效等等。所以在现实当中的数据库,只能在当前成本和技术限制的约束下,尽量维持一定程度的持久性。 所以这么整理起来。实际上事务性数据库实现的是 支持未完成的数据修改回滚的机制,对应“原子性” 力所能及的数据合法性检查,对应“一致性” 保证数据并发的修改的规则,对应“隔离性” 使用基于持久化存储(磁盘、SSD)的方式对数据进行存储,对应“持久性” 这四条中一、二、四不是本文要讨论的内容,不再赘述。后面主要来谈一下第三条——隔离性规则,即隔离级别的实现。 隔离级别和并发控制 SQL92标准定义了四种隔离级别——Read Uncommitted,Read Committed,Repeatable Read和Serializable。定义这4种隔离级别时,制定者主要围绕着基于锁的并发控制来说的。但是后来出现了MVCC,之后主流数据库都开始支持MVCC。有的数据库采用比较纯粹的MVCC实现,比如PostgreSQL;有的则是混杂的,比如MySQL InnoDB。这就会造成数据库的实现和标准的描述有很多出入。 其实我们并不在意标准怎么说。标准只是一个参考,引导我们理解我们能够接触的实际数据库系统的工作原理。我们先从最简单情况谈起。 最不严格的隔离级别 理论上,最不严格的隔离级别应该是不隔离。 不隔离很容易理解,不同的事务可以对同一数据并发的随便改:A事务改了一半的结果B能看到;B改了一半的结果A也能看到;如果A和B反复修改同一个数据,那么彼此的修改可以覆盖。数据系统在没有做隔离防护时,就一定会是这个样子。这样也就无所谓事务了。 这里数据访问冲突可以分为两种: Dirty Read,脏读。即一个事务的没提交之前的修改被另外一个事务可以看到。 Dirty… Continue Reading 【转载】数据库事务、隔离级别和锁ACID的真实含义隔离级别和并发控制MySQL和PostgreSQL对比如何写代码

原文:MySQL与PostgreSQL相比哪个更好? 作者:biaodianfu 转自:https://www.biaodianfu.com/mysql-vs-postgresql.html MySQL与PostgreSQL相比哪个更好? 2016年12月29日  1 min read 网上已经有很多拿PostgreSQL与MySQL比较的文章了,这篇文章只是对一些重要的信息进行下梳理。在开始分析前,先来看下这两张图: MySQL MySQL声称自己是最流行的开源数据库。LAMP中的M指的就是MySQL。构建在LAMP上的应用都会使用MySQL,如WordPress、Drupal等大多数php开源程序。MySQL最初是由MySQL AB开发的,然后在2008年以10亿美金的价格卖给了Sun公司,Sun公司又在2010年被Oracle收购。Oracle支持MySQL的多个版本:Standard、Enterprise、Classic、Cluster、Embedded与Community。其中有一些是免费下载的,另外一些则是收费的。其核心代码基于GPL许可,由于MySQL被控制在Oracle,社区担心会对MySQL的开源会有影响,所以开发了一些分支,比如: MariaDB和Percona。 PostgreSQL PostgreSQL标榜自己是世界上最先进的开源数据库。PostgreSQL的一些粉丝说它能与Oracle相媲美,而且没有那么昂贵的价格和傲慢的客服。最初是1985年在加利福尼亚大学伯克利分校开发的,作为Ingres数据库的后继。PostgreSQL是完全由社区驱动的开源项目。它提供了单个完整功能的版本,而不像MySQL那样提供了多个不同的社区版、商业版与企业版。PostgreSQL基于自由的BSD/MIT许可,组织可以使用、复制、修改和重新分发代码,只需要提供一个版权声明即可。 MySQL与PostgreSQL的对比 MySQL的背后是一个成熟的商业公司,而PostgreSQL的背后是一个庞大的志愿开发组。这使得MySQL的开发过程更为慎重,而PostgreSQL的反应更为迅速。这样的两种背景直接导致了各自固有的优点和缺点。 PostgreSQL相对于MySQL的优势 1)不仅仅是关系型数据库 除了存储正常的数据类型外,还支持存储: array,不管是一位数组还是多为数组均支持 json(hStore)和jsonb,相比使用text存储接送要高效很多 json和jsonb之间的区别 jsonb和json在更高的层面上看起来几乎是一样的,但在存储实现上是不同的。 json存储完的文本,json列会每次都解析存储的值,它不支持索引,但你可以为查询创建表达式索引。 jsonb存储的二进制格式,避免了重新解析数据结构。它支持索引,这意味着你可以不使用指定的索引就能查询任何路径。 当我们比较写入数据速度时,由于数据存储的方式的原因,jsonb会比json稍微的慢一点。json列会每次都解析存储的值,这意味着键的顺序要和输入的时候一样。但jsonb不同,以二进制格式存储且不保证键的顺序。因此,如果你有软件需要依赖键的顺序,jsonb可能不是你的应用的最佳选择。使用jsonb的优势还在于你可以轻易的整合关系型数据和非关系型数据, PostgreSQL对于mongodb这类的基于文档的数据库是个不小的威胁,毕竟如果一个表中只有一列数据的类型是半结构化的,没有必要为了迁就它而整个表的设计采用schemaless的结构。 2)支持地理信息处理扩展 PostGIS 为PostgreSQL提供了存储空间地理数据的支持,使PostgreSQL成为了一个空间数据库,能够进行空间数据管理、数量测量与几何拓扑分析。在功能上,和MYSQL对比,PostGIS具有下列优势: O2O业务场景中的LBS业务使用PostgreSQL + PostGIS有无法比拟的优势。 3)可以快速构建REST API PostgREST 可以方便的为任何 PostgreSQL 数据库提供完全的 RESTful API 服务。 4)支持树状结构 支持R-trees这样可扩展的索引类型,可以更方便地处理一些特殊数据。MySQL 处理树状的设计会很复杂, 而且需要写很多代码, 而 PostgreSQL 可以高效处理树结构。 5)有极其强悍的 SQL… Continue Reading 【转载】MySQL与PostgreSQL相比哪个更好?

原文:http://mysql.taobao.org/monthly/2015/12/07/ PgSQL · 答疑解惑 · 表膨胀 背景 最近处理了几起线上实例表膨胀的问题。表膨胀是指表的数据和索引所占文件系统的空间,在有效数据量并未发生大的变化的情况下,不断增大。PG使用过程中需要特别关注这方面,我们来给大家解析一下表膨胀的原因。 表膨胀的直接触发因素是表上的大量更新,如全表的update操作、大量的insert+delete操作等。而我们知道,PG在更新数据时,是不直接删除老数据的。一个update操作执行后,被更改的数据的旧版本也被保留下来,直到对表做vacuum操作的时候,才考虑回收旧版本。做数据更新时,这些旧版本不及时回收就会造成表膨胀。 线上实例都配置了autovacuum,有了autovacuum,PG会定期自动启动autovacuum worker进程,执行vacuum回收旧版本,防止表膨胀。但在我们看到的几起表膨胀问题里面,autovacuum几乎每分钟运行一次,仍然没有避免表膨胀,这是为什么呢? 表膨胀问题的重现 从问题实例的pg_stat_activity视图里面,可以发现它们有一个特点,就是有长时间未提交或终止的事务。 我们用下面的例子简单模拟一下。先创建一张表,插入一条数据。再建立两个连接,其中一个开启一个事务,执行插入和查询,但不提交;另一个不断执行update操作。 控制台A: postgres=# begin; BEGIN postgres=# insert into test_bloat values(1); INSERT 0 1 postgres=# select * from test_bloat; a — 1 1 (2 rows) postgres=# 控制台B: postgres=# update test_bloat set a = 1; UPDATE 1 postgres=# \watch… Continue Reading 【转载】PgSQL · 答疑解惑 · 表膨胀

原文:PostgreSQL防止更新丢失(覆盖) 作者:openwares.net 转自:https://blog.csdn.net/Starrain00/article/details/7333926 PostgreSQL防止更新丢失(覆盖) 所有的数据库都会遇到更新丢失(覆盖)的问题。 更新丢失(覆盖)的详细描述见数据库事务隔离级别。 发生更新丢失(覆盖)问题的关键在于”读取,计算,写回”这个过程中,当前事务使用了过期的(stale)数据,没有将其他并发事务对数据的修改纳入到计算结果中,从而在写回时将其他事务的更新覆盖了。 防止更新丢失(覆盖)的方法 悲观锁 事务中对需要修改的结果集加行锁,常用的就是select for update,或者lock table对整个表加锁。加锁之后,当前事务未处理完成之前,其他所有需要访问锁定行的事务都必须等待。这虽然能解决更新丢失(覆盖)的问题,但很明显会影响数据库的并发性能。 如果并发事务冲突的几率很高,则采用悲观锁可以减少事务回滚并重试的开销。 乐观锁 乐观锁不锁定任何行,当更新数据时再做检查数据是否已经发生了变化。多版本并发控制MVCC(Multiversion concurrency control)可以实现乐观锁。PostgreSQL使用MVCC实现并发控制。PostgreSQL的默认事务隔离级别为读已提交(Read Commited),在这个级别上会发生不可重复读现象,这个隔离级别是无法防止更新丢失(覆盖)的。 但是在可重复读(Repeatable Read)事务隔离级别,可以完全防止更新丢失(覆盖)的问题,如果当前事务读取了某行,这期间其他并发事务修改了这一行并提交了,然后当前事务试图更新该行时,PostgreSQL会提示:ERROR: could not serialize access due to concurrent update事务会被回滚,只能重新开始。 由于使用的是MVCC机制的乐观锁,内部有版本号(这个字段名字叫xmin)来控制并发,所以不会对数据集上锁,对性能的影响是很小的。但如果并发事务冲突的几率比较大,那么事务回滚的开销就比较大了。 总的来说,如果并发事务冲突的几率很低那么应该选择乐观锁,对于PostgreSQL来说,将事务隔离级别提高到Repeatable Read即可。如果事务并发冲突的几率很高,那么可以谨慎的使用悲观锁。 还有一种防止更新丢失(覆盖)的方法叫条件更新,也就是在更新时指定where子句,检测指定的条件是否已经变化来决定是否进行更新。这不是一种通用的解决方案,只能根据业务逻辑来选择特定的检测条件,并不能防止这些检测条件之外的可能存在的更新丢失问题。而且有些情况下可能很难选择合适的更新检测条件,比如一个银行账户,关键的字段有账户号和余额,很难通过WHERE条件来检测当前事务执行期间是否有其他并发事务已经修改了余额并做了提交。所以这种方法只在特定的逻辑环境下有一定的用途。 PostgreSQL可重复读隔离级小实验 先建一张表,并插入一条记录: 下面演示两个事务并发的几种情况,左边为事务A,右边为事务B。 第一种情况: begin transaction isolation level repeatable read ;        | select whatever from sometable where… Continue Reading 【转载】PostgreSQL防止更新丢失(覆盖)