首页 新闻 会员 周边 捐助

asp.net 并发问题,求简单解决示例!

0
悬赏园豆:10 [已解决问题] 解决于 2014-05-31 08:07

一个预约系统,用最基本的ado.net写的,同一时间段只能由一个用户预约,现在的做法是用户点预约按钮后先ado.net执行select查询数据库是否存在该时间段,存在则提示用户不能预约,否则ado.net执行insert语句插入数据库

现在某一时间内网站并发高了,多个用户预约同一时间,搞得我发现同一时间被多个用户预约上了,请问怎么解决这个问题?让一个时间段只能由一个用户预约?

 

把select语句和insert语句放到存储过程中再用事务可以解决吗?

或者直接在c#代码中直接在ado.net执行语句前加lock行吗?求指教 。。。

牛腩的主页 牛腩 | 初学一级 | 园豆:-6
提问于:2014-05-26 23:59
< >
分享
最佳答案
1

2个方法,数据库锁

lock锁

数据库其实比较简单,因为可以用一个update语句实现,这个算是一个技巧了.

你update的时候where里面要加上你select出来的判断条件.比如库存>0

update 库存-=1 where库存大于0

这样然后根据返回值判断这个操作是否成功.这个还有很大的优化空间,

但总的来说如果是同一张表.这样操作是比较好的

收获园豆:5
吴瑞祥 | 高人七级 |园豆:29449 | 2014-05-27 10:10

我不是update,数据库里事先 是没有数据的。。我要先Selec查询一次后没有数据才向数据库中插入数据的,用户A在select后还没有insert之前,用户B已经insert进数据库了,然后用户A也跟着select了,所以导致有二条重复数据。

用把select语句和insert语句写在一起吗?然后用ado.net执行这个语句:

if((select count(1) from tab where time='2014-05-27')=0){

  insert into tab values('2014-05-27')

}

牛腩 | 园豆:-6 (初学一级) | 2014-05-27 10:37

@牛腩: 这个可以用merge语句,怎么用就得百度了

吴瑞祥 | 园豆:29449 (高人七级) | 2014-05-27 11:20

@吴瑞祥: 最后我把select 语句和insert语句放到一个存储过程中了,不过没用事务 。现在看起来客户也没反馈什么问题。。。

