C#事务-最佳性能

本文关键字:性能 最佳 事务 | 更新日期: 2023-09-27 18:10:12

我的Web应用程序有一个类WorkItem,其中包含一个RecordID(Guid作为主键(和一个由Type-Date-RandomNumbers组成的FriendlyID(字符串(。

如果我创建了一个新的WorkItem,我也会创建一个新FriendlyID
FriendlyID的格式不能更改(客户端规范(,与<Type (one char)>-<Current Date (yyymmdd)>-<6 random numbers>类似。

private string GenerateFriendlyID()
{
    string res = String.Empty;
    // code omited
    // ...
    // IT'S NOT THE QUESTION HOW TO PROGRAM THIS METHOD!
    // It's about the fastest and best way/design to make 
    // sure the generated ID is unique! (see below)
    return res; // sth like "K-20110930-158349"
}
public override void Create()
{
    if (String.IsNullOrEmpty(friendlyID))
    {
        GenerateFriendlyID();
    }
    base.Create();
}

这段代码在重负载下确实会失败,所以我多次获得相同的FriendlyID
确保我的友好ID唯一的最佳方法是什么

  1. 在数据库中对FriendlyID进行UNIQUE约束
    • 开始事务,生成FriendlyID,插入并提交
    • 如果出现SQLException,请回滚并重试
  2. 只要创建它。
    • 使用this.FriendlyID选择所有工作项
    • 如果选择是> 1,则重复此操作,直到它是== 1

我相信还有另一种方法,但我想#1应该是首选。

有没有我错过的路,或者#1是我要走的路?我不喜欢在我的工作流程中使用异常,我知道它们真的很慢。

C#事务-最佳性能

我的建议是,在任何情况下,无论你想生成什么类型的id,都要在SQL中的存储过程中执行,而不是从中执行。NET客户端代码。最好有一个原子入口点,它接受一些参数并完成任务,这样你就可以调用存储的,保存你的记录,并将id作为out参数返回给你,甚至不止一个,比如唯一的代码和guid。

然后,通过这种方式,您可以将并发问题从中移除。NET客户端代码到数据库服务器和数据库服务器是设计的,以很好地处理并发。

由于RecordID已经基于GUID,我会解析它来创建友好的ID。Guid.ToByteArray()可能是一个有用的起点。

使用M.Fowler的PoEAA中的KeyGenerator模式。这是一个文件系统解决方案的示例,它使用互斥来进行跨进程锁定。在MS SQL的情况下,您可以使用事务而不是互斥。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using System.Runtime.CompilerServices;
namespace ConsoleApplication1
{
    public class KeyGenerator
    {
        private string FileName;
        private long IncrementedBy;
        private long NextId;
        private long MaxId; 
        public KeyGenerator(string filename, long incrementedby)
        {
            FileName = filename;
            IncrementedBy = incrementedby;
            NextId = MaxId = 0; 
        }

        //[MethodImpl(MethodImplOptions.Synchronized)]
        public long NextID()
        {

            if (NextId == MaxId)
            {
                reserveIds();
            }
            return NextId++; 
        }
        private void reserveIds()
        {
            Mutex m = new Mutex(false, "Mutex " + FileName.Replace(Path.DirectorySeparatorChar, '_'));
            try
            {
                m.WaitOne();
                string s = File.ReadAllText(FileName);
                long newNextId = long.Parse(s);
                long newMaxId = newNextId + IncrementedBy; 
                File.WriteAllText(FileName, newMaxId.ToString());
                NextId = newNextId;
                MaxId = newMaxId;
                // Simulate some work.
                Thread.Sleep(500);
            }
            finally
            {
                m.ReleaseMutex();
            }
        }
    }
}