如何处理并发和同步?
主要是通过锁机制。
悲观锁:指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度.虽然这样保证了线程安全的问题,但是,如果是在“高并发”的场景中。也就是说,会很多这样的修改请求,每个请求都需要等待“锁”,某些线程可能永远都没有机会抢到这个“锁”,这种请求就会死在那里。同时,这种请求会很多,瞬间增大系统的平均响应时间,结果是可用连接数被耗尽,系统陷入异常。
乐观锁:相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。
如一个金融系统,当某个操作员读取用户的数据,并在读出的用户数据的基础上进行修改时(如更改用户帐户余额),如果采用悲观锁机制,也就意味着整个操作过程中(从操作员读出数据、开始修改直至提交修改结果的全过程,甚至还包括操作员中途去煮咖啡的时间),数据库记录始终处于加锁状态,可以想见,如果面对几百上千个并发,这样的情况将导致怎样的后果。
乐观锁机制在一定程度上解决了这个问题。
乐观锁,大多是基于数据版本Version记录机制实现。数据版本,为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是给数据库表增加一个 “version” 字段来实现。
假设数据库中帐户信息表中有一个 version 字段,当前值为 1 ;而当前帐户余额字段( balance )为 50( 50 )。
- 在操作员 A 操作的过程中,操作员 B 也读入此用户信息( version=1 ),并 从其帐户余额中扣除 100-$20 )。
- 操作员 A 完成了修改工作,将数据版本号加一(version=2),连同帐户扣除后余额(balance=$50),提交至数据库更新,此时由于提交数据版本大于数据库记录当前版本,数据被更新,数据库记录 version 更新为 2 。
- 操作员 B 完成了操作,也将版本号加一(version=2)试图向数据库提交数据(balance=$80),但此时比对数据库记录版本时发现,操作员 B提交的数据版本号为2,数据库记录当前版本也为2,不满足“交版本必须大于记录当前版本才能执行更新 “的乐观锁策略,因此,操作员B的提交被驳回。这样,就避免了操作员 B 用基于version=1 的旧数据修改的结果覆盖操作 员 A 的操作结果的可能。 从上面的例子可以看出,乐观锁机制避免了长事务中的数据库加锁开销(操作员A和操作员B操作过程中,都没有对数据库数据加锁),大大提升了大并发量下的系统整体性能表现。
脏数据
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据(Dirty Data),依据脏数据所做的操作可能是不正确的。 `
事务
在关系数据库中,一个事务可以是一条SQL语句,一组SQL语句或整个程序。