说一说数据库的并发控制
最近在看Elasticsearch时看到了并发控制,由此看到了新的并发控制方式。不得不说Elasticsearch相较于关系型数据库就是两种理论建立的数据存储体系,当然它们在并发控制上也相差甚远,各有千秋。
什么是并发冲突
说到并发就不得不提一下串行,所谓串行就是数据库操作事物按照特定的顺序从上到下执行,在串行操作某个数据表的某个字段的值时写入和读取都很好理解,且不会发生异常。但是往往线上数据操作会发生各种异常,因为线上数据库对用户来说不可能是串行访问的,往往是并发访问。就会产生如写入并发造成的覆盖现象,读写并发造成的脏读现象等。下面简单介绍一下这些异常形成的原因。
A获取字段X的值为3将它减1然后写入数据库X为2,当B在A写入之前同时获取X的值为3将它减1然后写入数据库X为2。
可以看到上面X经过两次减1操作,但是它的值只减了1,因为B将A的操作覆盖了。
如果X的值为1,A想将X的值减1,如果X等于0则返回,当A将减1操作执行完成前,B同样进行这个操作,B取到的X也等于1,然后执行减1操作,会是X的值变为-1。
上面的例子说明了并发操作中存在冲突,需要一种机制来处理这种冲突。于是锁的机制就应运而生来。锁顾名思义就是将需要执行的数据锁起来,从而让他从并发变成串行。
如何治理并发冲突
悲观锁并发控制
悲观锁对数据被修改持悲观态度。即每次获取数据的时候,都会担心数据被修改,所以每次获取数据的时候都会进行加锁,确保在自己使用的过程中数据不会被别人修改,使用完成后进行数据解锁。由于数据进行加锁,期间对该数据进行读写的其他线程都会进行等待。
读写锁
在数据库中,锁被设计为两种模式,即共享锁(读锁)和互斥锁(写锁)。当一个事物获得共享锁之后,它只能进行读操作;当一个事物获取一行数据当互斥锁时,就可以对该行数据进行读和写操作。
多个事物可以同时获取同一行数据的共享锁,但互斥锁同一时间只能被一个事物获取。没有获得锁但事物将处于等待状态。
两阶段锁协议(2PL)
两阶段锁协议是一种能够保证事物可串行化但协议,它将事物的获取锁和释放锁分成增长和缩减两个不同的阶段。在增长阶段,一个事物可以获得锁但是不能释放锁;而在缩减阶段事物只可以释放锁,不能获取新的锁。
- Strict 2PL 事物持有的互斥锁必须在提交后再释放
- Rigorous 2PL 事物持有的所以锁必须在提交后释放
乐观锁并发控制
乐观锁并不是真正的锁,而是一种并发控制的思想。它可以基于各种协议来实现这种并发控制。不同的事物按照锁协议同一数据项依次执行,因为后面执行的事务想要获取的数据已经被前面的事务加锁,只能等待锁的释放。
基于时间戳的协议
每个事务都有一个全局唯一且随时间递增的时间戳。每一项数据有两个时间戳分别是读时间戳和写时间戳,分别代表事务的开始和结束。这个协议使无论读操作还是写操作都需要比较读写时间戳,如果小于当前值(最后一次操作都时间戳)就会拒绝,然后回滚,然后数据库给这个事务一个新的时间戳重新执行。
基于验证的协议
这个协议根据事务的只读或者更新将所有事务的执行分为两到三个阶段。
在读阶段,数据库会执行事务中的全部读操作和写操作,并将所以写后的值村润临时变量中,并不会真正更新数据库的内容;然后进入下一阶段,数据库会检测当前改动是否合法,即是否有其他事务在读阶段更新了数据,如果通过测试就直接写入数据库,否则就拒绝执行。
多版本并发控制
多版本并发控制意味着更新数据时不覆盖原数据,而是在数据库中保留多个版本,根据需要使用某个版本。它本质上跟上面的不冲突,属于两种解决方案,它并不能解决冲突而是将冲突分割开。