康慧欣 2015-09-08
并发事务的定义:多个事务同时发生,会产生5中并发问题(3个读,两个更新),但其实在数据库的某一个时刻,只会执行一个sql,但一个事务可能包含多个步骤(sql),这样可能多个事务的多个步骤交叉执行,而产生并发问题。
并发一般可以采取锁和隔离级别进行处理。
那锁和隔离级别的区别在于哪里:
隔离级别----解决看的问题--select
锁----解决更新的问题。
例如spring的事务就可以配置隔离级别,不同的隔离级别会影响事务在select时所查询的结果。
不过我们一般很少使用,一般使用数据库默认的隔离级别ReadCommitted,即只能查看已经提交的事务的数据。
锁一般可以分为:共享锁,独占锁。
按记录分又分为:表锁,行锁
我们在select时即取的是表的共享锁,那么独占锁,使用select...forupdate实现
如果一个事务取得了独占锁,那么其他事务必须等待,等待那个事务提交后释放独占锁才能继续执行。
我们常见的锁解决方案如乐观锁:解决多个事务修改相同资源造成的不可重复读和第二类更新丢失。如hibernate的version或timestamp
那么如果在如下场景,比如有golf场地,每个预订可以选择一个时间段,那么场地的可用要受时间的影响,在每次预订前,都要检查在该时间段是否有可用场地。在多线程情况下,控制这样并发insert乐观锁是做不到的,只能使用悲观锁,控制事务的并发。
如何使用悲观锁呢?
可以使用hibernate的Session的Objectload(ClasstheClass,Serializableid,LockOptionslockOptions);方法,新建一个T_LOCKS表,用来实现悲观锁,
XXXServiceImpl.java
lockDao.getLock(); //一系列dao操作 .......
hibernate的Query和Criteria都支持锁:
Criteria criteria = session.createCriteria(TLock.class); criteria.add(Restrictions.eq("lockName", "facilitylock")); criteria.setLockMode(LockMode.UPGRADE); criteria.uniqueResult() ; Query query = session.createQuery("from TLock where lockName= :lockName"); query.setParameter("lockName", "facilitylock"); query.setLockOptions(LockOptions.UPGRADE); query.uniqueResult();
注意:select...fromtableNameforupdate必须保证一定能查到记录,不然,不会有独占锁。因此,可以专门设计一张表t_locks用来存储各个需要使用独占锁的情况下去取对应的记录行的独占锁
个人感觉,数据库悲观锁类似于java的同步synchronized概念和作用,但在集群环境下java的synchronized不行,可以使用数据库悲观锁代替。
但是经使用发现mysql的并发处理性能在线程数到达100-200的时候明显降低,对于这种情况使用分布式锁,比如zookeeper是非常好的一个解决方案,zookeeper的应用场景之一就是分布式锁,而且性能经验证比mysql强很多。
分布式锁是同一进程内所有的线程共享的,所以分布式锁是用来控制集群环境中各节点的访问;
java的Synchronized是线程同步锁,只能允许一个进程的一个线程访问;
因此在分布是环境中,要控制同步,要加两个锁:一个是分布式锁,一个是线程锁(java的Synchronize方法就可以)。
Curator是zoookeeper客户端框架,提供zookeeper的各种应用场景的封装API.