01
概述
数据库是一个多用户使用的共享资源。当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性。加锁是实现数据库并发控制的一个非常重要的技术。当事务在对某个数据对象进行操作前,先向系统发出请求,对其加锁。加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他的事务不能对此数据对象进行更新操作。
OLTP 场景下通常要求具有很高的并发性。并发事务实际上取决于资源的使用状况,原则上应尽量减少对资源的锁定时间,减少对资源的锁定范围,从而能够尽量增加并发事务的数量,那么影响并发的因素有哪些呢?本文将从巨杉分布式数据库本身的机制以及隔离级别、数据库锁、参数、及实际例子进行详解,读完本文将对巨杉数据库并发性与锁机制有一个初步的了解。
02
隔离级别与并发性
在单用户环境中,每个事务都是顺序执行的,而不会遇到与其他事务的冲突。但是,在多用户环境下,多个事务并发执行。因此每个事务都有可能与其他正在运行的事务发生冲突。有可能与其他事务发生冲突的事务称为交错的或并行的事务,而相互隔离的事务称为串行化事务,这意味着同时运行它们的结果与一个接一个连续地运行它们的结果没有区别。在多用户环境下,在使用并行事务时,会发生四种现象:
-
丢失更新:这种情况发生在两个事务读取并尝试更新同一数据时,其中一个更新会丢失。例如:事务 1 和事务 2 读取同一行数据,并都根据所读取的数据计算出该行的新值。如果事务 1 用它的新值更新该行以后,事务 2 又更新了同一行,则事务 1 所执行的更新操作就丢失了。
-
脏读:当事务读取尚未提交的数据时,就会发生这种情况。例如:事务 1 更改了一行数据,而事务 2 在事务1 提交更改之前读取了已更改的行。如果事务 1 回滚该更改,则事务 2 就会读取被认为是不曾存在的数据。
-
不可重复的读:当一个事务两次读取同一行数据,但每次获得不同的数据值时,就会发生这种情况。例如:事务 1 读取了一行数据,而事务 2 在更改或删除该行后提交了更改。当事务 1 尝试再次读取该行时,它会检索到不同的数据值(如果该行已经被更新的话),或发现该行不复存在了(如果该行被删除的话)。
-
幻像:当最初没有看到某个与搜索条件匹配的数据行,而在稍后的读操作中又看到该行时,就会发生这种情况。例如:事务 1 读取满足某个搜索条件的一组数据行,而事务 2 插入了与事务 1 的搜索条件匹配的新行。如果事务 1 再次执行产生原先行集的查询,就会检索到不同的行集。
维护数据库的一致性和数据完整性,同时又允许多个应用程序同时访问同一数据,这样的特性称为并发性。巨杉数据库目前通过事务、隔离级别、锁等机制来对并发性进行控制,它决定在第一个事务访问数据时,如何对其他事务锁定或隔离该事务所使用的数据。目前巨杉数据库支持以下隔离级别来实现并发性:
-
读未提交(ReadUncommitted):该隔离级别指即使一个事务的更新语句没有提交,但是别的事务可以读到这个改变,几种异常情况都可能出现。会出现读取的数据是不对的。
-
读已提交(Read Committed):该隔离级别指一个事务只能看到其他事务的已经提交的更新,看不到未提交的更新,消除了脏读和第一类丢失更新,这是大多数数据库的默认隔离级别。保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”,但不能避免“幻读”和“不可重复读取”。该级别适用于大多数系统。
-
读稳定性(RepeatableStability):该隔离级别指一个事务中进行两次或多次同样的对于数据内容的查询,得到的结果是一样的。假设SQL语句中包括查询条件, 则会对全部符合条件的纪录加对应的锁。假设没有条件语句。也就是对表中的全部记录进行处理。则会对全部的纪录加锁。
-
可重复读(Repeatable Read):REPEATABLE READ隔离级解决了READUNCOMMITTED隔离级导致的问题。它确保同一事务的多个实例在并发读取数据时,会“看到同样的”数据行。不过理论上,这会导致另一个棘手问题:幻读(Phantom Read)。简单来说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影”行。数据库存储引擎可以通过多版本并发控制 (Multiversion Concurrency Control)机制解决了幻读问题,如MySQL的InnoDB和Falcon。巨杉数据库对于多版本控制(MVCC)技术是通过采用事务锁、内存老版本以及磁盘回滚段重建老版本的设计来实现。此架构设计的理论基础是通过对内存结构的合理利用,存储数据和索引的老版本信息,从而实现数据的快速的并发访问。
03
数据库锁参数与并发性实践
1. SequoiaDB的事务配置
事务作为一个完整的工作单元执行,事务中的操作要么全部执行成功要么全部执行失败。SequoiaDB事务中的操作只能是插入数据、修改数据以及删除数据,在事务过程中执行的其它操作不会纳入事务范畴,也就是说事务回滚时非事务操作不会被执行回滚。如果一个表或表空间中有数据涉及事务操作,则该表或表空间不允许被删除。
-
事务开启、提交与回滚
在SDB中,关于事务启停的配置项如下:
默认情况下,SequoiaDB 所有节点的事务功能都是开启的。若用户不需要使用事务功能,可参考以下方法,关闭事务功能。
步骤1:通过sdb shell设置集群所有节点都关闭事务。
db.updateConf( { transactionon: false }, { Global: true } )
步骤2:在集群每台服务器上都重启 SequoiaDB 的所有节点。
[sdbadmin@ubuntu-dev1 ~]$ /opt/sequoiadb/bin/sdbstop -t all
[sdbadmin@ubuntu-dev1 ~]$ /opt/sequoiadb/bin/sdbstart -t all
注意&#x