spring事务与代码同步问题

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

首先交代一下笔者想要完成的功能:

从http://news.qq.com上收集相应的新闻事件,存入数据库event表,并且将新闻的标题做分词处理,存入tag表。tag和event之间建立关系,event_tag。长话短说,就是要建立一个多对多的关系。

实现方式:

爬虫:webmagic

中间件:spring

持久层:mybatis

其中遇到的一个问题是:因为webmagic一次性开启多个线程,在分词完成以后先将标签存入数据库,数据库标签应该是唯一的(unique_constraint保证)。最初的实现是这样的:

   @Transactional(propagation=Propagation.REQUIRED)	
   private void createTagIndex(Event event){
		String title = event.getTitle();

        //分词,并且去除重复
		Set tags = CharactorSegmentor.seperate(title);

		for(String t:tags){
			Tag tag = tagDAO.getByLabel(t);
			/**
			 *创建tag的时候应该去加锁,保证不会重复插入tag
			 *因为在某一时刻,可能n个线程检测到tag数据库都是null,所以同时插入。
			 *数据库因为unique_constraint 就会报错
			 * */
			if (tag == null)
			{
				lock.lock();

				try{
					tag = tagDAO.getByLabel(t);
					/**
					 * 双重检查,很有可能是自己被同步锁阻塞了,回过头来,其实别人已经插入了。
					 * */
					if(tag == null){
						tag = new Tag();
						tag.setLabel(t);
						tagDAO.insertTag(tag);								
					}
				}
				finally {
					lock.unlock();
				}


			}
			
			connectTagWithEvent(event, tag);
		}
	}

这样想的是没错,在tag保存进入数据库时,很有可能线程A处理的tag当中有"中国",线程B处理的tag当中有"中国",但是数据库当中没有“中国”这个标签,那么AB线程就会竞争。如果不想出现unqiue constraint exception,那么同步就是必须的了。

但是有个问题:当前的方法被声明在一个spring事务当中,结果上面的代码仍然不对。原因是什么呢?

由于事务范围大于锁代码块范围,在锁代码块执行完成后,此时事务还未提交,导致此时进入锁代码块的其他线程,读到的仍是原有的库存数据。

意识到了这一点之后,后面就知道怎么做了,事务应该直接在DAO层声明。就能够解决问题了。从上面代码的执行情况来看,最初是挺慢的,因为数据库当中没有标签,每次都要加锁竞争。到后面,标签很多了,几乎每次遇到的标签都已经保存过了,自然就不会加锁了,速度也就提升了。

 

还有一个小问题需要注意:采用Spring的自动装配,由于我的DAO层不是实现统一接口的,因此在声明事务当中配置应该要这样配置

  

 

这样采用cglib生成动态代理,而不是JDK动态代理。JDK生成动态代理会有类型上的错误(如果不强制转型)

更多更详细的代码:https://github.com/qiulimao/collector/

转载于:https://my.oschina.net/b1ack2ephyr/blog/817958

你可能感兴趣的:(spring事务与代码同步问题)