《事务-锁05204.doc》由会员分享,可在线阅读,更多相关《事务-锁05204.doc(15页珍藏版)》请在金锄头文库上搜索。
1、事务-锁.txt人和人的心最近又最远,真诚是中间的通道。试金可以用火,试女人可以用金,试男人可以用女人-往往都经不起那么一试。大数据库之二:事务-锁kingbase/http:/ 最简单的事务定义BEGIN TRAN一段SQL语言程序COMMIT(2)含事务名称的事务定义,事务处理支持嵌套:BEGIN TRAN T1.BEGIN TRAN T2.COMMIT T2.COMMIT T1嵌套的事务名称,如T1等不要超过32字节。(3)日志中有标记的事务BEGIN TRAN T1 WITH MARK .COMMIT T1(4)多服务器上同时开始事务BEGIN DISTRIBUTED TRANSACT
2、ION.COMMIT例如,在一个服务器ServerA上注册有:ServerB、ServerC、ServerD,如果在 ServerA 上发出 BEGIN DISTRIBUTED TRANSACTION,该连接调用ServerB上的存储过程和ServerC上的另一个存储过程,并且ServerC上的存储过程对ServerD执行一个分布式查询,那么四个 SQL 服务器都进入分布式事务中了。ServerA 是该事务的创建者和控制服务器。事务的典型处理框架结构是:BEGIN TRAN.IF ERR0 OR 其他错误BEGINROLLBACKENDELSEBEGIN.COMMITEND也就是是说一旦发生错
3、误,ROLLBACK将撤消这个事务开始后产生的一切影响。在定义这个事务后,其中的SQL语言的执行,将按ACID的标准完成。逻辑工作单元必须具备这四个属性才能称为事务。该属性能够使事务免受其它并发事务所执行的更新的影响。每个事务的隔离级别实际上都是可以自定义的。自定义隔离级别用:SET TRANSACTION ISOLATION LEVEL 语句,但目前我们先不用自定义事务处理的隔离级别,我们从简单问题开始处理。二,简单事务处理范例例1 TEST数据库有T1(ID NUMERIC,AX VARCHAR(20)表,其中ID是标识字段,这个字段上可以自动产生增加的序列,ID是PK类列,不会有重复。I
4、D字段不允许插入,这个值是自动产生的。1 给这个数据库的表插入记录,每次插入记录后,数据库会自动生成一个ID,然后根据这个ID取出AX的内容,判断是否是你前面插入的值,过程如:DECLARE N NUMERICINSERT INTO T1(AX) VALUES (AAAA)SELECT N=MAX(ID) FROM T1SELECT COUNT(*) FROM T1 WHERE AX=AAAA AND ID=N上面的范例中,SELECT N=MAX(ID) FROM T1是获得数据库中最后一个插入记录的标识字段值,请一定注意这个语句的意义。每次运行,请注意调整插入的内容,例如下次是AAAB,不
5、要相同,等等。在单用户情况下,这个结果必然是1,这验证了单用户下ID、AX的必然对应结果。最后,打印IDENTITY的值,然后在T1表中看看是否和ID字段的值一样。2 无事务、大批量插入记录测试此时由于插入记录非常多,所以使用了C#做循环插入,我们用这样的程序构造了插入字段,全部程序见TRAN程序(在button1_click)。int A=new int4;/代表插入的AX最长将是4位string SQL,st;string S=ABCDEFGHIJKLLMNOPQRSTUVWXYZ0123456789;/每一位循环的字符A0=0;A1=0;A2=0;A3=0; while(A2=0) /这
6、个地方是说循环到第3位就可以了st=;for(int i=0;i4;i+)st=st+S.Substring(Ai,1); A3=A3+1;for(int i=0;i36)Ai=0;Ai-1=Ai-1+1;SQL=Insert into T1(AX) Values (+st+);MyDB.ExecuteSQL(SQL);int MyID=MyDB.ExecuteSQLScalar(SELECT MAX(ID) FROM T1); SQL=SELECT COUNT(*) FROM T1 WHERE ID=+MyID.ToString()+ AND AX=+st+;int n=MyDB.Execu
7、teSQLScalar(SQL);if(n!=0)MessageBox.Show(插入数据异常,插入操作终止,警告); return;dataGrid1.BeginInvoke(new MyDelegate(updateGrid); /关注这个C#技巧 当你的程序连接到自己的服务器上,这个程序不会有错误提示,但是,你如果和其他人都连接一个服务器,不久就会出现错误。出现错误的快慢和数据库服务器的速度有关系,服务器速度越好、出现问题的可能性越低。出现错误的原因:你获得的IDENTITY值是别人插入记录的结果,此时按这个值、查询得到的结果不是你插入的记录。在上面这个程序中,其中:dataGrid1.
8、BeginInvoke(new MyDelegate(updateGrid);是发出一个dataGrid1更新消息,等计算机CPU闲的时候,更行这个控件中的显示,如果在整个while循环中直接更新,这个循环会占用很多CPU时间,dataGrid1根本不可能有机会更新。3 有事务、大批量插入数据试验上面的情况立刻预示着:当我们在插入一个记录的时候、直到取出IDENTITY值以前,不希望别人能插入记录,这个情况就是我们要进行事务编程的理由,于是我们在查询分析器中定义:CREATE PROCEDURE INST(AX VARCHAR(20)ASDECLARE MYID INTBEGIN TRANIN
9、SERT INTO T1(AX) VALUES (AX)SELECT MYID=MAX(ID) FROM T1COMMITRETURN MYIDGO这样的有返回值的存储过程,一定要注意:SQL Server系统中,自定义函数是不能使用事务处理的,而存储过程则允许返回值,这是MS的规定。测试这个存储过程是简单的,可以用:DECLARE N NUMERICEXEC N=INST AAAXYSELECT NSELECT COUNT(*) FROM T1 WHERE ID=N AND AX=AAAXY在上面的语句中,SELECT N的含义同PRINT N,这段程序是在我们C#中也要用到的。于是修改我们
10、的C#程序(在button3_click):int A=new int4;string SQL,st;string S=ABCDEFGHIJKLLMNOPQRSTUVWXYZ0123456789;A0=0;A1=0;A2=0;A3=0;while(A2=0)st=;for(int i=0;i4;i+)st=st+S.Substring(Ai,1); A3=A3+1;for(int i=0;i36)Ai=0;Ai-1=Ai-1+1;SQL=DECLARE N INT;EXEC N=INST +st+;SELECT N;/注意这个用法int MyID=MyDB.ExecuteSQLScalar(S
11、QL);SQL=SELECT COUNT(*) FROM T1 WHERE ID=+MyID.ToString()+ AND AX=+st+;int n=MyDB.ExecuteSQLScalar(SQL); if(n!=1)MessageBox.Show(插入数据异常,插入操作终止,警告); return;dataGrid1.BeginInvoke(new MyDelegate(updateGrid); 注意事项:1 对ExecuteScalar()的引用,这个方法,对存储过程或函数的返回值都可以得到。传统上,在C#下,使用SqlCommand对象的Parameters属性,可以设置或得到一
12、个存储过程、函数的返回值,但是,由于各个存储过程、函数的入口参数都不一致,所以这个方法很麻烦,根本没有可能构造出一个有普遍意义的类,MyDBase中这个方法很简单,但需要你熟悉SQL语言;2 无论VB还是C#,都在语言中提供了执行事务的手段。例如在C#上有SqlTransaction对象,详细用法见MyDBase类中的方法:ExecuteSQLWithTrans(string Sqls)但是,考虑到事务处理的本质是锁定其他人的操作、来完成自己的工作,如果你的程序是在网络远程执行,那么有可能会把这个事务的处理拖的时间很久,完全卡主其他人的操作,多用户情况下会非常崩溃,整个数据库的效率就会大大下降。所以事务处理编程,最好是在数据库服务器上、使用SQL语句来完成,而不是在其他地方。如果在客户端启动事务处理或表加锁,网络情况不好的场合下就等死吧、多么高档的服务器都救不你。执行上述程序,你不再会发现有错误提示,只要你服务器能力很强,连接多少用户都是正确的。例2 在数据库TEST中,你会找到一些你以前接触过的表,如LINKTABLE表、