如何處理幾十萬條並發數據


NO 1 如何處理幾十萬條並發數據

推薦使用時間戳來解決的。

 比如我們在SQL Server中的表中定義一個字段為timestamp類型的字段ts,這個字段的值不需要我們進行控制的。

--0x00000000000007DD  --Insert

 

declare @ts timestamp

select @ts=ts from data where id =21

 

update data set Name='sky' where id =21 and ts=@ts

 

 --0x00000000000007DE  ---Update

 在Insert與Update時,數據庫會自己進行ts值的更新,因此我們只要在Update時使用:
 Update xxx where key=@key and ts=@ts 就可以了,根本不用考慮ts里的值更新。

 Delete時也最好進行一下判斷,用這種方式是可以控制數據並發操作的。
只需要在Update與Delete時,判斷"影響條數"就可以知道更新是否成功。 下面我就介紹一下,在.NET下如何實現,自設的時間戳控制。

 

 或者也可以采用Guid值,也可以產生唯一值,但我覺得Guid值太大,可能會影響效率。

或者也可以采用DateTime.Now.Ticks,這是一個12位的數字

 那好,在我們Insert時:Insert xxxx ts='221283747584' where key='1' 
 在Update時 Update xx set xxx ts='39383848593839' where key='1' and ts='111111111111' //假設取到的原值為'11111111111'  
 Delete類似上面的。
 
 我們判斷影響條數,如果為0則說明更新不成功。



 

 

 如果是批量更新與批量刪除,如何進行並發控制呢?
 由於批量更新時,不是一條記錄:Update xxx where Birthday='2004-2-1'之類的,會影響到N條數據,要進行並發控制就不那么容易了。如果還是采用一條條判斷ts那是不現實的。
  對於這種只能放棄並發控制嗎?

 在多線程編程時,開發人員經常會遭遇多個線程讀寫某個資源的情況。這就需要進行線程同步來保證線程安全。一般情況下,我們的同步措施是使用鎖機 制。但是,假如線程只對資源進行讀取操作,那么根本不需要使用鎖;反之,假如線程只對資源進行寫入操作,則應當使用互斥鎖(比如使用 Monitor類等)。還有一種情況,就是存在多個線程對資源進行讀取操作,同時每次只有一個線程對資源進行獨占寫入操作。

 

 .NET 中提供的兩個讀寫鎖類。然而 .NET 3.5 提供的新讀寫鎖 ReaderWriterLockSlim 類消除了ReaderWriterLock 類存在的主要問題。與 ReaderWriterLock相比,性能有了極大提高。更新具有原子性,也可以極大避免死鎖。更有清晰的遞歸策略。在任何情況下,我們都應該使用 ReaderWriterLockSlim 來代替 ReaderWriterLock 類。

我對其進行了 .NET 封裝。代碼如下:

using System;
using System.Threading;
using System.Runtime.InteropServices;
  
namespace Lucifer.Threading.Lock
{
  /// <summary>
  /// Windows NT 6.0 才支持的讀寫鎖。
  /// </summary>
  /// <remarks>請注意,這個類只能在 NT 6.0 及以后的版本中才能使用。</remarks>
  public sealed class SRWLock
  {
    private IntPtr rwLock;
  
    /// <summary>
    /// 該鎖不支持遞歸。
    /// </summary>
    public SRWLock()
    {
      InitializeSRWLock(out rwLock);
    }
  
    /// <summary>
    /// 獲得讀鎖。
    /// </summary>
    public void EnterReadLock()
    {
      AcquireSRWLockShared(ref rwLock);
    }
  
    /// <summary>
    /// 獲得寫鎖。
    /// </summary>
    public void EnterWriteLock()
    {
      AcquireSRWLockExclusive(ref rwLock);
    }
  
    /// <summary>
    /// 釋放讀鎖。
    /// </summary>
    public void ExitReadLock()
    {
      ReleaseSRWLockShared(ref rwLock);
    }
  
    /// <summary>
    /// 釋放寫鎖。
    /// </summary>
    public void ExitWriteLock()
    {
      ReleaseSRWLockExclusive(ref rwLock);
    }
  
    [DllImport("Kernel32", CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
    private static extern void InitializeSRWLock(out IntPtr rwLock);
  
    [DllImport("Kernel32", CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
    private static extern void AcquireSRWLockExclusive(ref IntPtr rwLock);
  
    [DllImport("Kernel32", CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
    private static extern void AcquireSRWLockShared(ref IntPtr rwLock);
  
    [DllImport("Kernel32", CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
    private static extern void ReleaseSRWLockExclusive(ref IntPtr rwLock);
  
    [DllImport("Kernel32", CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
    private static extern void ReleaseSRWLockShared(ref IntPtr rwLock);
  }
}此外,在其他平台也有一些有意思的讀寫鎖。比如 Linux 內核中的讀寫鎖和 Java 中的讀寫鎖。感興趣的同學可以自己研究一番。

 讀寫鎖有個很常用的場景就是在緩存設計中。因為緩存中經常有些很穩定,不太長更新的內容。MSDN 的代碼示例就很經典,我原版拷貝一下,呵呵。代碼示例如下:

using System;
using System.Threading;
  
namespace Lucifer.CSharp.Sample
{
  public class SynchronizedCache
  {
    private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
    private Dictionary<int, string> innerCache = new Dictionary<int, string>();
  
    public string Read(int key)
    {
      cacheLock.EnterReadLock();
      try
      {
        return innerCache[key];
      }
      finally
      {
        cacheLock.ExitReadLock();
      }
    }
  
    public void Add(int key, string value)
    {
      cacheLock.EnterWriteLock();
      try
      {
        innerCache.Add(key, value);
      }
      finally
      {
        cacheLock.ExitWriteLock();
      }
    }
  
    public bool AddWithTimeout(int key, string value, int timeout)
    {
      if (cacheLock.TryEnterWriteLock(timeout))
      {
        try
        {
          innerCache.Add(key, value);
        }
        finally
        {
          cacheLock.ExitWriteLock();
        }
        return true;
      }
      else
      {
        return false;
      }
    }
  
    public AddOrUpdateStatus AddOrUpdate(int key, string value)
    {
      cacheLock.EnterUpgradeableReadLock();
      try
      {
        string result = null;
        if (innerCache.TryGetValue(key, out result))
        {
          if (result == value)
          {
            return AddOrUpdateStatus.Unchanged;
          }
          else
          {
            cacheLock.EnterWriteLock();
            try
            {
              innerCache[key] = value;
            }
            finally
            {
              cacheLock.ExitWriteLock();
            }
            return AddOrUpdateStatus.Updated;
          }
        }
        else
        {
          cacheLock.EnterWriteLock();
          try
          {
            innerCache.Add(key, value);
          }
          finally
          {
            cacheLock.ExitWriteLock();
          }
          return AddOrUpdateStatus.Added;
        }
      }
      finally
      {
        cacheLock.ExitUpgradeableReadLock();
      }
    }
  
    public void Delete(int key)
    {
      cacheLock.EnterWriteLock();
      try
      {
        innerCache.Remove(key);
      }
      finally
      {
        cacheLock.ExitWriteLock();
      }
    }
  
    public enum AddOrUpdateStatus
    {
      Added,
      Updated,
      Unchanged
    };
  }
}

  再次 Update 於 2008-12-07 0:47

  如果應用場景要求性能十分苛刻,可以考慮采用 lock-free 方案。但是 lock-free 有着固有缺陷:極難編碼,極難證明其正確性。讀寫鎖方案的應用范圍更加廣泛一些。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM