知言 wordpress,如何优化网站关键词排名,网上商城推广文案,中国建筑集团有限公司官网首页01概述数据库是一个多用户使用的共享资源。当多个用户并发地存取数据时#xff0c;在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据#xff0c;破坏数据库的一致性。加锁是实现数据库并发控制的一个非常重要的技术。…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 的所有节点。[sdbadminubuntu-dev1 ~]$ /opt/sequoiadb/bin/sdbstop -t all[sdbadminubuntu-dev1 ~]$ /opt/sequoiadb/bin/sdbstart -t all注意1. 开启及关闭节点的事务功能都要求重启该节点。2. 在开启节点事务功能的情况下节点的配置项logfilenum(该配置项默认值为20)的值不能小于 5。SequoiaDB 事务支持的操作如下写事务操作INSERT、UPDATE、DELETE。读事务操作QUERY。SequoiaDB的其它操作(如创建表、创建索引、创建并读写LOB等其它非 CRUD 操作)不在事务功能的考虑范围。 支持隔离级别配置参数及取值如下可以通过以下方式修改db.updateConf( { transisolation: 1 }, { Global: true } )注意该参数在线生效会在下一次事务中生效通过 transBegin、transCommit 及transRollback 方法用户可以在一个事务中对若干个操作进行事务控制。其使用方式如下 db.transBegin() 操作1 操作2 操作3 ... db.transCommit() or db.transRollback()在上述使用模式中用户必须显式调用transCommit 及 transRollback 方法来结束当前事务。然而对于写事务操作若在操作过程发生错误数据库配置中的 transautorollback 配置项可以决定当前会话所有未提交的写操作是否自动回滚。transautorollback 的描述如下注意该配置项只有在事务功能开启(即 transactionon 为 true )的情况下才生效。默认情况下transautorollback 配置项的值为 true。所以当写事务操作过程出现失败时当前事务所有未提交的写操作都将被自动回滚。事务自动提交 数据库配置中关于事务自动提交的配置项如下事务自动提交功能默认情况下是关闭的。当transautocommit 设置为 true 时事务自动提交功能将开启。此时使用事务存在以下两点不同用户不需要显式调用 transBegin 和transCommit 或者 transRollback 方法来控制事务的开启、提交或者回滚。事务提交或者回滚的范围仅仅局限于单个操作。当单个操作成功时该操作将被自动提交当单个操作失败时该操作将被自动回滚。例如如下操作中 /* transautocommit 设置为 true */ db.foo.bar.update({$inc:{salary: 1000}}, {department: A}) // 更新 1 db.foo.bar.update({$inc:{salary: 2000}}, {department: B}) // 更新 2 db.foo.bar.update({$inc:{salary: 3000}}, {department: C}) // 更新 3 ...更新 1、更新 2、更新 3 分别为独立的操作。假设更新 1 和 更新 2 操作成功而更新 3失败。那么更新 1 和 更新 2 修改的记录将全部被自动提交。而更新 3 修改的记录将全部被自动回滚。其它配置数据库配置中关于事务的其它主要配置项如下调整设置当用户希望调整事务的设置时(如是否开启事务、调整事务配置项等)有如下 3 种方式供用户选择使用用户可以将数据库配置描述的事务配置项配置到集群所有(或者部分)节点的配置文件中。若修改的配置项要求重启节点才能生效用户需重启相应的节点。使用 updateConf()命令在 sdb shell 中修改集群的事务配置项。若修改的配置项要求重启节点才能生效用户需重启相应的节点。使用 setSessionAttr()命令在会话中修改当前会话的事务配置项。该设置只在当前会话生效并不影响其它会话的设置情况。2. SequoiaDB并发与锁操作实践示例建立数据库以及表mysql use company;Database changedmysql create table t1 (a int,b int);Query OK, 0 rows affected (0.03 sec) 1)事务提交与回滚例子1使用事务回滚插入操作。事务回滚后插入的记录将被回滚集合中无记录mysql begin;Query OK, 0 rows affected (0.00 sec)mysql insert into t1 values(1,1);Query OK, 1 row affected (0.08 sec)mysql select * from t1;------------| a | b |------------| 1 | 1 |------------1 row in set (0.00 sec)mysql rollback;Query OK, 0 rows affected (0.01 sec)mysql select * from t1;Empty set (0.00 sec)例子2使用事务提交插入操作。提交事务后插入的记录将被持久化到数据库mysql begin;Query OK, 0 rows affected (0.00 sec)mysql insert into t1 values(1,1);Query OK, 1 row affected (0.00 sec)mysql commit;Query OK, 0 rows affected (0.01 sec)mysql select * from t1;------------| a | b |------------| 1 | 1 |------------1 row in set (0.01 sec)2)隔离级别为RU并发与锁例子3在隔离级别为RU(transisolation 设置为0)的情况下设置当前会话级(会话1及会话2同时设置)隔离级别为read uncommitted mysql SELECT tx_isolation;-----------------| tx_isolation |-----------------| REPEATABLE-READ |-----------------1 row in set, 1 warning (0.00 sec)mysql set session transaction isolation level read uncommitted;Query OK, 0 rows affected (0.00 sec)mysql SELECT tx_isolation;------------------| tx_isolation |------------------| READ-UNCOMMITTED |------------------1 row in set, 1 warning (0.00 sec)在窗口1会话1对该表写入数据并不提交。mysql create table t3(a int,b int);Query OK, 0 rows affected (0.02 sec)mysql begin;Query OK, 0 rows affected (0.00 sec)mysql insert into t3 values(1,1);Query OK, 1 row affected (0.03 sec)在窗口2会话2对该表进行查询查到的是未提交的数据。mysql select * from t3;------------| a | b |------------| 1 | 1 |------------1 row in set (0.01 sec)小结由于采用了隔离级别是RU允许脏读在第二个会话中不会产生锁等待而直接会读到未提交的数据。该隔离级别建议设置在以读为主的历史数据平台应用中在真实的OLTP环境中不能满足业务需求。这样业务查询会读取到未提交事务的修改如果事务发生回滚那么读取的数据是错误的。不能满足一致性的要求。3)隔离级别为RC并发与锁RR隔离级别的实现概述巨杉数据库在RC隔离级别上除了支持传统关系型数据库的读已提交以外通过MVCC多版本访问的方式支持读取最后一次提交的版本而不会产生锁等待从而提高业务的并行处理能力。例子4在隔离级别为RC(transisolation 设置为1translockwait为true)的情况下看看并发情况首先修改隔离级别为1translockwait为true该修改将在下一次连接的时候生效。 db.updateConf({transisolation:1},{Global:true});Takes 0.051656s. db.updateConf({translockwait:true},{Global:true});Takes 0.041699s. db.updateConf({transactiontimeout:30},{Global:true});Takes 0.099934s. db.updateConf({transautocommit:true},{Global:true});Takes 0.040183s.备注也可以通过mysql端进行当前session隔离级别参数的修改。设置当前会话级(会话1及会话2同时设置)隔离级别为read committed mysql set session transaction isolation level read committed; Query OK, 0 rows affected (0.00 sec)在窗口1事务1对该表写入数据并不提交。mysql create table t4 (a int,b int);Query OK, 0 rows affected (0.09 sec)mysql begin;Query OK, 0 rows affected (0.00 sec)mysql insert into t4 values(1,1);Query OK, 1 row affected (0.03 sec)在窗口2事务2对该表进行查询可以看到一直会处于等待锁的状态直到锁超时(transactiontimeout设置为30秒)退出。mysql select * from t4;ERROR 1030 (HY000): Got eror 40013 from storage engine通过捉取锁的快照可以看到第一个事务持有锁的信息持有该表上的IX,IS锁以及记录上的X锁。如下图所示 而第二个事务等待锁的情况在等锁该表记录上的S锁。如下图所示 小结由于采用了隔离级别是RC 并且translockwait设置为true的情况下在第二个事务中会产生锁等待直到第一个事务释放该表上的行锁第二个事务才能执行否则会一直等待到锁超时退出为止。这也是大多数传统关系型数据库的默认隔离级别。例子5在隔离级别为RC(transisolation 设置为1translockwait为false)的情况下看看并发情况我们先来看看translockwait设置为false的说明: 不等待记录锁直接从系统读取最后一次提交的版本。设置SDB参数配置 db.updateConf({translockwait:false},{Global:true});Takes 0.041699s.备注可以通过mysql端进行当前session隔离级别参数的修改。设置当前会话级(会话1及会话2同时设置)隔离级别为read committed mysql set session transaction isolation level read committed; Query OK, 0 rows affected (0.00 sec)在窗口1事务1对该表写入数据并不提交。mysql create table t5 (a int,b int);Query OK, 0 rows affected (0.09 sec)mysql begin;Query OK, 0 rows affected (0.00 sec)mysql insert into t5 values(1,1);Query OK, 1 row affected (0.03 sec)在窗口2事务2对该表进行查询可以看到马上返回并没有发生锁等待的情况。这时候查到的数据是最后一次提交的版本mysql select * from t5;Empty set (0.01 sec)小结由于采用了隔离级别是RC 并且translockwait设置为false的情况下在第二个事务不会产生锁等待而是会读到最后一次版本已提交的数据。通过锁快照也可以看到没有任何锁等待的情况出现。该隔离级别设置适用于绝大多数的OLTP场景。 4)隔离级别为RS并发与锁例子6在隔离级别为RS(transisolation 设置为2)的情况下看看并发情况 db.updateConf({transisolation:1},{Global:true});Takes 0.051656s.在窗口1事务1对该表进行查询数据不提交。mysql create table t6 (a int,b int,primary key(a));Query OK, 0 rows affected (0.09 sec)mysql insert into t6 values(1,1);Query OK, 1 row affected (0.03 sec)mysql insert into t6 values(2,1);Query OK, 1 row affected (0.03 sec)mysql begin;Query OK, 0 rows affected (0.00 sec)mysql select * from t6;------------| a | b |------------| 1 | 1 || 2 | 1 |------------1 row in set (0.01 sec)在窗口2事务2对该表进行更新可以看到处于锁等待的状态。最终锁超时事务进行回滚:mysql egin;Query OK, 0 rows affected (0.00 sec)mysql update t6 set b11 where b1;ERROR 1030 (HY000): Got error 40013 from storage engine通过捉取锁的快照可以看到第一个事务持有锁的情况查询拿到了该表上的S锁。如下图所示而事务2需要取到该表上的X锁而产生了等待如下图所示 小结由于采用了隔离级别是RS ,在第二个事务更新的事务会产生锁等待任何事务查找的记录都不允许更新直到该读取的表的锁被释放。通过锁快照也可以看到有锁等待的情况出现。RS场景并发性较差一般适应于总帐计算系统系统。查到的数据该事务不提交侧不允许被修改。5)隔离级别为RR并发与锁RR隔离级别的实现概述在多版本控制技术的事务锁实现中RR(可重复读)配置下的读操作可以在使用完记录之后立即释放锁不需要一直持有直到事务提交或者回滚。但是写事务操作则需要一直持有插入、更改和删除的锁直到事务完成提交或者回滚。巨杉数据库锁的实现是采用悲观锁机制与传统关系型数据库的采用的主流锁机制类似。在多版本控制技术的实现中除了引入悲观锁的机制以外巨杉数据库还采用了内存老版本机制提升数据库并发访问及操作的能力。内存老版本是通过在记录锁上附加有一个存储原版本数据和索引相关的结构于内存中存储了老版本的数据。以下通过实例操作进行详解例子7在隔离级别为RR(transisolation 设置为3)的情况下看看并发情况 db.updateConf({transisolation:3},{Global:true});Takes 0.051656s. db.updateConf({mvccon:true},{Global:true})Takes 0.156197s. db.updateConf({globtranson:true},{Global:true})Takes 0.051241s.备注打开RR隔离除了transisolation 设置为3以外需要修改以上多二个参数。mvccon及globtranson这二个参数为true。通过mysql端也可以直接进行设置。通过mysql直接设置当前会话级(会话1及会话2同时设置)隔离级别为REPEATABLEREAD;mysql set session transaction isolation level REPEATABLE READ; Query OK, 0 rows affected (0.00 sec)在窗口1事务1对该表rr进行查询数据不提交mysql create table rr (a int);Query OK, 0 rows affected (0.06 sec)mysql insert into rr values(1);Query OK, 1 row affected (0.19 sec)mysql begin;Query OK, 0 rows affected (0.00 sec)mysql select * from rr;------| a |------| 1 |------1 row in set (0.00 sec)在窗口2事务2对该表rr(事务1第一次查询后)进行数据更新后提交。mysql begin;Query OK, 0 rows affected (0.00 sec)mysql update rr set a2 where a1;Query OK, 1 row affected (0.01 sec)Rows matched: 1 Changed: 1 Warnings: 0mysql commit;Query OK, 0 rows affected (0.01 sec)在窗口1事务1对该表rr进行再次查询数据查询到的数据可以看到不会由于事务2的更新提交而改变而是读到事务开始前的版本数据。mysql select * from rr;------| a |------| 1 |------1 row in set (0.00 sec)通过捉取锁的快照可以看到第一个查询的事务1在整个事务的查询中没有持任何锁,而事务2更新的操作持用该表的行锁。如下所示事务1持有锁的情况(未持有锁) 事务2持有锁的情况持有该表的X锁如下图所示 小结由于采用了隔离级别是RR ,任何查询都不会持用锁也不会等锁可以看到在第二个事务更新的操作不会影响事务1任何更新的操作不会影响查询由于事务1是在事务2之前执行查询当前事务1始终查到的是rr表的事务2的更新前版本。该隔离级别适应于大并发查询的交易场景能有效提高整个应用的并发性。05总结巨杉数据库完整支持传统关系型数据库的几种常用隔离级别可满足所有核心生产场景(OLTP及OLAP等场景)需求。创新性采用事务锁、内存老版本以及磁盘回滚段重建老版本的设计来实现了多版本并发控制技术。通过对内存结构的合理利用存储数据和索引的老版本信息从而实现多版本数据的快速的并发访问。