USE [cajx]
GO
/****** Object:  StoredProcedure [dbo].[proc_xyyuyue_cajx]    Script Date: 2014-05-31 星期六 08:03:37 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO






-- =============================================
-- Author:        牛腩
-- Create date: 2013年11月08日11时4分
-- Description:    学员预约
-- =============================================
ALTER PROCEDURE [dbo].[proc_xyyuyue_cajx] 
    -- Add the parameters for the stored procedure here
    @userid int, --学员ID
    @jlid int, --教练ID
    @date nvarchar(16), --预约日期
    @time nvarchar(16), --预约时间
    @xs int,            -- 学时(通过上面的预约时间计算)
    @front_time nvarchar(16), --前一个时间段,只有在flag=0预约加班时间时这里才会用到
    @yycount int,   --一周可以预约的次数, 星期1-4只能约3次,其他能约4次,正常预约时才判断
    @zhou_start nvarchar(16), --今天时间,计算一周
    @zhou_end nvarchar(16),    --以今天为准 ,一周结束后的时间
    @ip nvarchar(32), --学员预约时的IP地址
    @ksid    int,    --考试ID
    @flag int=2,    -- 0预约的是加班时间,1考试预约,2正常预约
    @mes nvarchar(64) output, --输出信息
    @kemu nvarchar(64), --练车科目
    @lcdd nvarchar(64), --练车地点
    @jsdd nvarchar(64) --接送地点 
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    -- 验证学员
    if((select count(1) from [user] where id=@userid)=0)
     begin
        set @mes='没有该学员!'
        return
     end

    -- 验证教练
    if((select count(1) from jiaolian where id=@jlid)=0)
     begin
        set @mes='没有该教练!'
        return
     end

    -- 教练已停用
    if((select count(1) from jiaolian where ty=1 and id=@jlid)>0)
     begin
        set @mes='教练已停用,请联系管理员!'
        return
     end

    -- 取教练名,用于插入预约表 
    declare @jlname nvarchar(50)
    select @jlname = realname from jiaolian where id=@jlid

    -- 教练在该时间段已有预约
    if((select count(1) from yuyue where replace(CONVERT(char(10),yydate,111),'/','-')=@date and jlid=@jlid and yytime=@time and status<>2)>0)
     begin
        set @mes='教练在该时间段已有预约!'
        return
     end

    -- 学员学时不足
    declare @userxs int --学员剩余学时
    declare @username nvarchar(64) --学员姓名,用于最后的插入日志表
    select @userxs=xueshi,@username=username from [user] where id=@userid
    if(@userxs<>-1 and (@userxs-@xs)<0)
     begin
        set @mes='学员的剩余学时不足,不能预约!'
        return
     end

    -- 判断flag
    if(@flag=0)
     begin
        --预约的是加班时间,判断当前time的前一小时是否预约,没约则不能约
        if(@time<>'18-19点' and (select count(1) from yuyue where status in (0,1) and userid=@userid and yytime=@front_time and replace(CONVERT(char(10),yydate,111),'/','-')=@date)=0)
        begin
         set @mes='请先预约前一时间段!'
         return
        end
     end
    else if(@flag<>1)
     begin
        --即不是考试预约也不是加班时间预约,进行预约时间限制判断
        --判断一天只能约一次
        if((select count(1) from yuyue where status<>2 and replace(CONVERT(char(10),yydate,111),'/','-')=@date and userid=@userid)>=1)
         begin
            set @mes='每个学员一天只能预约一次!'
            return
         end

         --星期1-4只能约3次,其他能约4次,联杰不用此计算
         --if((select count(1) from yuyue where status<>2 and userid=@userid and replace(CONVERT(char(10),yydate,111),'/','-')>=@zhou_start and replace(CONVERT(char(10),yydate,111),'/','-')<=@zhou_end)>=@yycount)
         -- begin
         --  set @mes='一周最多只能预约'+(ltrim(rtrim(str(@yycount))))+'次'
         --  return
         -- end
     end

     --上面验证全通过,插入预约表
     insert into yuyue(userid,jlid,yydate,yytime,ksid,[status],username,jlname,kemu,lcdd,jsdd) values(@userid,@jlid,@date,@time,@ksid,0,@username,@jlname,@kemu,@lcdd,@jsdd)

     --插入日志表
     insert into log(remark,ip,username,userid) values('会员预约('+@date+' '+@time+')',@ip,@username,@userid)


    set @mes='预约申请提交成功.'
END
牛腩 | 园豆:-6 (初学一级) | 2014-05-31 08:05
其他回答(3)
1

如果从数据库这边考虑,把select语句和insert语句放到存储过程中再用事务,应该解决不了的。我感觉用触发器应该能解决。

c#这边考虑,可以将插入的这段代码做成一个静态方法,静态方法中使用lock,应该就能解决了

收获园豆:1
于为源 | 园豆:956 (小虾三级) | 2014-05-27 08:50
1

被预约的对象应该有一个状态字段。每次再预约时,先update这个状态字段=被预约 where 状态字段=未预约。当返回为1时,再insert记录。返回为0时,提示该对象已被其他人预约。

 

收获园豆:1
滴答的雨 | 园豆:3660 (老鸟四级) | 2014-05-27 09:21
1

使用事务,先锁起来,在运行事务中的代码

收获园豆:3
James.Ying | 园豆:1472 (小虾三级) | 2014-05-27 12:19

我把语句都放存储过程中了,目前看来客户没反馈,应该没问题了。

USE [cajx]
GO
/****** Object:  StoredProcedure [dbo].[proc_xyyuyue_cajx]    Script Date: 2014-05-31 星期六 08:03:37 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO






-- =============================================
-- Author:        牛腩
-- Create date: 2013年11月08日11时4分
-- Description:    学员预约
-- =============================================
ALTER PROCEDURE [dbo].[proc_xyyuyue_cajx] 
    -- Add the parameters for the stored procedure here
    @userid int, --学员ID
    @jlid int, --教练ID
    @date nvarchar(16), --预约日期
    @time nvarchar(16), --预约时间
    @xs int,            -- 学时(通过上面的预约时间计算)
    @front_time nvarchar(16), --前一个时间段,只有在flag=0预约加班时间时这里才会用到
    @yycount int,   --一周可以预约的次数, 星期1-4只能约3次,其他能约4次,正常预约时才判断
    @zhou_start nvarchar(16), --今天时间,计算一周
    @zhou_end nvarchar(16),    --以今天为准 ,一周结束后的时间
    @ip nvarchar(32), --学员预约时的IP地址
    @ksid    int,    --考试ID
    @flag int=2,    -- 0预约的是加班时间,1考试预约,2正常预约
    @mes nvarchar(64) output, --输出信息
    @kemu nvarchar(64), --练车科目
    @lcdd nvarchar(64), --练车地点
    @jsdd nvarchar(64) --接送地点 
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    -- 验证学员
    if((select count(1) from [user] where id=@userid)=0)
     begin
        set @mes='没有该学员!'
        return
     end

    -- 验证教练
    if((select count(1) from jiaolian where id=@jlid)=0)
     begin
        set @mes='没有该教练!'
        return
     end

    -- 教练已停用
    if((select count(1) from jiaolian where ty=1 and id=@jlid)>0)
     begin
        set @mes='教练已停用,请联系管理员!'
        return
     end

    -- 取教练名,用于插入预约表 
    declare @jlname nvarchar(50)
    select @jlname = realname from jiaolian where id=@jlid

    -- 教练在该时间段已有预约
    if((select count(1) from yuyue where replace(CONVERT(char(10),yydate,111),'/','-')=@date and jlid=@jlid and yytime=@time and status<>2)>0)
     begin
        set @mes='教练在该时间段已有预约!'
        return
     end

    -- 学员学时不足
    declare @userxs int --学员剩余学时
    declare @username nvarchar(64) --学员姓名,用于最后的插入日志表
    select @userxs=xueshi,@username=username from [user] where id=@userid
    if(@userxs<>-1 and (@userxs-@xs)<0)
     begin
        set @mes='学员的剩余学时不足,不能预约!'
        return
     end

    -- 判断flag
    if(@flag=0)
     begin
        --预约的是加班时间,判断当前time的前一小时是否预约,没约则不能约
        if(@time<>'18-19点' and (select count(1) from yuyue where status in (0,1) and userid=@userid and yytime=@front_time and replace(CONVERT(char(10),yydate,111),'/','-')=@date)=0)
        begin
         set @mes='请先预约前一时间段!'
         return
        end
     end
    else if(@flag<>1)
     begin
        --即不是考试预约也不是加班时间预约,进行预约时间限制判断
        --判断一天只能约一次
        if((select count(1) from yuyue where status<>2 and replace(CONVERT(char(10),yydate,111),'/','-')=@date and userid=@userid)>=1)
         begin
            set @mes='每个学员一天只能预约一次!'
            return
         end

         --星期1-4只能约3次,其他能约4次,联杰不用此计算
         --if((select count(1) from yuyue where status<>2 and userid=@userid and replace(CONVERT(char(10),yydate,111),'/','-')>=@zhou_start and replace(CONVERT(char(10),yydate,111),'/','-')<=@zhou_end)>=@yycount)
         -- begin
         --  set @mes='一周最多只能预约'+(ltrim(rtrim(str(@yycount))))+'次'
         --  return
         -- end
     end

     --上面验证全通过,插入预约表
     insert into yuyue(userid,jlid,yydate,yytime,ksid,[status],username,jlname,kemu,lcdd,jsdd) values(@userid,@jlid,@date,@time,@ksid,0,@username,@jlname,@kemu,@lcdd,@jsdd)

     --插入日志表
     insert into log(remark,ip,username,userid) values('会员预约('+@date+' '+@time+')',@ip,@username,@userid)


    set @mes='预约申请提交成功.'
END

 

支持(0) 反对(0) 牛腩 | 园豆:-6 (初学一级) | 2014-05-31 08:06
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册