MSSQL网页编程之编写平安的SQL Server扩大存储历程
一个语句分成两个event(实际上不止,其他可以忽略),一个table_mapevent和一个Rows_log_event。Table_mapevent是一样的,主要看Rows_log_event。server|平安|存储历程SQLServer的扩大存储历程,实在就是一个一般的WindowsDLL,只不外依照某种划定规矩完成了某些函数罢了。克日在写一个扩大存储历程时,发明再写这类静态库时,仍是有一些必要出格注重的中央。之以是会出格注重,是由于DLL运转于SQLServer的地点空间,而SQLServer究竟是怎样举行线程调剂的,却不是我们能懂得的,即使懂得也没法把持。
我们写静态库通常为本人用,即使给他人用,也很少像SQLServer如许,一个静态库很有大概加载屡次,而且都是加载到一个历程的地点空间中。我们晓得,当一个静态库加载到历程的地点空间时,DLL一切全局与部分变量初始化且仅初始化一次,今后再次挪用LoadLibrary函数时,仅仅增添其援用计数罢了,那末很明显,假设有一全局int,初始化为0,挪用一个函数另其自加,此时其值为1,然后再挪用LoadLibray,并使用前往的句柄挪用输入函数输入该值,固然挪用者以为本人加载后当即输入,然后该值的确1而不是0。windows是历程自力的,而在线程方面,假设不注重,下面的情形极可能会程序员带来贫苦。
先容一下我的扩大存储历程,该静态库导出了三个函数:Init,work,Final,Init读文件,存储信息于内存,work复杂的只是向该内存检索信息,Final接纳内存。如上所说,假设不思索统一历程空间屡次加载成绩,两次挪用Init将形成无谓的华侈,由于我第一次已读进了内存,如果经由过程堆分派内存,还会形成内存保守。
我利用的援用计数办理的该成绩,代码很短,间接贴下去:
#include"stdafx.h"
#include<string>
usingnamespacestd;
extern"C"{
RETCODE__declspec(dllexport)xp_part_init(SRV_PROC*srvproc);
RETCODE__declspec(dllexport)xp_part_process(SRV_PROC*srvproc);
RETCODE__declspec(dllexport)xp_part_finalize(SRV_PROC*srvproc);
}
#defineXP_NOERROR0
#defineXP_ERROR1
HINSTANCEhInst=NULL;
intnRef=0;
voidprintError(SRV_PROC*pSrvProc,CHAR*szErrorMsg);
ULONG__GetXpVersion(){returnODS_VERSION;}
SRVRETCODExp_part_init(SRV_PROC*pSrvProc){
typedefbool(*Func)();
if(nRef==0){
hInst=::LoadLibrary("part.dll");
if(hInst==NULL){
printError(pSrvProc,"不克不及加载part.dll");
returnXP_ERROR;
}
FunctheFunc=(Func)::GetProcAddress(hInst,"Init");
if(!theFunc()){
::FreeLibrary(hInst);
printError(pSrvProc,"不克不及取得分类号与专辑的对应表");
returnXP_ERROR;
}
}
++nRef;
return(XP_NOERROR);
}
SRVRETCODExp_part_process(SRV_PROC*pSrvProc){
typedefbool(*Func)(char*);
if(nRef==0){
printError(pSrvProc,"函数还没有初始化,请起首挪用xp_part_init");
returnXP_ERROR;
}
FunctheFunc=(Func)::GetProcAddress(hInst,"Get");
BYTEbType;
ULONGcbMaxLen,cbActualLen;
BOOLfNull;
charszInput={0};
if(srv_paraminfo(pSrvProc,1,&bType,(ULONG*)&cbMaxLen,(ULONG*)&cbActualLen,(BYTE*)szInput,&fNull)==FAIL){
printError(pSrvProc,"srv_paraminfo前往FAIL");
returnXP_ERROR;
}
szInput=0;
stringstrInput=szInput;
stringstrOutput=";";
intcur,old=0;
while(string::npos!=(cur=strInput.find(’;’,old))){
strncpy(szInput,strInput.c_str()+old,cur-old);
szInput=0;
old=cur+1;
theFunc(szInput);
if(string::npos==strOutput.find((string)";"+szInput))
strOutput+=szInput;
}
strcpy(szInput,strOutput.c_str());
if(FAIL==srv_paramsetoutput(pSrvProc,1,(BYTE*)(szInput+1),strlen(szInput)-1,FALSE)){
printError(pSrvProc,"srv_paramsetoutput挪用失利");
returnXP_ERROR;
}
srv_senddone(pSrvProc,(SRV_DONE_COUNT|SRV_DONE_MORE),0,0);
returnXP_NOERROR;
}
SRVRETCODExp_part_finalize(SRV_PROC*pSrvProc){
typedefvoid(*Func)();
if(nRef==0)
returnXP_NOERROR;
FunctheFunc=(Func)::GetProcAddress(hInst,"Fin");
if((--nRef)==0){
theFunc();
::FreeLibrary(hInst);
hInst=NULL;
}
return(XP_NOERROR);
}
我想固然看上往不是很拙劣,但是成绩应当是办理了的。
另有一点申明,为何不利用Tls,厚道说,我思索过利用的,由于实在代码是有一点成绩的,假设一个用户挪用xp_part_init,然后另外一个用户也挪用xp_part_init,注重我们的存储历程但是服务器真个,然后第一个用户挪用xp_part_finalize,那末会如何,他仍旧能够一般利用xp_part_process,这倒无所谓,但是第一个用户挪用两次xp_part_finalize,就可以够影响第二个用户了,他的xp_part_process将前往毛病。
利用Tls仿佛能够办理这成绩,比方再增加一个tls_index变量,挪用TlsSetValue保留用户公家数据,TlsGetValue检索公家数据,当xp_part_init时,假设该公家数据为0,实行一般的初始化历程,(即下面的xp_part_init)实行乐成后存储公家数据为1,假设是1,间接前往,xp_part_finalize时,假设公家数据为1,则实行一般的xp_part_finalize,然后设公家数据为0,假设是0,间接前往。
仿佛设法仍是不错的,如许断绝了多个用户,平安性仿佛进步了很多,但是现实是不成行的。由于Tls保留的并非公家数据,而是线程当地变量,我们不克不及包管一个用户的屡次操纵都是用统一个线程实行的,这个由SQLServer本人把持,现实上我在查询剖析器里屡次实行的了局显现,SQLServer外部仿佛利用了一个线程池。既然云云,那这类设法也只能作罢。
支持AIX、FreeBSD、HP-UX、Linux、MacOS、NovellNetware、OpenBSD、OS/2Wrap、Solaris、Windows等多种操作系统 大家注意一点。如下面的例子: 这一点很好的加强了profiler的功能。但是提到profiler提醒大家注意一点。windows2003要安装sp1补丁才能启动profiler。否则点击没有反应。 语句级快照和事务级快照终于为SQLServer的并发性能带来了突破。个人感觉语句级快照大家应该应用。事务级快照,如果是高并发系统还要慎用。如果一个用户总是被提示修改不成功要求重试时,会杀人的! 对一张百万级别的表建游标,同时又没有什么过滤条件,取得游标效率是如果直接SQL查询百万条数据;如果再对每条记录做处理,耗时将更长。 Mirror可以算是SQLServer的Dataguard了。但是能不能被大伙用起来就不知道了。 但是随着数据量的增大,这种成本差距会逐渐减小,趋于相等。(500万数量级只相差10%左右) 相信各位对数据库和怎么样学习数据库都有一些经验和看法,也会有人走了一些弯路总结出自己的经验来,希望大家能把各自的看法和经验拿出来分享,给别人一份帮助,给自己一份快乐 代替了原来VB式的错误判断。比Oracle高级不少。
页:
[1]