每个线程都调用_ConnectionPtr.Open创建一个实例,连接数据库,目前运行正常的,但是频繁的打开、关闭连接,无论从性能上,还是从稳定性上,都是不太好。但是我又不知道如何利用“连接池”或者线程间共享连接的方法?望各位赐教

解决方案 »

  1.   

    1、导致连接失败的原因应该是死锁,这时很多进程都在等待其他的进程释放连接,如果死锁,肯定导致连接失败3、连接最好不要放到Session和Application中去,这样在访问人数多的时候容易死锁,ADO不是线程共享的,如果ADO连接放到Session和Application中,连接一直不能被释放,而总的连接数是固定的,其他的程序就一直等待连接的释放,从而导致请求的队列越来越长。
      

  2.   

    谢谢 CoolAbu的回复!
    1。我也认为是因为事务死锁的原因,导致连接失败。但是这个死锁是因为同一个进程的多线程间竞争资源导致的?还是多个进程间的不同权限数据库操作,比如查询、更新、删除等导致的?还是不同数据库用户登录之后访问同一表格导致的?我一直找不到具体的理由。
    2。你的回复我看不太明白,Session到底是一个什么概念呢?“连接最好不要放到Session和Application中去,这样在访问人数多的时候容易死锁,ADO不是线程共享的,如果ADO连接放到Session和Application中,连接一直不能被释放,而总的连接数是固定的,其他的程序就一直等待连接的释放,从而导致请求的队列越来越长。”
    连接不放入应用程序中,那我将其放入何处呢?
      

  3.   

    另外,谁知道如何查看调试ADO的返回错误呢?如果_ConnectionPtr.Open()发生异常,可以通过_com_error得到错误信息,但是如果没有异常发生,只是返回FAILED(HRESULT)返回TURE,(就是Open调用失败),这时我到哪里查找错误信息呢?MSDN中一直没有找到,高人指点。
      

  4.   

    对于你的A.exe,是否可以一开始就建立6~10个连接,然后每一个新的线程,
    需要使用ADO,时就从这些连接里取一个,用完了再放回去(实际上就是自己
    实现连接池而已)
    至于死锁问题,可以检查,
    1)是不是在用户进行数据输入时,就启动了一个事务
    2)是不是启动了事务一直没提交
      

  5.   

    用FAILED(hr)里hr的值转成16进制,然后在MSDN里面搜索这个值就可以得到错误码。共享Connection或者可以这样:
      建立一个进程共享区,当用户请求连接并在查询完成要释放连接时,不关闭此连接,而是把这个连接放到共享区中。下一个用户请求查询时,先从共享区找有无可用连接,如果有,使用该连接查询,如无,则新建一个,使用完毕后同样放入共享区。
    这样带来的问题:
      1、共享区里的连接要做保护,以免不同进程同时使用一个连接;
      2、要限制共享区连接的数目,以免资源耗尽;
      3、要写一个查询可用连接的算法。仅供参考,希望对楼主有所帮助。
      

  6.   

    谢谢Blackcolor的回复。
    你说的关于共享连接池的做法,应该是完全可以的,但是这里涉及了Aizz所说的几个问题,必须解决这些问题,才能很好的建立连接池。难道旧贴中各位高手谈到的“连接池”就是这个意思吗?需要自己维护、管理连接?目前我的做法是将一个连接作为全局变量来实现,每个线程都共享这个数据库连接(这里是不是要考虑ADO是否线程安全的问题呢?必须经过实践得知。)另外,你所说的死锁问题如何检测呢?通过“事件探查器”吗?
    因为我所用到的仅是一些Update、Select、Insert语句,所以事务的概念是ADO自身为我建立、提交的,所以这里应该不是因为事务没有提交而导致的,另外a.exe是一个服务程序,没有与用户进行交互。
      

  7.   

    楼上Aizz兄弟说得想法非常明白,我估计无论是何种连接池,Socket、或者数据库连接在实现时都可以参照你的想法,不过对于我来说,就有点太麻烦了,我想微软应该为我们想到了一种省力的维护连接的方法,或者系统本身提供了“连接池”这么一种机制,我们直接使用就是了,一直在寻找中。
    另外,如果HR的值转换为16进制之后,是0x80004005的话,这个代表什么意思啊?在MSDN中我一直没有找到响应的错误解释,还望老兄明示啊。
      

  8.   

    你可以多个进程公用一个connection,这样只有一个连接,但是可以执行多个操作(通过,adocommand,执行sql 语句)
      

  9.   

    0x8000405: Unspecified error(未定义错误)呵呵...
      

  10.   

    你可以在Watch窗口中监视hr的值,并在后面加上",hr"格式符即可看到相应的英文信息。当然,有部分自定义的就没有办法了。
    另外,我认为ADO是线程安全的。起码我的程序到现在还没有出现过问题。
      

  11.   

    ado.net内部封装了连接池,转用dot net吧:)
      

  12.   

    lyhold(让你飞) ,如果多个进程公用一个连接,通过CADORecordset查询、或者通过CADODatabase执行update、Insert操作应该没有问题吧?我并没有使用CADOCommand。
      

  13.   

    jeffchen(Jeff),不知道我们所理解的“线程安全”的概念是否一致,这个问题好像是COM控件自身机制所决定的,当COM控件运行于不同的线程模型中,比如单线程公寓模型、或者多线程公寓模型的时候,COM控件所输出的属性是否能被各线程共享,我个人猜测就是这个问题了。等我仔细看看资料,再做论断。
      

  14.   

    winvxd(小文),你所说的“要用的时候打开连接,用完马上释放,不会出现你说的这个问题”,关键是如何打开、如何关闭?如果调用_ConnectionPrt.CreateInstance来创建实例,调用_Connection.Open打开连接的话,那么调用_ConnectionPtr.Close能不能算是关闭连接呢?(因为我不知道实例所占用的资源是否被系统回收?请高手明示)。如果这样做的话算是安全打开和关闭的话,那么我的程序理论上没有问题,但是却出现了错误,原因估计没有释放资源导致的。
      

  15.   

    关于连接池,
    我所了解的资料中,连接池是com+的特性,
    请参考我以前所写的关于COM的一些文章
    http://www.csdn.net/develop/read_article.asp?id=9512
    另外,就算是自己管理ADO的连接池,也不是特别复杂
      

  16.   

    1. ADO连接池其实就是OLEDB连接池,是存在的。不过,MSDN的资料显示,连接池应该有至少一个常驻的连接存在才会起作用,所以应该创建一个全局的连接并打开它。然后,在应用程序中创建每次可用的临时连接对象,再使用它。全局对象在程序退出释放,但并不使用。你可以测试这样做的效率和共享一个连接对象的效率。
    2. 死锁的原因是因为事务中加锁的顺序。如果都是隐式的事务即单条SQL语句基本不会造成死锁。显式事务和游标操作即recordset的movenext等,会造成死锁,要分析事务的加锁过程,更新锁、只读锁、独占锁等的次序问题。如果事务的开始就是独占锁或更新锁大多数情况不会死锁。
    3. COM的智能指针_ConnectionPtr已经封装了异常机制不需要FAILED(HRESULT),源程序if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    4. _ConnectionPtr.Close是关闭连接但不是释放COM对象,释放对象要_ConnectionPtr.Release()。
    5. ADO的程序注意MDAC的版本,最好2.6以上。
      

  17.   

    blackcolor(blackcolor),因为我对COM不熟悉,以后我会慢慢看你的原创文章。连接池我会参考楼下ntr所描述的方法才是看看,因为在测试中发现,如果所有的线程共享一个ADO连接,就会出现错误:“连接占线导致另一个命令”,来源是“OLE DB Provider for Sql server”。
      

  18.   

    谢谢ntr的回复,你的回复让我看到了解决问题的曙光啊。但是有几个问题需要请教:
    1。我已经建立了一个全局连接,但是多线程共享使用出现了错误,所以此方法不妥啊,不过看了你的描述,我必须在每个线程中再次建立临时连接对象,然后在使用它。这里有一个问题,就是对于临时对象的管理问题,我必须使用_ConnectionPtr.Release来释放COM控件实例吗?还是仅仅调用Close关闭连接就可以了?
    2。关于死锁的问题,对于SQL Server中的锁机制,应该是仅作用于数据库的对象,比如表、库、表中的记录等。所以无论是单个进程中的多个线程开启的多个连接,还是多个进程不同数据库用户登录后开启的多个连接,都是一样的,即锁机制与线程、进程无关,仅于数据库对象有关,不知道我的理解对不对?望你明示啊。我的应用中大部分都是SQL语句,不过也利用了Recordset处理Select语句返回的结果,但是我自己应用程序中的Select、Update等语句我可以协调,但是别的进程,比如IIS通过ASP访问数据库的同一表格,这时我就无能为力了,数据库应该协调访问操作吧?
    3。关于_ConnectionPrt.Open返回的HRESULT不等于S_OK,你的意思是我的程序中没有必要判断FAILED(HR),因为_ConnectionPtr自身会抛出异常?
    4。如何明确知道MDAC的版本呢?
    期待你的回复。
      

  19.   

    1. 原则上调用Close就可以了,_ConnectionPtr对象析构时会调用Release。
    2. 锁机制由数据库提供无需应用程序实现,应用程序的线程和数据库线程也不同。数据库当然会协调锁机制,不管是那个应用程序的那个线程。死锁的关键是事务的过程中可能对不同的资源先后要进行加锁造成的冲突。如果你有BeginTrans等事务语句不妨先注释,看看是不是这个原因。
    3. 对,如果ADO的版本没问题,应该能抛出异常的。你可以看msado15.tli中的源程序。
    4. 看C:\Program Files\Common Files\System\ado下的msado15.dll的版本。(操作系统在C盘)
      

  20.   

    pooling是ado自己实现的,对开发者是透明的,不过要想充分利用ado的pooling机制,需要注意几点问题
    第一:必须用完全同样的连接字符串
    第二:用完connection对象后,尽量快的调用close()关闭它,在vb,vbscript里也要显式的调用close()
    这样ado会自动把连接放入池中,直到超时或应用程序关闭。com+里的pooling和ado的pooling基本上是不相干的,只有当com+对象池中某对象被完全清除时,和此对象联系的ado连接池中的连接也会清空。ado.net中连接池和ado中基本相同,只不过你可以明确的选择不使用连接池机制。
      

  21.   

    jorry(乖乖虫),你说的两点我有点明白了,只是在具体操作时,如何做到呢?把ADO连接都声明局部变量的话,函数一旦调用返回,就会被Release掉,所以是不是把ADO连接实例定义为全局变量呢?还是无论如何使用,ADO内部会自动实现?
      

  22.   

    ntr(NiuTianrui),你说得我都明白了,下一句就是如何协调各程序之间对表访问操作了。
      

  23.   

    和申明成什么变量是没有关系的。ADO和你的应用是两个层面上的概念,对ADO来说,他并不知道他返回的连接是被当作全局变量还是局部变量。ADO所要做的,就是当连接对象关闭时,把对象放到池里,并管理池的大小和池内对象的生命期。他如何区别不同的连接呢,就是通过连接字符串,只有并只要连接字符串相同,ADO就会首先从他维护的池中取出一个相符的connection对象返回。所以在你的应用中,不论connection对象实例本身是存为全局的或是局部的都是没关系,但要尽快的close(),却与你是否调用release(在vc中)或设成nothing(在vb中)是没关系的,(当然在vc中是必须要掉用release的,我的意思是ado pooling是在open和close中做的,而不是在addref和release中做的)。上面有人讨论的智能指针,当然和pooling就更没关系了。其实只了解这些还是不能非常好的利用pooling机制,ado是个很可恶的东东,在很多情况下它会隐式的建立connection,ado自动建立的连接是不能被pooling的,所以你的代码中要确保在执行是ado不会建立额外的连接。如何防止运行时产生不能被pooling的连接是个非常麻烦的问题,几句话说不清,你去仔细查查msdn吧。
      

  24.   

    另外,问一个问题啊,我写了一个非常简单的StoreProcdure,里面的语句可以单独执行,整个StoreProcdure没有任何效果,语句如下:CREATE PROCEDURE dbo.restorepatient  
    @patid char(50) ,
    @Volume varchar(100),
    @LocalPath varchar(200) 
    AS
    BEGIN
    --update 'volume' field of Series table
    update Series
    set Series.Volume=@Volume
    from Series INNER JOIN Image ON Series.SeriesID=Image.SeriesID
    where  Image.ImageName like @patid and Image.LocalPath like '%_TMP' --update 'Volume' and 'LocalPath' fields of Images table
    update Image
    set Image.Volume=@Volume ,Image.LocalPath=@LocalPath
    from Image 
    where  Image.ImageName like @patid and Image.LocalPath like '%_TMP'
    END
    GO对于这种情况,通常是因为什么原因引起的呢?
    exec restorepatient @patid='02-8171%',@Volume='MR_GEMS8610_disk3',@LocalPath='c:\dicom\MR_GEMS8610_disk2'
    go执行后的结果:(所影响的行数为 0 行)
      

  25.   

    jorry(乖乖虫) ,谢谢你的回复,让我澄清了一些概念。
    1。你对楼上的ntr做的回复,“MSDN的资料显示,连接池应该有至少一个常驻的连接存在才会起作用,所以应该创建一个全局的连接并打开它。然后,在应用程序中创建每次可用的临时连接对象,再使用它。”,有何理解和认识呢?
    2。说到隐含连接,本站上有一片zhengyun_ustc写的一篇文章,
    http://www.csdn.net/develop/read_article.asp?id=15591
    你看看是不是有同感?但是MSDN中关于此问题的描述还是比较少的。
      

  26.   

    回答简单的
    catch(_com_error &e)
    {
    CString ErrorStr;
    _bstr_t bstrSource(e.Source());
    _bstr_t bstrDescription(e.Description());
    ErrorStr.Format( "Error\n\tCode = %08lx\n\tCode meaning = %s\n\tSource = %s\n\tDescription = %s\n",
    e.Error(), e.ErrorMessage(), (LPCSTR)bstrSource, (LPCSTR)bstrDescription );
    AfxMessageBox( ErrorStr, MB_OK | MB_ICONERROR );
    }
      

  27.   

    再回答简单的
    CREATE PROCEDURE dbo.restorepatient  
    @patid char(50) ,
    @Volume varchar(100),
    @LocalPath varchar(200) 
    AS
    更改为
    CREATE PROCEDURE dbo.restorepatient  
    @patid varchar(50) ,--使用中发现的
    @Volume varchar(100),
    @LocalPath varchar(200) 
    AS
      

  28.   

    谢谢oracle3(kk) 的回复。
    1。前面关于错误的处理我知道,因为msado.tli中就是这么处理的,并且我所使用的ADO类中也是这么处理异常的,一旦报告“未定义错误”,就不知道该怎么办了。
    2。存储过程的问题解决了,数据类型导致执行没有效果,原来SQL Server对数据的长度有限制,必须把char改为varchar才行。但是MSDN中没有找到相关的解释。
      

  29.   


    回复人: seth99(seth) ( ) 信誉:100  2002-12-11 0:08:57  得分:0 死锁与登录用户没有关系,关键是看你有多少个连接在同时读写数据,100万,如果不是很复杂的系统或者很糟糕的程序,应该不会很容易出现死锁,如果确实更新很频繁,可以在程序中作处理,例如可以根据需要降低锁定等级,采用batch update,锁定判断等处理避免循环锁就好了,另外不在一个表里放超过1个image字段也可以避免一些不必要的锁定.
      

  30.   

    maybe helpful
    Q. If I use only one Connection object, does that mean I'll have only one connection to my database?A. No. If you ask ADO to accomplish a task and ADO determines that it cannot perform that task because the connection is busy, it will establish a temporary connection to your database in order to complete the task. This behavior is most likely to affect developers using SQL Server who encounter the limitation of one active query per connection. We'll talk about this more in Chapter 7 when we discuss cursors, but keep that distinction in the back of your mind. One Connection object does not equal one connection to your database.
      

  31.   

    fingerfox(foxfinger),这段FAQ来自哪里啊?能不能告诉我网址?
      

  32.   

    本人找到了一些关于Connection Pooling的资料、文章:
    http://www.sql-server-performance.com/bl_asp_ado.asphttp://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnmdac/html/pooling2.asphttp://support.microsoft.com/default.aspx?scid=KB;en-us;q191572
      

  33.   

    通过大家的指导,我已经初步弄明白了问题所在。
    1。关于连接失败的问题,估计是OLE DB Resource Pooling运用不当造成的,
    如果大家有兴趣,可以参阅MSSDK中的一篇文档”Pooling in the Microsoft Data Access Components“,写的非常清楚,详细介绍了连接池的实现、运用等。基本正如ntr、jorry所言,在此谢过两位高手了。
    2。事务死锁的问题,除了更改自己的操作流程,数据处理逻辑之外,好像没有其他的有效办法。
    就此揭帖吧。
      

  34.   

    多个线程完全可以共享一个ado连接的!我处理的时候,我吧ado连接就看作是一个临界区,进行保护!这样虽然速度上比较慢,但是完全可以满足要求!你说的一个线程使用一个连接,我认为不妥!因为在一个复杂的系统中,线程的总数可能成千上万,如果那些线程同时开启,估计数据库服务器会当时死悄悄!使用线程的同步和互斥技术,完全可以防止两个线程再同一个连接上的碰撞。