Цитата:
Полез в логи базы и сделал следующие выводы: проблема возникает с таблицей inet_session_$mid_seq, которая, по всей видимости, используется для генерирования уникальных идентификаторов сессий. Многочисленные потоки и сервера (?) добавляют и удаляют данные из этой таблицы при авторизации абонентов и, соответственно, устанавливают блокировки. Когда происходит очень много подключений в единицу времени эти блокировки "перекрываются" (с учётом таймаута) и возникает ошибка, которая, возможно, приводит к отбрасыванию соединения - это очень плохо.
Вообще, такой механизм генерирования уникальных идентификаторов сложно назвать удачным - mysql для такой операции слишком избыточен. Здесь бы следовало использовать счётчик в памяти и мютексы (ну или как там это называется). На крайний случай можно использовать NoSQL базы - всё лучше чем MySQL.
В InnoDB, как оказалось поле auto_increment после рестарта MySQL каждый раз инициализируется заново, как MAX(id), что не логично и не удобно, т.к. при переносе соединения в таблицу-лог в inet_connection может остаться соединение с меньшим id и после перезагрузки MySQL появится соединение с дублирующимся id. Счетчик в памяти нельзя использовать, т.к. InetAccounting-ов может быть несколько.
MySQLпредлагают такой вариант для эмулирования sequence:
Цитата:
If expr is given as an argument to LAST_INSERT_ID(), the value of the argument is returned by the function and is remembered as the next value to be returned by LAST_INSERT_ID(). This can be used to simulate sequences:
Create a table to hold the sequence counter and initialize it:
mysql> CREATE TABLE sequence (id INT NOT NULL);
mysql> INSERT INTO sequence VALUES (0);
Use the table to generate sequence numbers like this:
mysql> UPDATE sequence SET id=LAST_INSERT_ID(id+1);
mysql> SELECT LAST_INSERT_ID();
The UPDATE statement increments the sequence counter and causes the next call to LAST_INSERT_ID() to return the updated value. The SELECT statement retrieves that value. The mysql_insert_id() C API function can also be used to get the value. See Section 20.7.3.37, “mysql_insert_id()”.
Он используется сейчас в модуле MPS. Но по тестам он в транзакции блокирует до коммита (т.е. UPDATE sequence SET id=LAST_INSERT_ID(id+1) ждет пока не будет закончена другая транзакция с этой же командой), чего не делает auto_increment.
Поэтому применили измененный вариант, который по тестам в параллельных потоках отрабатывал эффективнее(конкурентнее):
Код:
INSERT INTO inet_connection_<mid>_seq (id) VALUES (NULL)
ps.executeUpdate();
final long id = ServerUtils.lastInsertId( ps );
// чтобы не копить записи в таблице генерирования id
if( id % 100 == 0L )
{
DELETE FROM inet_connection_<mid>_seq WHERE id<?
ps.setLong( 1, id - 500 );
ps.executeUpdate();
}
В последнем билде поменяли 100 и 500 на 1000 и 1000.