关于sql server的纪录修改冲突解决和oracle的for update 的并发锁定测试

来源:互联网 发布:小米4 9006端口救砖 编辑:程序博客网 时间:2024/05/16 09:57

在普通的多用户数据库应用中,经常碰到数据库的记录同时被多个用户阅读且当作正式数据来修改修改。比如:

比如有记录r1
字段分别为 f1,f2 ,对应的值分别为v1,v2
此时有用户u1,u2。
过程:
1,u1查询到此记录,马上u2也查到此记录
2,u1看准了v1,要把它改为v3,开始执行
3,u2看准了v1,要把它改为v4,开始执行
4,结果可想而知,u1的结果最终变成了v4,而不是其期望的v3
5,这个就导致了很多问题,甚至操作者之间的无端误会,
6,为了解决此问题,本人也想了一些办法

针对sql server,由于现在我处理业务逻辑全部采用业务实体对象作为处理单元媒介,而一个实体对象是不是时时刻刻都保持和数据库记录同步,这个问题都非常难搞。本人利用了sql server 的一个timestamp字段作为实体的特殊属性来记录当前记录是否已经被别的用户更改过了,如果更改过了,本人就来逐个比较要存储(一般为update)实体被修改的所有属性的原始值是否和数据库记录相同。如果相同,说明用户要修改的字段没有被别的用户修改,这样就可以继续执行本实体对象的生成的sql语句;如果不同,说明有属性修改冲突,就要抛出相关异常,提示用户重新编辑实体对象。

针对oracle数据库,本人采用了锁定记录来处理,测试代码如下:

测试数据表结构
-- Create table
create table TEST
(
AUTOID INTEGER not null,
VAL VARCHAR2(1000),
primary key(autoid)
)

insert into test values(1,'abc')


using System;
using System.Collections.Generic;
using System.Text;

namespace OracleLockTest
{
    class Program
    {
       
        private const string connStr = "Data Source=ORCL;User Id=riskms;Password=riskms;";

        static System.Data.OracleClient.OracleConnection oc = null;

        static void Main(string[] args)
        {
            oc = GetOracleConn();

            string val;
            while ((val = System.Console.ReadLine()) != "exit")
            {
               
                if (val == "1")
                {
                    Th1();
                }
                else if (val == "2")
                {
                    Th2();
                }
                else if (val == "3")
                {
                    Th3(oc);
                }
                else if (val == "4")
                {
                    Th4(oc);
                }
                else if (val == "5")
                {
                    Th5(oc);
                }
            }
        }

        /// <summary>
        /// 连接池,每次从这个函数获得的connection对象都是随机的
        /// </summary>
        /// <returns></returns>
        static System.Data.OracleClient.OracleConnection GetOracleConn()
        {
            return new System.Data.OracleClient.OracleConnection(Program.connStr);
        }

        static void Th1()
        {
            System.Data.OracleClient.OracleConnection oc = GetOracleConn();
           
            System.Data.OracleClient.OracleCommand cmd = oc.CreateCommand();

            cmd.CommandText = "select * from test t where autoid = 1 for update  nowait";

            if (cmd.Connection.State == System.Data.ConnectionState.Closed)
                cmd.Connection.Open();

            try
            {
                cmd.ExecuteNonQuery();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            cmd.CommandText = "update test set val = '" + System.DateTime.Now.ToString() + "'  where autoid = 1 ";

            cmd.ExecuteNonQuery();
        }

        static void Th2()
        {
            System.Data.OracleClient.OracleConnection oc = GetOracleConn();

            System.Data.OracleClient.OracleCommand cmd = oc.CreateCommand();

            cmd.CommandText = "select * from test t where autoid = 1   for update nowait";

            if (cmd.Connection.State == System.Data.ConnectionState.Closed)
                cmd.Connection.Open();

            try
            {
                cmd.ExecuteNonQuery();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            cmd.CommandText = "update test set val = '" + System.DateTime.Now.ToString() + "'  where autoid = 1 ";

            cmd.ExecuteNonQuery();
        }

        static void Th3(System.Data.OracleClient.OracleConnection _oc)
        {
            System.Data.OracleClient.OracleConnection oc = _oc;

            System.Data.OracleClient.OracleCommand cmd = oc.CreateCommand();

            cmd.CommandText = "select * from test t where autoid = 1   for update nowait";

            if (cmd.Connection.State == System.Data.ConnectionState.Closed)
                cmd.Connection.Open();

            try
            {
                cmd.ExecuteNonQuery();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            cmd.CommandText = "update test set val = '" + System.DateTime.Now.ToString() + "'  where autoid = 1 ";

            cmd.ExecuteNonQuery();
        }

        static void Th4(System.Data.OracleClient.OracleConnection _oc)
        {
            System.Data.OracleClient.OracleConnection oc = _oc;

            System.Data.OracleClient.OracleCommand cmd = oc.CreateCommand();

            cmd.CommandText = "select * from test t where autoid = 1   for update nowait";

            if (cmd.Connection.State == System.Data.ConnectionState.Closed)
                cmd.Connection.Open();

            try
            {
                cmd.ExecuteNonQuery();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
           
            cmd.CommandText = "update test set val = '" + System.DateTime.Now.ToString() + "'  where autoid = 1 ";
           
            cmd.ExecuteNonQuery();
        }

        /// <summary>
        /// 根据当前连接执行一条操作语句,注意它默认了提交动作,如果发生异常,则默认了回滚动作,这样锁定的资源便可以释放
        /// </summary>
        /// <param name="_oc"></param>
        static void Th5(System.Data.OracleClient.OracleConnection _oc)
        {
            System.Data.OracleClient.OracleConnection oc = _oc;
           
            System.Data.OracleClient.OracleCommand cmd = oc.CreateCommand();

            cmd.CommandText = "update test set val = '" + System.DateTime.Now.ToString() + "'  where autoid = 1 ";

            if (cmd.Connection.State == System.Data.ConnectionState.Closed)
                cmd.Connection.Open();

            try
            {
                cmd.ExecuteNonQuery();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
       
        }

    }
}


结果是:
1,同一个session(也就是数据库连接)中执行了for update 后,相关记录被锁定,如果此连接断开,则相关锁定自动释放锁定
2,锁定记录被另外的用户要求锁定会抛出异常,有两个情况,如果另外用户带有nowait,则马上抛出异常,如果不带nowait,则处于等待记录释放状态,直到该记录被释放
3,注意同一会话锁定后再执行操作语句后就会被默认执行了“commit”,也就是释放了该记录
4,连接池对象的不定性要注意

原创粉丝点击