Транзакции на C # — лучшая производительность

#c# #performance #transactions

#c# #Производительность #транзакции

Вопрос:

В моем веб-приложении есть класс WorkItem с RecordID (Guid в качестве первичного ключа) и FriendlyID (string), который состоит из Type-Date-RandomNumbers .

Если я создаю новый рабочий элемент, я также создаю новый 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 в БД.
    • Запустите транзакцию, сгенерируйте дружественный идентификатор, вставьте и зафиксируйте
    • Откат и повторите попытку, если я получу исключение SQLException.
  2. Просто создайте его.
    • Выберите все рабочие элементы с this.FriendlyID помощью .
    • Если выбор есть > 1 , повторяйте, пока он не == 1

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

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

Комментарии:

1. Какие требования предъявляются к идентификатору, чтобы быть «дружественным»?

2. Почему вы не можете использовать RecordID as FriendlyID какие-либо требования для FriendlyID ?

3. Почему у вас есть два столбца для идентификации строки в таблице? У вас должен быть только один. Другое дело, что вы не должны использовать GUID в качестве кластеризованного индекса (я предполагаю, что вы используете значение по умолчанию из mssql, PK)

4. Я не могу понять, почему заголовок вопроса содержит best performance

5. Вопрос не в том, как сгенерировать FriendlyId, речь идет о лучшем и быстром способе убедиться, что он уникален , и сгенерировать новый, если это не так.

Ответ №1:

Мое предложение в любом случае, какой бы идентификатор вы ни хотели сгенерировать, делайте это в SQL в хранимой процедуре, а не из .ЧИСТЫЙ клиентский код. Всегда лучше иметь атомарную точку входа, которая принимает некоторые параметры и выполняет работу, чтобы вы могли вызвать сохраненную и сохранить свою запись, а идентификатор вернуть вам в качестве параметра out, даже больше, чем один, например, уникальный код и идентификатор guid.

таким образом, вы перемещаете проблемы параллелизма из .СЕТЕВОЙ клиентский код для сервера базы данных и серверов БД спроектирован так, чтобы хорошо обрабатывать параллелизм.

Комментарии:

1. Я не уверен, что это возможно с нашей текущей архитектурой, но я мог бы создать триггер, который вызывается после вставки и в основном делает то же самое. Спасибо за идею!

2. пожалуйста, не делайте этого! избегайте такого рода разреженной логики, просто используйте правильный уникальный индекс и ограничение PK и делайте все в сохраненной процедуре. Требует определенных усилий, но действительно окупается… Я видел так много триггеров ни для чего, например, установка значения по умолчанию или выполнение других действий, которые могут и должны выполняться обычными ограничениями по умолчанию…

Ответ №2:

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

Комментарии:

1. Вопрос не в создании идентификатора, а в том, чтобы убедиться, что он уникален

2. Моя точка зрения / полностью /. GUID / гарантированно / уникальны. Все другие (надежные) подходы, выполняемые на стороне клиента, будут связаны с блокировками и, следовательно, потенциальными конфликтами; это может быть непреодолимым, если вы запускаете этот код, например, из фермы веб-серверов без межпроцессного взаимодействия. предложение @ Davide перенести генератор на сервер базы данных уменьшит падение производительности, поскольку тогда будет только одно место, где генерируются идентификаторы, но это не позволит избежать этого полностью.

Ответ №3:

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

    }

}