Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 22284|回复: 0

mvc 高并发解决方案之一---存储过程

[复制链接]
  • TA的每日心情
    奋斗
    2024-4-6 11:05
  • 签到天数: 748 天

    [LV.9]以坛为家II

    2034

    主题

    2092

    帖子

    70万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    705612
    发表于 2021-4-10 15:10:53 | 显示全部楼层 |阅读模式

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

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

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

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

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

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

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

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

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

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

    引用地址:http://www.cnblogs.com/hoojo/archive/2011/07/19/2110862.html

     

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

    一、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”

     

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-17 08:46 , Processed in 0.182847 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表