Я уверен, что все знают радости параллелизма, когда дело доходит до потоков.
Представьте себе следующий сценарий при каждой загрузке страницы при произвольной настройке MySQL db:
UPDATE stats SET visits = (visits+1)
Если тысяча пользователей загружает страницу одновременно, будет ли счет причиной каких-либо проблем? это механизм блокировки / блокировки таблицы? Какой из них использует mysql.
У вас есть две потенциальные проблемы:
Правильный ответ зависит от того, могут ли два пользователя вычислить (посещение + 1) при одном и том же значении посещения. Мы можем представить, что база данных должна выполнять следующие действия:
Read visit count Add one to visit count Write visit count
Так что, если два пользователя работают одновременно, они оба могут прочитать одно и то же старое значение? Именно здесь вступает в действие уровень изоляции транзакции. Как заметил Artefacto, уровень изоляции по умолчанию является повторяемым, и, следовательно, мы получаем:
Grab a lock Read, increment, Write Release lock
Скорее, чем
Read (two users same old value) Increment First user Grab Lock, second waits Write Release, second user grabs lock Write (same value!) Release
Однако уровень конкуренции может быть довольно высоким и очень зависит от объема вашей транзакции. Предположим, что у вас есть:
Begin transaction Do the visit increment stuff Do some serious business work End transaction <==== visit lock is held until here
Тогда вы получите много людей, ожидающих этого посещения. Мы не знаем общую структуру вашего приложения, независимо от того, используете ли вы такие крупные транзакции. Скорее всего, вы получаете поведение по умолчанию для одной транзакции в SQL-заявлении, и в этом случае вы являетесь конкурентом только на время выполнения инструкции SQL, в значительной степени, как вы надеялись.
Другим людям может быть не так повезло: есть среды (например, Java EE Servlets), где неявные области транзакций могут быть созданы инфраструктурой, а затем более продолжительные транзакции, которые я показываю выше, происходят по умолчанию. Хуже того, что ваш код не написан последовательно (с инкрементным посещением всегда сначала или всегда последним) вы можете получить:
Begin transaction Do the visit increment stuff Do some serious business work End transaction <==== visit lock and business locks held until here
а также
Begin transaction Do some other serious business work Do the visit increment stuff End transaction <==== visit lock and maybesame business locks held until here
И бинго: Тупик
Для сайтов с большими объемами вы можете рассмотреть возможность записи события «Посещение» в очередь и наличие демона, который прослушивает эти события и поддерживает подсчет. Более сложное, но, возможно, меньшее количество спорных вопросов.
Нет, это не испортит. Это вполне приемлемо в любой ACID-совместимой БД. I
выступаю за Изоляцию . Каждый из этих запросов блокирует все строки в таблице посещений . A
(в ACID) означает Atomicity и означает, что транзакция должна выполняться полностью или вообще не выполняться.
Для MySQL в руководстве написано:
[Повторяемое чтение] – это уровень изоляции по умолчанию для InnoDB. […] Для операторов […]
UPDATE
иDELETE
блокировка зависит от того, использует ли оператор уникальный индекс с уникальным условием поиска или условие поиска типа диапазона. Для уникального индекса с уникальным условием поиска InnoDB блокирует только найденную индексную запись, а не пробел перед ней. Для других условий поиска InnoDB блокирует сканируемый диапазон индексов, используя блокировки блокировки или блокировки блокировки плюс индекс-запись, чтобы блокировать вставки другими сеансами в промежутки, охватываемые диапазоном.
Поэтому я бы сказал, да, все в порядке, хотя этот конкретный запрос может блокировать всю таблицу. Вероятно, было бы лучше:
UPDATE stats SET value = value + 1 WHERE key = 'visits'
с индексом «ключ».
Кажется, что все ответы предполагают таблицу InnoDB
, которая поддерживает транзакции; с таблицами MyISAM
, вместо этого вы получаете «атомные транзакции», что должно быть хорошо для вашего конкретного варианта использования (хотя для общего случая они не соответствуют полной ACID).
В документах MySQL по транзакциям (например, здесь ) он дает форму UPDATE
как типичный пример хорошей практики , и я цитирую …:
Это дает нам нечто похожее на блокировку столбцов, но на самом деле даже лучше, потому что мы только обновляем некоторые из столбцов, используя значения, относящиеся к их текущим значениям. Это означает, что типичные утверждения UPDATE выглядят примерно так:
UPDATE tablename SET pay_back=pay_back+125;
… Это очень эффективно и работает, даже если другой клиент изменил значения в
pay_back
[[column]]
Удостоверьтесь, что у вас есть SET autocommit
поэтому это рассматривается как транзакция, и счет будет в порядке. Единственное беспокойство – производительность (например, наличие горячей точки стола)
Это будет работать, если вы:
Будьте особенно осторожны со вторым моментом. Это не проблема, потому что MySQL позволяет вам блокировать блокировки до такой степени, что это действительно испортит.
С другой стороны (когда блокировка настроена правильно), если вы нажмете какой-то (очень) интенсивный трафик, это может стать вашей шеей бутылки (поскольку она может выполняться только в одном потоке). Если вы держите транзакцию открытой дольше, чем просто обновлять номер, это становится более вероятным, и это может даже вызвать тупик, если вы не будете осторожны, как подробно объясняет djna.
Поскольку все сказали, что это заблокирует строку, если вы используете InnoDB. Теперь, если вы используете только одну строку, и эта строка хранит статистику обо всех обращениях, тогда блокировка этой строки может замедлить работу, так как запросы ожидают получения блокировки. Это замедление может быть незаметным под вашими нагрузками. Если это важно, вы можете обойти его, написав несколько разных строк, скажем 0-255. Это все равно будет блокировать каждую строку, но вероятность блокировки теперь составляет 1/256 от того, что изначально было. Когда вы хотите получить общее количество, вы можете просто суммировать все строки.
UPDATE stats SET value=value+1 WHERE id=X
где X – случайный id 0-255
тогда
SELECT SUM(value) FROM stats
даст вам реальную сумму.
Все нормально.
все, что «блокировка таблицы / блокировка строк» - это базы данных дерьма, которые были изобретены, чтобы позаботиться.
Могут возникнуть другие проблемы, когда «тысяча пользователей загружает страницу одновременно», например, обновление индекса. Но это еще одна история, и noobily настройка MySQL в любом случае не является делом.