Как запретить одновременное выполнение определенного участка кода при нескольких одновременных подключениях?
Я не знаю как правильно спросить у гугла, но уверен, что ответы уже есть.
В общем, суть следующая. Есть скрипт php (определенный участок), этот скрипт работает с базой данных. Данный скрипт добавляет данные в таблицы, если этих данных не существует. Само собой настроены связи у таблиц, в том числе и множественные.
Дело в том, что иногда добавляются дубликаты этих данных при одновременном подключении и добавлении одинаковых данных. Первое подключение видимо проверяет, что данные не существуют и принимает решение добавить в то время, когда второй начал эти данные проверять ну и тоже принимает соответствующее решение.
Я думал, что мне помогут транзакции, но не тут то было. После того, как я их настроил, стало только хуже.
Добавляются абсолютно все данные. Первое подключение пока проверяет и добавляет все данные, второе подключение делает то же самое, так как первое ещё не сделало свой commit и результаты всей работы не отражены.
Как правильно поступить в моем случае? Я пытаюсь что-то узнать у гугла, но толком ничего не нашел, кроме SyncMutex.
Если честно, даже не знаю то ли это и как оно работает вообще.
В моем понимании как-то бы заблокировать работу определенного участка кода, пока не выполнился этот же участок кода в другом подключении, но как это сделать - я не знаю.
Тип таблиц innodb, если что.
Подключение к базе ведется посредством PDO.
Ну ладно, пусть я буду новичком, но вы бы хотя бы намекнули куда копать. Я уж сам понял, что мне нужна блокировка, но в каком месте и как её реализовать?
Блокировать на чтение таблицу - как-то не очень идея для меня, это пока будет добавляться большое количество данных, то тому пользователю, которому эти данные нужны в итоге будет ждать?
svm, Что то вы делаете не так. PHP по своей сути отстрелялся и умер. Возможно что вы берете данные при первом заходе и пытаетесь их вставить при посте, когда кто то их уже изменил.
Стандартное решение лочить таблицу или запись, запрашивать данные, сверять не изменились ли с последнего раза и обновлять или выкидывать ошибку с сообщением.
По-моему самое простое и правильное решение здесь - insert ignore, другой вариант транзакции с блокированием таблицы, но это может сильно ударить по производительности.
Если вы имеете ввиду настроить уникальный индекс и добавлять таким образом, то тоже вряд ли получится. Я уже подумал об этом, когда писал вопрос. В принципе, идея хорошая, но в текущей реализации моей работы, это будет чуть-чуть проблематично сделать. Там тип поля надо менять и немного изменять вводимые данные.
svm, тогда блокировки. Как вариант блокировка на уровне приложения, использовать mutex на проблемном участке. Это будет блокировать только проблемный участок, а все остальные запросы будут проходить без блокировки.
Vitsliputsli, Мне очень понравилось, что вы мне сейчас сказали, да вот я не знаю как эта штука работает. Направьте на путь истинный, пожалуйста ))) Что почитать, чтобы понять?
svm, конкретное решение зависит от вашей архитектуры и инструментов (например фреймворков php, там зачастую есть готовые удобные решения).
В общем случае, перед проблемным участком вы вызываете блокировку мьютексом. Параллельный процесс видя блокировку будет ждать пока произойдет разблокировка (опционально можно прервать выполнение кода). После выполнения проблемного участка происходит разблокировка мьютексом, и параллельный процесс может в свою очередь произвести блокировку мьютексом и начать выполнять проблемный участок.
Блокировка мьютексом может быть разной, основные варианты - файловая блокировка, разумеется это будет работать только если у вас 1 сервер приложений, блокировка в БД, если 1 СУБД.
svm, по SyncMutex в описании не нашел как проходит блокировка, но скорее всего это будет работать в пределах 1 сервера приложений (иначе не сделаешь), так что проверяйте такие моменты.
Vitsliputsli, Инструменты в текущей реализации минимальны. Никаких фреймворков не используется. Просто на чистом PHP пишу. Если подскажете библиотеку для реализации этого мутекса, буду благодарен.
Если вдруг знаете что-то. Сам тоже поищу, конечно же.
Если вы имеете ввиду настроить уникальный индекс и добавлять таким образом, то тоже вряд ли получится.
Вы как-то слабо обосновали почему, по моему абсолютно пофиг какие поля связывать для получения составного уникального индекса, если одно из них не блоб/текст... и даже в таком случае можно создать поле с хешем, гарантирующим хорошую уникальность данных..
ThunderCat, Поле - текст. Плюс ещё ко всему этому тогда структуру данных в другом поле надо будет менять, так как сейчас там JSON строка, в которой два и более пункта, которые расположены в хаотичном порядке. В принципе, по алфавиту разложить не долго всю таблицу, а вот с полем текст уже проблемка.
Дело в том, что у меня полнотекстовый поиск сделан по этим полям - только для добавления данных, так что тут на самом деле проблемка. Но идея хорошая, я подумаю обязательно что мне лучше сделать.
svm, не подскажу, нужно самому смотреть. В принципе, там все просто. Если у вас 1 сервер app, то берите SyncMutex. Если несколько серверов app и 1 СУБД, то делайте блокировку через нее (не обязательно MySQL, можно и в Redis, если он общий). В зависимости от СУБД делать нужно по-разному, тут лучше сперва поискать готовые решения.