C # транзакции - лучшая производительность - PullRequest
0 голосов
/ 30 сентября 2011

Мое веб-приложение имеет класс WorkItem с RecordID (Guid as Primary Key) и FriendlyID (строка), который состоит из Type-Date-RandomNumbers.

Если я создаю новый 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 несколько раз.
Как лучше всего убедиться, что мой дружественный идентификатор уникален?

  1. Создайте УНИКАЛЬНОЕ ограничение на FriendlyID в БД.
    • Начать транзакцию, сгенерировать FriendlyID, вставить и зафиксировать
    • Откат и попробуйте еще раз, если я получу SQLException.
  2. Просто создайте это.
    • Выберите все рабочие элементы с помощью this.FriendlyID.
    • Если выбрано значение > 1, повторять до тех пор, пока оно не станет == 1

Я уверен, что есть другой способ, но Я думаю, # 1 должен быть предпочтительным.

Есть ли какие-то пути, по которым я скучаю, или путь №1? Я ненавижу использовать исключения для моего рабочего процесса, и я знаю, что они действительно медленные.

Ответы [ 3 ]

2 голосов
/ 30 сентября 2011

Мое предложение в любом случае, независимо от того, какой тип идентификатора вы хотите сгенерировать, делайте это в SQL в хранимой процедуре, а не из клиентского кода .NET.Всегда лучше иметь атомарную точку входа, которая принимает некоторые параметры и выполняет свою работу, так что вы можете вызвать сохраненную запись и получить сохраненную запись и идентификатор обратно вам как выходной параметр, даже более одного, например уникальный код итаким образом, вы перемещаете проблемы параллелизма из клиентского кода .NET на сервер баз данных, и серверы БД предназначены для обеспечения хорошей обработки параллелизма.

2 голосов
/ 30 сентября 2011

Поскольку ваш RecordID уже основан на GUID, я бы проанализировал его, чтобы создать дружественный идентификатор. Guid.ToByteArray() может быть полезным для начала.

0 голосов
/ 30 сентября 2011

Используйте шаблон KeyGenerator от PoEAA от M.Fowler. Вот пример решения для файловой системы, в котором используется мьютекс для межпроцессной блокировки. В случае 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();
            }
        }

    }

}
...