设计 | 贾妍
随着实时应用需求场景的增多,企业数据并发读写的任务也越来越多,但基于Hadoop、Hive、Spark等技术框架的数据处理方式却无法满足需求。因而,当发生读写冲突时,如何通过并发模式控制数据处理的顺序,以保证数据的一致性,成为新的技术挑战。
基于Hadoop、Hive、Spark等技术框架之所以不能很好地实现上述功能,一个很重要的原因是因为并发的存在,但是又没有像数据库那样进行并发控制的事务功能。
在操作系统中,并发是指一个很短的时间段中有几个程序都处于已启动运行状态,到运行完毕状态之间,多个程序之间有相互制约关系:直接制约体现为一个程序需要另一个程序的计算结果;间接制约体现为多个程序竞争同一资源,如缓冲区、数据等。
在数据库系统中,并发主要是指资源的争用,当两个进程在同一时刻访问或更新同一个数据时,就会产生资源的争用,当对资源争用不加以控制时,它会引起一系列的问题,比如数据不一致、查询阻塞、死锁等。
SQL Server必须对资源争用加以控制,实现并发控制读操作的不同方法,称作并发模式。当发生读写冲突时,并发模式控制数据处理的顺序,以保证数据的一致性。
事务是所有数据库系统的一个基本概念。 一次事务的要点就是它把多个步骤捆绑成了一个单一的,不成功则成仁的操作。 其它并发的事务是看不到在这些步骤之间的中间状态的,并且如果发生了一些问题, 导致该事务无法完成,那么所有这些步骤都完全不会影响数据库。
事务具有4个特性,统称ACID属性:
原子性:事务整体是一个工作单元,对数据的修改操作,要么全部执行,要么完全不执行,没有第三种状态。
一致性:在一个事务执行之前和执行之后数据库都必须处于逻辑上的一致性状态,数据在不同的事务中是相同的。
隔离性:并发执行的事务之间是相互隔离的,一个事务内部的状态,对其他事务是不可见的。
持久性:当系统发生故障时,持久性确保已提交事务的更新不会丢失,也就是说一旦一个事务提交,DBMS保证数据的改变是永久性的,持久性通过事务日志来保证。
ACID 四属性的关系可大概表述为:原子性是要求,一致性是目标,隔离性是手段,持久性是结果。所以,如何做到原子性的隔离是实现事务的重中之重。在宏观上,实现事务特性是通过并发控制。在微观上,实现事务要靠隔离。
SQL Server支持悲观并发模式和乐观并发模式来控制并发,保证事务的ACID属性,两种并发模式的不同之处是保护数据的结构不同。在一个数据库中,只能选择其中一个并发模式,默认的并发模式是悲观并发模式。
1. 悲观并发模式
悲观并发模式使用锁结构来保护数据,保证数据的一致性,适用于锁消耗低于回滚事务的成本环境中。
悲观并发模式是默认的并发模式,在数据库系统中,当多个进程访问同一资源时,SQL Server会通过各种类型的锁来协调资源的访问,确保在并发环境下数据保持一致的状态。而锁的作用范围是在事务中,事务建立在并发模式下。
在悲观并发模式下,SQL Server认为有大量的写操作发生,并且写操作会受到写操作的影响。也就是说,悲观并发模式对任何正在访问的数据进行加锁,以避免多个进程同时修改或读取数据,造成数据的不一致。在默认的隔离级别下,读和写是相互阻塞的。
2. 乐观并发模式
乐观并发模式在并发过程中不产生锁,而是使用行版本(Row Version)来保护数据,保证数据的一致性,适用于写操作较少的环境中。
在乐观并发模式下,当一个事务对数据进行修改时,SQL Server把已经提交的老版本数据存储在tempdb的版本库(version store)中。读操作读取的是已经提交的老版本,这些数据实际上存储在tempdb中。
对于乐观并发模式,SQL Server假设只有少量的写写冲突发生,默认的机制是使用快照技术,在写进程完成数据修改之前,先把数据的行版本保存到tempdb中。由于数据的老版本已经保存,读进程可以直接读取已经保存的行版本,而不会受到写进程的影响。
乐观并发使得读写操作不会相互阻塞,但是这会导致一个潜在的问题,读进程可能会读取到老的数据。
每一个事务都运行在一个特定的隔离级别内,该隔离级别是由会话(session)决定的。SQL Server定义的五个隔离级别,都是为了定义读数据的行为:
READ UNCOMMITTED :允许脏读、数据不可重复读和数据范围不可重复读
READ COMMITTED:防止脏读(读取未提交的数据更新),允许数据不可重复读和数据范围不可重复读
REPEATABLE READ:防止脏读和数据不可重复读,允许数据范围不可重复读
SERIALIZABLE:防止脏读、数据不可重复读和数据范围不可重复读
SNAPSHOT:快照隔离级别,主要是为了避免读写相互阻塞
隔离级别定义事务处理数据读取操作的隔离程度,在悲观并发模式下,隔离级别只会影响读操作申请的共享锁(Shared Lock),而不会影响写操作申请的互斥锁(Exclusive Lock),在乐观并发模式下,从tempdb中读取事务开始时已经提交的行版本(row version),而不会读取到尚未提交的数据行版本。
在SQL Server定义的五个隔离级别中Snapshot隔离级别属于乐观并发模式,其他隔离级别属于悲观并发模式。在SNAPSHOT隔离级别下,任何写操作都会将更新之前的数据行保存到tempdb中,读取操作要么从Original Database的数据表中读取数据,要么从tempdb中读取行版本数据。
Snapshot隔离级别指定:在一个事务中,任何语句读取的数据,是事务一致性的版本。事务一致性是指在事务开始时,在表级别创建数据快照,只能识别其他事务已提交的数据更新。
在事务开始之后,当前事务不会识别其他事务执行的数据更新。如果一个Snapshot 事务尝试去提交数据行的更新,但是该数据行已经被其他事务修改,并且修改的时间早于当前事务开始的时间,那么SQL Server将当前事务作为失败者,并回滚其事务操作。
Sanpshot隔离级别实现事务级别的数据一致性。SQL Server 使用tempdb来存储行版本化(row versioning)的数据,如果数据更新较多,存储的行版本太多,会导致tempdb成为系统瓶颈。乐观并发模式用于冲突较少的环境中,如果应用程序在更新数据时经常发生冲突,Snapshot隔离级别可能不是最好的选择。
想要参与本期话题讨论
欢迎扫描小助手二维码
加入技术交流群
分析型数据库FastData for DLink有何优势?
广州城投 | 新华联 | 华发股份
点击蓝字
关注我们