Блокировка редактирования базы данных по имени ключа - PullRequest
0 голосов
/ 16 мая 2010

Мне нужно предотвратить одновременное редактирование поля базы данных. Пользователи выполняют операцию push для поля структурированных данных, поэтому я хочу упорядочить операции, а не просто игнорировать одно редактирование и взять второе.

По сути, я хочу сделать

synchronized(key name)
{
  push value onto the database field
}

и настройте синхронизированный элемент таким образом, чтобы одновременно выполнялась только одна операция с «именем ключа». (примечание: я упрощаю, это не всегда простой толчок).

Грубый способ сделать это - глобальная синхронизация, но это узкое место всего приложения. Все, что мне нужно сделать - это выполнить две последовательные записи с одним и тем же ключом, что является редким, но раздражающим случаем.

Это веб-приложение Java, написанное на Spring (и использующее JPA / MySQL). Операция запускается вызовом веб-службы пользователя. (основная причина - когда пользователь отправляет два HTTP-запроса одновременно с одним и тем же ключом).

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

Ответы [ 3 ]

2 голосов
/ 16 мая 2010

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

Класс KeyLock используется для предотвращения одновременных операций базы данных с одним именем ключа.

package KeyLocks;

import java.util.concurrent.locks.Lock;

public class KeyLock
{
    private final KeyLockManager keyLockManager;
    private final String keyName;
    private final Lock lock;

    KeyLock(KeyLockManager keyLockManager, String keyName, Lock lock)
    {
        this.keyLockManager = keyLockManager;
        this.keyName = keyName;
        this.lock = lock;
    }

    @Override
    protected void finalize()
    {
        release();
    }

    public void release()
    {
        keyLockManager.releaseLock(keyName);
    }

    public void lock()
    {
        lock.lock();
    }

    public void unlock()
    {
        lock.unlock();
    }
}

Класс KeyLockManager - это хранилище, отвечающее за время жизни блокировки ключа.

package KeyLocks;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class KeyLockManager
{
    private class LockEntry
    {
        int acquisitionCount = 0;
        final Lock lock = new ReentrantLock();
    }

    private final Map<String, LockEntry> locks = new HashMap<String, LockEntry>();
    private final Object mutex = new Object();

    public KeyLock getLock(String keyName)
    {
        synchronized (mutex)
        {
            LockEntry lockEntry = locks.get(keyName);
            if (lockEntry == null)
            {
                lockEntry = new LockEntry();
                locks.put(keyName, lockEntry);
            }
            lockEntry.acquisitionCount++;
            return new KeyLock(this, keyName, lockEntry.lock);
        }
    }

    void releaseLock(String keyName)
    {
        synchronized (mutex)
        {
            LockEntry lockEntry = locks.get(keyName);
            lockEntry.acquisitionCount--;
            if (lockEntry.acquisitionCount == 0)
            {
                locks.remove(keyName);
            }
        }
    }
}

Вот пример того, как вы бы использовали блокировку клавиш.

package test;

import KeyLocks.KeyLock;
import KeyLocks.KeyLockManager;

public class Main
{
    private static final String KEY_NAME = "TEST_KEY";

    public static void main(String[] args)
    {
        final KeyLockManager keyLockManager = new KeyLockManager();
        KeyLock keyLock = null;
        try
        {
            keyLock = keyLockManager.getLock(KEY_NAME);
            keyLock.lock();
            try
            {
                // Do database operation on the data with the specified key name
            }
            finally
            {
                keyLock.unlock();
            }
        }
        finally
        {
            if (keyLock != null)
            {
                keyLock.release();
            }
        }
    }
}
1 голос
/ 16 мая 2010

Даже если вы заблокируете ключ, в следующий раз вы не сможете убедиться, что это тот же (а не только равный) ключ. Вам нужно что-то вроде выбрать для обновления , сделанное базой данных. Если это невозможно, вам нужно запрограммировать ключ самостоятельно, используя синхронизированный набор заблокированных ключей в качестве члена вашего хранилища / dao.

0 голосов
/ 16 мая 2010
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...