您的位置:首页 > 其它

使用SequoiaDB驱动多线程开发常见错误分析

2015-08-14 13:22 316 查看

一、摘要

工作上的需要,最近使用国内的一款NoSQL数据库SequoiaDB。由于对SequoiaDB不太了解,刚开始在驱动的使用上踩了不少坑,这里做一个简单的分享。SequoiaDB在多线程应用中,需要确保一个连接对象(以及由其衍生出来的所有集合或其它对象)不会同时被多个线程所访问。大家在使用驱动进行多线程开发时,如果使用不当,有可能会遇到各种各样的问题。本文将给大家讲述在使用SequoiaDB驱动进行多线程开发时,应该注意的问题。

二、现象描述

下面是使用使用不当,而可能导致的一些结果:

客户端收到-65(Unexpected result received)错误。

客户端卡死

客户端得到不正确的结果

客户端崩溃

为了分析以上几种错误,我们需要了解SequoiaDB驱动在实现上的一些细节。

目前(1.12)SequoiaDB驱动一般会有以下几种类(以本人使用Java驱动为例,其它驱动也一样):

Sequoiadb

CollectionSpace

DBCollection

DBCursor

DBLob

DBDomain

ReplicaGroup

ReplicaNode

这些类在实现上,有以下两个特点:

1. 所有对象共用Sequoiadb对象的socket,如下面代码所示:

Sequoiadb db       = null;
CollectionSpace cs = null;
DBCollection cl    = null;
DBCursor cursor    = null;
DBLob lob          = null;
...

db = new Sequoiadb("192.168.20.42", 11810, "", ""); // 建立连接
cs = db.createCollectionSpace("foo");
cl = cs.createCollection("bar");
cursor = cl.query();
lob = cl.createLob();
...


建立连接后,db对象将持有一个socket对象,当db对象创建cs对象,cs对象创建cl对象,cl对象创建cursor,lob对象后,所有对象都共用db所持有的socket对象。如果将这些对象分配到不同的线程中去使用,将会面临一个socket对象在多个线程中使用的问题。目前,SequoiaDB驱动并没有对这个共用的socket对象加锁保护。所以,将这些共用同一个socket的对象放到不同的线程去使用是不安全的。

2. 每个对象都有自己的send buffer/receive buffer

上述的每个对象,都有各自的独立的send buffer/receive buffer。这些buffer在使用的时候也是没有加锁保护的。如果将一个对象,放在多个线程中去使用,有可能出现对象发送或接收的内容被修改的情况。

SequoiaDB所有驱动都没有加锁保护。由于存在以上两点,大家在使用多线程开发时,需要合理使用这些对象。

三、问题分析

对上面列出的几种错误情况,下面讲述它们出错的原因。

1. 客户端收到-65(Unexpected result received)错误。

当多线程使用不当时,客户端最容易收到-65(Unexpected result received)错误。SequoiaDB驱动每次和数据库交互时,都会校验它发送和接收的消息。如果驱动发送的是一个query的请求,但却收到一个update请求的应答,那么驱动将报出一个-65的错误。造成这种收错消息的原因基本上就像上面所说的:1、共用一个socket的两个对象放在不同的线程中使用,造成彼此收错消息的情况。2、同一个对象(假设为cl对象),放在了不同的线程中使用,cl在A线程中把query请求的应答收回到receive buffer中,准备提取query的结果时,cl在B线程中update请求的应答刚好也收回到receive buffer中,并把query请求的内容覆盖掉,这时,cl在A线程中便出现收错消息的情况。

2. 客户端卡死

这种情况发生在客户端发送一个请求给数据库,而数据库没有发应答数据包给客户端的时候。在Sequoiadb驱动与数据库交互的命令中,有几个命令数据库是不需要回复应答给驱动的,disconnect就是其中一个。考虑以下场景:db对象被放到A、B两个线程。在A线程中,db发了一个create collection space的消息给数据库,数据库收到这个请求,需要回复一个应答给驱动。但是,在A线程中,db对象准备发送消息的前一刻,send buffer中的内容被B线程中的db对象修改了,改为了发送一个disconnect的消息。这样,数据库只收到一个disconnect的消息,它不会给A线程的db对象回应答包,于是,A线程就出现死等的情况。

3. 客户端得到不正确的结果

这个情况和收到-65错误的情况很相似。假设在A、B两个线程中,共用同一个socket的两个cl对象都向数据库发送不同条件的query请求,而结果它们又彼此收错了对方的应答消息包,那么业务层必然得到错误的结果。

4. 客户端崩溃

对象的send buffer/receive buffer的大小根据不同消息命令可以设置不同的大小,如果将一个对象放在不同线程去使用,有可能出现A线程在使用buffer的时候,buffer刚好被B线程删除掉,重新建。这时,A线程就极有可能因为访问非法内存而导致整个客户端崩溃。另外一种可能是,A线程在发送或接收前一刻,socket资源被B线程释放了,那么,A线程也面临着访问非法内存而导致客户端崩溃的问题。

四、建议

以上分析了几种在多线程中不恰当使用对象而导致错误的情况。目前,SequoiaDB驱动对于多线程并发的场景还有很大可以提升的空间。如果大家需要使用多线程开发,应当避免:

将共用同一个socket的几个对象放到不同的线程使用。

将一个对象放到不同的线程中使用。

大家可以如下使用SequoiaDB驱动在多线程中开发:

共用同一socket的对象只能被同一线程持有

确保共用同一socket的所有对象都释放后,再释放db对象(Sequoiadb对象)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: