Реализация атомарных счетчиков с помощью MySQL, ASP.NET и Entityframework

Я разрабатываю веб-приложение в ASP.NET/С# с MySQL и Entity Framework. Я хочу использовать некоторые столбцы счетчиков, которые будут часто обновляться путем увеличения их значения +1. У меня есть таблица продуктов, в которой находятся столбцы счетчиков. У меня есть два вопроса:

1) Должен ли я использовать вторую таблицу для счетчиков? - Основная таблица продуктов будет часто считываться, и если я буду очень часто обновлять строку, это может снизить производительность, потому что строка будет блокироваться каждый раз, когда обновляются счетчики (я использую InnoDB)

2) Как лучше всего увеличить строку на +1 с помощью MySQL и Entity Framework. Я не против того, что счетчик будет на 100% точен, поэтому если будут какие-то конфликты обновлений, я их поймаю, но могу и пройти без обновления. Я предполагаю, что большинство обновлений будет сделано быстро и будет включено в обновление.

Я добавляю некоторый код, который я использую для реализации этого, используя статическую переменную, чтобы предотвратить частую запись в БД.

Код:

if (Global.DataCounters != null)
    {
        if (Global.DataCounters.ContainsKey(id))
        {
            int new_value = ++Global.DataCounters[id];
            Global.DataCounters.Remove(id);
            Global.DataCounters.Add(id, new_value);

            // check datatime

            if ((DateTime.Now - Global.LastFlushed).Minutes > 10)
            {
                // update counter in db and reset data

                Global.LastFlushed = DateTime.Now;
            }
        }
        else
        {
            // first time view of a device, set to zero views
            Global.DataCounters.Add(id, 1);
        }

    }
    else
    {
        Global.DataCounters = new Dictionary<int, int>();
    }

Глобальный.asax:

public static Dictionary<int, int> DataCounters = new Dictionary<int,int>();
public static DateTime LastFlushed;

void Application_Start(object sender, EventArgs e)
{
    LastFlushed = DateTime.Now;
    RegisterRoutes(System.Web.Routing.RouteTable.Routes);
}

Как вы думаете, это хорошая реализация?

Спасибо


person Idan Shechter    schedule 22.01.2013    source источник


Ответы (1)


Вторая таблица для счетчиков будет полезна только в том случае, если, скажем, для значительных чтений таблицы продуктов (скажем,> 50%) не требуется информация о счетчиках. В противном случае вы всегда будете присоединяться к двум таблицам, делая чтение дорогим.

На основе документа MySQL: для обновления счетчики, если ваше условие WHERE использует уникальный индекс для поиска уникальной строки (для обновления), тогда будет взята только блокировка на уровне строки (и блокировка промежутка не будет взята). Это был бы наилучший возможный сценарий для вашего случая (при условии, что идентификатор продукта может использоваться как уникальный индекс). Я предлагаю использовать Entity SQL для запуска оператора Update (или вызова SP, который сделает то же самое).

Если вы хотите играть только с терминами сущностей (без EntitySQL или SP), то не используйте область транзакций для этой конкретной операции - таким образом, выбор для получения сущности продукта не будет блокироваться, а SaveChanges будет одним оператором обновления, увеличивающим счетчики. Конечно, использование двух операторов вне транзакции означает, что могут быть шансы, что обновление завершится ошибкой (при условии, что у вас есть некоторый столбец меток времени, который можно использовать для проверки оптимистического параллелизма), но вы указали, что это приемлемо.

Наконец, если транзакции неизбежны при увеличении счетчиков, попробуйте сделать их короче и попробуйте использовать более низкий уровень изоляции, например Read Committed.

person VinayC    schedule 22.01.2013