当前位置: 首页>数据库>正文

postgresql存储过程关于并发性的问题 存储过程并发执行

MVC用户访问多线程,一般的lock是无法造成单例的。

存储过程既是一种解决方案,先来看看存储过程优缺点:

        A、 存储过程允许标准组件式编程

        存储过程创建后可以在程序中被多次调用执行,而不必重新编写该存储过程的SQL语句。而且数据库专业人员可以随时对存储过程进行修改,但对应用程序源代码却毫无影响,从而极大的提高了程序的可移植性。

        B、 存储过程能够实现较快的执行速度

        如果某一操作包含大量的T-SQL语句代码,分别被多次执行,那么存储过程要比批处理的执行速度快得多。因为存储过程是预编译的,在首次运行一个存储过程时,查询优化器对其进行分析、优化,并给出最终被存在系统表中的存储计划。而批处理的T-SQL语句每次运行都需要预编译和优化,所以速度就要慢一些。

        C、 存储过程减轻网络流量

        对于同一个针对数据库对象的操作,如果这一操作所涉及到的T-SQL语句被组织成一存储过程,那么当在客户机上调用该存储过程时,网络中传递的只是该调用语句,否则将会是多条SQL语句。从而减轻了网络流量,降低了网络负载。

        D、 存储过程可被作为一种安全机制来充分利用

        系统管理员可以对执行的某一个存储过程进行权限限制,从而能够实现对某些数据访问的限制,避免非授权用户对数据的访问,保证数据的安全。

 

再来说说存储过程的事务原理

一、sql事务

1.什么是事务:事务是一个不可分割的工作逻辑单元,在数据库系统上执行并发操作时事务是做为最小的控制单元来使用的。他包含的所有数据库操作命令作为一个整体一起向系提交或撤消,这一组数据库操作命令要么都执行,要么都不执行。

2.事务的语句
开始事物:BEGIN TRANSACTION
提交事物:COMMIT TRANSACTION
回滚事务:ROLLBACK TRANSACTION

3.事务的4个特性
  ①原子性(Atomicity):事务中的所有元素作为一个整体提交或回滚,是不可折分的,事务是一个完整的操作。
  ②一致性(Consistemcy):事物完成时,数据必须是一致的,也就是说,和事物开始之前,数据存储中的数据处于一致状态。保证数据的无损。
  ③隔离性(Isolation):对数据进行修改的多个事务是彼此隔离的。这表明事务必须是独立的,不应该以任何方式来影响其他事务。
  ④持久性(Durability):事务完成之后,它对于系统的影响是永久的,该修改即使出现系统故障也将一直保留,真实的修改了数据库

4.事务的分类.
按事务的启动与执行方式,可以将事务分为3类:
  ①显示事务 :也称之为用户定义或用户指定的事务,即可以显式地定义启动和结束的事务。分布式事务属于显示事务
  ②自动提交事务:默认事务管理模式。如果一个语句成功地完成,则提交该语句;如果遇到错误,则回滚该语句。
  ③隐性事务:当连接以此模式进行操作时,sql将在提交或回滚当前事务后自动启动新事务。无须描述事务的开始,只需提交或回滚每个事务。它生成连续的事务链。

 

已事务中的全局变量为基准数,判断@@trancount;

说了这么多,来看看代码

ALTER PROCEDURE [dbo].[GetLottery]AS


SET NOCOUNT ON;
    declare @trancount int --commit,rollback只控制本存储过程   
        set @trancount = @@trancount;  
    if (@trancount=0)
        begin tran GetLottery
    else
        save tran GetLottery

***************************************************

    if @@Error <> 0
        begin
            Rollback tran GetLottery
            return @id
        end
    else
        begin    
            commit tran GetLottery
            return @id
        end

 

全局@@trancount计数:

执行或者回归都会使计数归零。

最后自行做了测试引用缩写的存储过程发现大并发下并不能完全控制抢占资源问题。

当存储过程执行都在千分之一秒相同的情况下。会造成资源抢占冲突并引起“超发,多发问题”

原理也其实相当简单,数据库支持的时间格式也是千分之一秒。

 

一下贴出线程测试代码。

public class Threadlist
    {
        /// <summary>
        /// 状态
        /// </summary>
        public static StaticType Static { get; private set; }
        /// <summary>
        /// 线程池
        /// </summary>
        public static List<Thread> Threads { get; set; }
        /// <summary>
        /// 守护线程
        /// </summary>
        public static Thread KeeperThread { get; set; }
        /// <summary>
        /// 结果
        /// </summary>
        public static List<String> Result { get; set; }

        public static Int32 CustomerId { get; set; }
        static Threadlist()
        {
            Static = StaticType.StandBy;
            Threads = new List<Thread>();
            Result = new List<string>();
        }
        public static void Start()
        {
            switch (Static)
            {
                case StaticType.Done://已经完成,相当于重新开始 
                case StaticType.StandBy://就绪状态
                    Result = new List<string>();
                    Threads = new List<Thread>();
                    Static = StaticType.Running;
                    for (int i = 0; i < 300; i++)
                    {
                        CustomerId = i + 200;

                        Threads.Add(new Thread(delegate() { SqlTest(CustomerId, 14); }));
                        Threads.Add(new Thread(delegate() { SqlTest(CustomerId, 15); }));
                        Threads[i].Start();
                    }
                    KeeperThread = new Thread(new ThreadStart(Keeper));
                    KeeperThread.Start();
                    break;
                default:
                    break;
            }

        }

        private static void Keeper()
        {
            while (Static == StaticType.Running)
            {
                Thread.Sleep(500);
                if (!Threads.Any(t => t.IsAlive))//作业全部完成
                    Static = StaticType.Done;
            }
        }

        public enum StaticType
        {
            StandBy,
            Running,
            Done
        }
        public static void SqlTest(Int32 CustomerId, Int32 CommodityId)
        {
            var db = new WebDatabase();
            var Code = Guid.NewGuid().ToString();var lottery = db.EventLotteryHistories.FirstOrDefault(x => x.BonusId == null);
            var Type = 0;
            var PayCount = 1;
            var sqlstatue = 0;
            var orderId = 0;
            while (sqlstatue==0)
            {
                sqlstatue = 1;
                try
                {

                    var lotteryId = new SqlParameter { ParameterName = "LotteryHistoryId", Value = lottery.Id, Direction = ParameterDirection.Input };
                    var result = new SqlParameter { ParameterName = "id", Value = 0, Direction = ParameterDirection.Output };
                    db.Database.ExecuteSqlCommand(System.Data.Entity.TransactionalBehavior.DoNotEnsureTransaction,
                        "exec @id = GetLottery @LotteryHistoryId", lotteryId, result);
                    //////////////////////////增加错误捕获代码///////////////////////////////
                    var statue = result.Value;
                    Result.Add(CustomerId.ToString() + "... " + statue.ToString());
                }
                catch (Exception)
                {
                    sqlstatue = 0;
                }
            }
        }
    }
}

之前在使用ef调用存储过程中遇到了卡了一天的一个坑。

ef直接调用存储过程中会在外壳添加一个事务循环,以此来判断事务是否正确运行。

但是如果在调用事务中原本就有事务导致冲突,因此调用时请选择事务状态:“不执行事务模式”---“System.Data.Entity.TransactionalBehavior.DoNotEnsureTransaction”

 




https://www.xamrdz.com/database/6ff1928750.html

相关文章: