Mysql Innodb не возвращает последнюю строку при выборе с порядком удаления

У нас есть схема таблицы, как показано ниже:

CREATE TABLE IF NOT EXISTS `offers` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `campaign_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `price` double NOT NULL,
  `ip` varchar(15) NOT NULL,
  `cdate` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `campaign_id` (`campaign_id`,`price`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin5 AUTO_INCREMENT=190514 ;

При каждом новом предложении пользователя мы проверяем, был ли последний заказ предоставлен тем же пользователем:

"select user_id from offers where campaign_id='".$campaign['id']."' order by id desc limit 1"

если user_id - то же самое, мы запрещаем новое предложение защитить пользователя от случайных двойных щелчков.

если с предложением нет проблем, мы добавляем предложение:

"insert into offers(campaign_id,user_id,price,ip,cdate) values (".$campaign['id'].",".$user['id'].",'".$price."','".$_SERVER['REMOTE_ADDR']."',".time().")"

Но проблема в том, что select возвращает только последнюю вставленную строку примерно через 1 секунду. Это означает, что пользователь может вставить несколько предложений, если он нажмет кнопку слишком быстро.

Мы используем 5.5.30-30.2-log Percona Server в качестве сервера базы данных. Ниже приведен наш файл my.cnf:

[mysqld]
datadir                         = /var/lib/mysql
tmpdir                          = /var/lib/mysqltmp
socket                          = /var/lib/mysql/mysql.sock
skip-external-locking           = 1
skip-name-resolve
open-files-limit                = 40000
max_heap_table_size             = 64M
tmp_table_size                  = 64M
log-error                       = /var/log/mysqld.log
thread-cache-size               = 50
table-cache                     = 4096
table-open-cache                = 4096
table-definition-cache          = 512
query-cache-size                = 0
query-cache-limit               = 16M
query-cache-type                = 0
sort-buffer-size                = 1M
read-buffer-size                = 1M
read-rnd-buffer-size            = 8M
join-buffer-size                = 1M
tmp-table-size                  = 64M
max-heap-table-size             = 64M
back-log                        = 100
max-connections                 = 10000
max-connect-errors              = 10000
max-allowed-packet              = 256M
interactive-timeout             = 360
wait-timeout                    = 360
innodb                          = FORCE
key-buffer-size                 = 32M
myisam-sort-buffer-size         = 4M
innodb-buffer-pool-size         = 60G
innodb-log-file-size            = 256M
innodb_log_files_in_group       = 2
innodb-log-buffer-size          = 4M
innodb-file-per-table          = 1
innodb-thread-concurrency       = 8
innodb-flush-log-at-trx-commit  =2
server-id                       = 1
slow-query-log                  = 1
slow-query-log-file             = /var/lib/mysqllogs/slow-log

4 ответа

Решение

Я думаю, что проблема была вызвана несколькими подключениями, выбирающими одну и ту же строку перед вставкой, как показано ниже:

php connection 1: выберите последние предложения user_id

php connection 2: выберите последние предложения user_id (укажите одно и то же)

PHP соединение 1: вставить предложение

PHP соединение 2: вставить предложение

Я решил проблему, заблокировав таблицу перед выбором и разблокировав ее после вставки, например:

Блокировка таблицы предлагает написать

выберите user_id последнего предложения

если он отличается от текущего предложения вставки пользователя

разблокировать таблицу предложений

Вы пытались использовать триггеры SQL при обновлении / вставке событий, чтобы проверить, например,

SELECT count(*) FROM `offers` 
WHERE campaign_id = [Your_value] AND user_id = [Your_value];

равно 0, и действовать в соответствии с ним. (Если он уже существует, не вносите никаких изменений [...])

Редактировать: Между прочим, это скорее проблема Ajax / Php / независимо от того, что вы используете, чем неправильное использование базы данных MySQL. Вы должны запретить пользователю отправлять много запросов за короткий промежуток времени.

MySQL кеширование запросов.

MySQL проверит первый щелчок и возвратит пустой результат, затем вставит запись, затем каждый последующий щелчок MySQL возвращает кэшированное значение пустого результата и разрешает другую вставку.

У нас было что-то подобное на сайте, над которым я работал, с очень близкими запросами.

В итоге нам пришлось запустить это первым

SET SESSION query_cache_type=0;

Вы пытались явно обернуть вашу вставку в транзакцию? Делать что-то вроде:

START TRANSACTION
INSERT ...
COMMIT
SELECT ...

Это должно гарантировать, что SELECT вернет последние вставленные данные.

Подробнее об уровнях изоляции InnoDB: [1] и [2]

Другие вопросы по тегам