如何处理几十万条并发数据

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 有着固有缺陷:极难编码,极难证明其正确性。读写锁方案的应用范围更加广泛一些。

 

posted @ 2016-06-23 18:22  光阴的故事-SKY  阅读(5108)  评论(0编辑  收藏  举报