<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      歡迎大家下載試用折桂單點登錄系統, https://www.zheguisoft.com

      編程經驗點滴----使用接口表保存可能并發的業務,然后順序處理

        這篇文章的主要內容,來自與其他人的討論。

        軟件系統的開發或設計時,容易遇到有并發的情況。有時候需要刻意去避免,防止數據錯誤。比如超市賣商品,可能兩個柜臺同時賣出一款礦泉水,如果軟件系統后臺需要跟蹤每個商品的庫存,此時就需要特別考慮。如果兩個柜臺,同時采取"讀當前庫存,減一,得到最新庫存,保存"的設計,則可能會導致數據錯誤。比如,兩個柜臺,讀當前庫存,都得到 100, 減一,都得到99,作為最新數據保存,保存99。最后,盡管同時賣出了兩瓶礦泉水,最后系統的庫存確是99。無疑是有問題的。

        一個簡單的解決辦法,就是再設計一個接口表。對于有可能并發的操作,統一插入一條"待處理的操作指令"到此接口表中,然后單獨起一個線程,逐個處理此接口表中待處理數據。

        大致步驟如下:

      1. 并發處理,統一插入一條待處理的操作指令到此接口表中,只 insert:
      insert into ti_xxx ....; --process_flag = 0

      2. 單獨起一個線程,逐個讀 : ti_xxx 中未處理的數據.
      2.1
      select top 1 from ti_xxx where process_flag = 0 order by increase_key,created_time;

      2.2. insert/update 到 tt_xxx :
      if exists(select 1 from tt_xxx where ....)
        update tt_xxx ....
      else
        insert into tt_xxx...

      2.3 更新 ti_xxx 數據為已處理:
      update ti_xxx set process_flag = 1 where increase_key = xxx;

      其中,ti_xxx 表使用自增長主鍵,或使用 uuid 做主鍵。

       

        如果只是單純的超市軟件系統,它的庫存計算,其實不用很實時。讓管理員人員,看當前時間的庫存,與看5分鐘之前的庫存,從純粹的管理層面,并沒有大的區別。實際上,絕大多數系統,數據的實時性要求,都沒有高到需要完全實時。另一方面,此類系統對數據的最終準確性,要求卻是非常高的。比如,客戶不太在意,9:05 分賣出一款礦泉水,只能在 9:10看到庫存減少。但客戶在意的是,9:05 分時刻賣出一款礦泉水,至少在下班后(21:00),能看到結果。

        如果我們將以上所述"單獨起一個線程",做成每 0.5秒 運行一次的定時任務,則對于客戶來說,完全看不到影響。

       

      -------------------------------

      2017/6/3 補充,(2017/6/4發現,以下測試步驟中有不當的地方,請忽略).

      有人提到,可以用純 SQL 來處理并發,使用適當的 lock 。但這樣有時并不管用。比如按如下測試,則測試出問題:

      測試環境: Windows 8.1 64位 + SQL Server 2014 Express.
      測試步驟:
      step_1, 創建數據庫 test_db1。

      step_2, 運行 SQL 更改數據庫屬性:
      ALTER DATABASE test_db1 SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
      ALTER DATABASE test_db1 SET ALLOW_SNAPSHOT_ISOLATION ON;
      ALTER DATABASE test_db1 SET READ_COMMITTED_SNAPSHOT ON;
      ALTER DATABASE test_db1 SET MULTI_USER;

      step_3,創建表,
      CREATE TABLE [dbo].[Test](
      [Id] [bigint] NULL,
      [Name] [varchar](50) NULL,
      [Counter] [bigint] NULL
      ) ON [PRIMARY];

      step_4,創建存儲過程:
      SET ANSI_NULLS ON
      GO

      SET QUOTED_IDENTIFIER ON
      GO


      CREATE PROCEDURE [dbo].[sp_test]
      @Id [bigint],
      @Name [varchar](50)

      AS
      BEGIN
      BEGIN TRANSACTION
      --先嘗試更新記錄占坑
      UPDATE Test WITH(HOLDLOCK)
      SET [Counter] = [Counter] + 1
      WHERE Id = @Id;

      WAITFOR DELAY '00:02:00';


      --如果更新操作沒有影響行,證明記錄不存在,則插入
      IF @@ROWCOUNT<1
      BEGIN
      INSERT Test
      ( Id, Name, [Counter] )
      VALUES ( @Id, @Name, 1 );
      END
      COMMIT
      END


      GO

      中間加了暫停。

      step_5. 開兩個 SQL Server Management studio, 分別運行 sp_test, 參數分別為:
      step_5_1:
      id=1,
      name='A',

      step_5_2:
      id=1,
      name='B',

      step_6, 驗證最后數據:
      SELECT TOP 1000 * FROM [test_db1].[dbo].[Test];
      得到兩行數據:
      Id Name Counter
      1 A 2
      1 B 1

      結論:
      純SQL 代碼不能起到期望的結果。

      ---------------------------------------------

      2017/6/4 補充更正

      重新測試,結論是 UPDATE...WITH(HOLDLOCK)... 可以鎖住表的 update 操作,起到"并發時順序處理"的期望結果。但并不需要使用 SET [Counter] = [Counter] + 1 這樣的語句。

      測試環境: Windows 8.1 64位 + SQL Server 2014 Express.
      測試步驟:
      step_1, 創建數據庫 test_db1。

      step_2, 運行 SQL 更改數據庫屬性:
      ALTER DATABASE test_db1 SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
      ALTER DATABASE test_db1 SET ALLOW_SNAPSHOT_ISOLATION ON;
      ALTER DATABASE test_db1 SET READ_COMMITTED_SNAPSHOT ON;
      ALTER DATABASE test_db1 SET MULTI_USER;

      step_3,創建表,
      CREATE TABLE [dbo].[Test](
          [Id] [bigint] NULL,
          [Name] [varchar](50) NULL,
          [Counter0] [bigint] NULL,
          [created_time] [datetime] NULL,
          [updated_time] [datetime] NULL
      ) ON [PRIMARY];

      step_4,創建存儲過程:
      CREATE PROCEDURE [dbo].[sp_test]
          @Id  [bigint],
          @Name [varchar](50)

      AS
      BEGIN
          BEGIN TRANSACTION
          --先嘗試更新記錄占坑
          print 'a0:'+ convert(varchar(255), getdate(), 121) + ','

          UPDATE  Test WITH(HOLDLOCK)
          SET   --  [Counter] = [Counter] + 1,
              Name=@Name, updated_time = getdate()
          WHERE   Id = @Id;

          --需要在 WAITFOR DELAY 之前,將 @@ROWCOUNT 中的數值,暫時保存起來。因為 WAITFOR DELAY 之后,@@ROWCOUNT 中的數值會變。
          DECLARE @v_ROWCOUNT bigint
          set @v_ROWCOUNT = @@ROWCOUNT
          print 'a1:'+ convert(varchar(255), getdate(), 121)  + ',ROWCOUNT='+ cast( @v_ROWCOUNT as varchar(255))
          print 'a1.5:'+ convert(varchar(255), getdate(), 121)  + ',ROWCOUNT='+ cast( @@ROWCOUNT as varchar(255))

          WAITFOR DELAY '00:00:20';

          print 'a2:'+ convert(varchar(255), getdate(), 121) + ',ROWCOUNT='+ cast( @@ROWCOUNT as varchar(255))

          --如果更新操作沒有影響行,證明記錄不存在,則插入
          IF @v_ROWCOUNT < 1
          BEGIN
              INSERT  Test
                      ( Id, Name
                      --, [Counter]
                      ,created_time,updated_time )
              VALUES  ( @Id, @Name
                  --, 1
                  , getdate(), getdate() );
              print 'a3:'+ convert(varchar(255), getdate(), 121) + ',ROWCOUNT='+ cast( @@ROWCOUNT as varchar(255))
              
              WAITFOR DELAY '00:00:05';
          END
              print 'a4:'+ convert(varchar(255), getdate(), 121)
              WAITFOR DELAY '00:00:02';
              print 'a4.5:'+ convert(varchar(255), getdate(), 121)
          COMMIT
              print 'a5:'+ convert(varchar(255), getdate(), 121)
      END


      GO

      中間加了暫停。

      step_5. 開兩個 SQL Server Management studio, 分別運行 sp_test, 參數分別為:
      step_5_1:
      id=1,
      name='A',

      step_5_2:
      id=1,
      name='B',

      step_6, 驗證最后數據:
      SELECT TOP 1000 * FROM [test_db1].[dbo].[Test];
      得到一行數據:
      Id    Name    Counter0    created_time    updated_time
      1    B    NULL    2017-06-04 14:59:46.517    2017-06-04 14:59:53.520

      從調試運行執行存儲過程 SQL 的消息日志中,可以看到第二次存儲過程的 update 的執行,確實是在第一次執行的 commit 之后。


      結論:
      UPDATE...WITH(HOLDLOCK)... 可以鎖住表的 update 操作,起到"并發時順序處理"的期望結果。但并不需要使用 SET [Counter] = [Counter] + 1 這樣的語句。

       

      很抱歉之前的錯誤結論,可能誤導了一些朋友。

      實測結果,數據庫屬性中,增加 :

      ALTER DATABASE test_db1 SET ALLOW_SNAPSHOT_ISOLATION ON;
      ALTER DATABASE test_db1 SET READ_COMMITTED_SNAPSHOT ON;

      可以在 UPDATE...WITH(HOLDLOCK)... 的數據庫事務執行過程中,select 表 Test 數據。

      而不加 ALLOW_SNAPSHOT_ISOLATION + READ_COMMITTED_SNAPSHOT,則此時 select 也堵塞。但 update Test 表都堵塞。

      posted @ 2017-06-01 15:11  杰克倫敦塵  Views(1388)  Comments(9)    收藏  舉報
      歡迎大家下載試用折桂單點登錄系統, https://www.zheguisoft.com
      主站蜘蛛池模板: 亚洲女同精品久久女同| 亚洲综合不卡一区二区三区| 欧美日本一区二区视频在线观看| 国产一区二区视频在线看| 无码AV无码免费一区二区| 风韵丰满妇啪啪区老老熟女杏吧| 亚洲欧美中文字幕日韩一区二区| 亚洲精品久久麻豆蜜桃| 午夜免费福利小电影| 国产成人欧美一区二区三区| 久久精品国产亚洲αv忘忧草| 精品国产亚洲av麻豆特色| V一区无码内射国产| 亚洲欧美日韩国产精品专区| 男女爽爽无遮挡午夜视频| 久久久久久综合网天天| 亚洲国产天堂一区二区三区| 亚洲aⅴ无码专区在线观看q| 国产精品自在自线视频| 无码AV无码免费一区二区| 久久久久人妻精品一区三寸| 国产伦精品一区二区亚洲| 欧洲亚洲国内老熟女超碰| 一个色的导航| 粉嫩av国产一区二区三区| 久久精品国产一区二区三| 妖精视频亚州无吗高清版| 日韩成人无码影院| 欧美老少配性行为| 久久天天躁夜夜躁狠狠综合 | 国产精品高清一区二区三区不卡| 久激情内射婷内射蜜桃| 国产区精品视频自产自拍| 国产午夜亚洲精品福利| 人人澡人人妻人人爽人人蜜桃| 国产一区二区三区美女| 亚洲精品自拍视频在线看| www射我里面在线观看| 日韩有码中文字幕国产| 亚洲一本大道在线| 中文字幕av一区二区三区|