За последние 24 часа нас посетили 17560 программистов и 1649 роботов. Сейчас ищут 839 программистов ...

Ошибка с блокировкой таблицы и multi_query

Тема в разделе "PHP и базы данных", создана пользователем tipati, 2 фев 2016.

  1. tipati

    tipati Новичок

    С нами с:
    14 окт 2015
    Сообщения:
    15
    Симпатии:
    0
    Есть скрипт. Получает аналоги деталей, записывает их себе в базу (естественно, разные потоки могут пытаться одновременно это делать) и выводит результат. Блокирую таблицы для получения нового id и записи всей цепочки именно с ним. Если не блокировать - все отрабатывает замечательно. Если блокирую - получаю ошибку после multi_query и соответственно отсутствие вывода. Скрипт в целом использует старый способ общения с mysql и только кусок с блокировкой - mysqli.

    Код (PHP):
    1. <?php
    2. //создаем новое модное соединение. в рамках него же будем блокировать таблицу.
    3. $mysqli = new mysqli("server", "user", "password", "db");
    4. ...
    5. if(empty($crossid))
    6. {
    7.   //получаем новый номер кросса, блокируя таблицу
    8.   echo("get lock: ".array_pop($mysqli->query("SELECT GET_LOCK('crosses',1)")->fetch_row())."<br>");
    9.   $mysqli->query("LOCK TABLES crosses WRITE");
    10.   $crossid=intval(array_pop($mysqli->query("SELECT MAX(crossid) FROM crosses")->fetch_row()))+1;
    11. }
    12. //сохраняем запрошенный номер. если он существует, но по нему не было запросов
    13. $mysqli->query("UPDATE crosses SET req=1,added=NOW() WHERE branum='".substr(preg_replace("|[^A-Z0-9]|","",strtoupper($brands[$partnum])),0,4).$partnum."'");
    14. //иначе это новый номер
    15. if(!$mysqli->affected_rows)
    16.   $mysqli->query("INSERT INTO crosses SET branum='".substr(preg_replace("|[^A-Z0-9]|","",strtoupper($brands[$partnum])),0,4).$partnum."',brand='".$brands[$partnum]."', number='".$partnum."',crossid='".$crossid."',req=1,added=NOW()");
    17. //сохраняем номера аналогов
    18. $repldata="";
    19. foreach($crosses as $v)
    20.   $repldata.="INSERT IGNORE INTO crosses SET branum='".substr(preg_replace("|[^A-Z0-9]|","",strtoupper($v['brand'])),0,4).$v['number']."',brand='".$v['brand']."', number='".$v['number']."',crossid='".$crossid."';";
    21. echo("is free lock1: ".array_pop($mysqli->query("SELECT IS_FREE_LOCK('crosses')")->fetch_row())."<br>");
    22. $mysqli->multi_query($repldata);
    23. echo("is free lock2: ".array_pop($mysqli->query("SELECT IS_FREE_LOCK('crosses')")->fetch_row())."<br>");
    24. $mysqli->query("UNLOCK TABLES");
    25. ... 
    Получаю выхлоп:
    204 - это где последний SELECT IS_FREE_LOCK.
    Почему после multi_query не срабатывает следующий mysqli()->query?

    Используй [code=php] чтобы было красиво.
        — модераторъ
     
  2. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.131
    Симпатии:
    1.250
    Адрес:
    там-сям
    наверное mysqli::$error скажет тебе больше, чем мы.

    Добавлено спустя 2 минуты 17 секунд:
    p.s. не вижу смысла в multiquery. один только гемор.
     
  3. tipati

    tipati Новичок

    С нами с:
    14 окт 2015
    Сообщения:
    15
    Симпатии:
    0
    был пуст... хотя... может, я его не там писал...
    попробуй записать несколько десятков тысяч строк, когда скрипт находится на одном сервере, а база данных - на другом континенте.
     
  4. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.131
    Симпатии:
    1.250
    Адрес:
    там-сям
    Странное объяснение. Мультиквери не нужен.
     
  5. tipati

    tipati Новичок

    С нами с:
    14 окт 2015
    Сообщения:
    15
    Симпатии:
    0
    А ты просто попробуй раздельные запросы и посмотри, сколько времени они будут выполняться.

    По теме: "Commands out of sync; you can't run this command now". А проблема была в том, что перед использованием fetch_row() надо почистить вывод от multi_query.

    Код (PHP):
    1. while($mysqli->next_result())
    2.   $mysqli->store_result();
    3.  
    Вот что называется "утро вечера мудренее"...

    Подсказка от модератора:
    Любой код или текст конфигурации пишите между тегом [code=php] и [/code].
    Используйте отступы в коде для форматирования текста.
    Это помогает быстрее понять вас, увеличивает шанс на получение ответа.
    Что выделять? Например: PHP, HTML, CSS, JavaScript, SQL, XML, .htaccess, ini, регулярные выражения, код шаблонизаторов, любая другая разметка, результаты array/object dump и т. д.
     
  6. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.131
    Симпатии:
    1.250
    Адрес:
    там-сям
    это я и называл гемор )

    выгода multi_query только во времени пересылки команд — это не самые большие затраты. отдельные команды через точку-с-запятой отстаются отдельными командами: для каждой строится новый план выполнения. т.е. сам сервер бд такую посылку быстрее отдельных команд не обработает!

    заметный выигрыш можно было бы получить создавая одну длинную команду вида
    Код (PHP):
    1. INSERT INTO xx(...) VALUES (...), (...), (...), ...
    или через подготовленные запросы (только не-эмулированные, а реальные серверные). и не нужен никакой multi_query!

    Добавлено спустя 3 минуты 2 секунды:
    еще я заметил, что ты используешь INSERT IGNORE. т.е. ты не уверен в безошибочности данных и никак не контролируешь результат. это допустимо в твоей задаче?

    Добавлено спустя 12 минут 21 секунду:
    вот этот момент особенно настораживает. автоинкремент и mysqli::insert_id() были бы правильным решением!!! чужой id из параллельного процесса ты никак не получишь! так что все эти напряги зря изначально.
     
  7. tipati

    tipati Новичок

    С нами с:
    14 окт 2015
    Сообщения:
    15
    Симпатии:
    0
    Не пойдет. Автоинкремент идет по primary id, а crossid это дополнительный, по которому автоинкремент не сделаешь.
    А слежение, не превысила ли длина команды допустимую - не гемор?
    Не скажи. Разница в моем случае - в десятки раз.
     
  8. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.131
    Симпатии:
    1.250
    Адрес:
    там-сям
    хозяин барин. ты продолжаешь заблуждаться.

    в mysql нет ограничения на длину запроса. есть лимит размера посылки. и ты со своим множеством insert-ов с альтернативным синтаксисом исчерпаешь его ещё быстрее.

    алсо, не зная структуры не могу утверждать, но предполагаю, что можно было выбрать правильный primary key. сейчас у тебя два уникальных ключа, это уже нарушение нормальной формы. насколько оправданно, хз. ты конечно скажешь, что так надо и конец )))

    ты сравнивал время выполнения множества insert (через multi_query) с одним длинным insert? не думаю.

    Добавлено спустя 9 минут 25 секунд:
    смеху ради сравнил два варианта вставки 10 строк:
    Код (PHP):
    1. INSERT INTO table SET fil1="234", fil2="324";
    2. INSERT INTO table SET fil1="234", fil2="324";
    3. INSERT INTO table SET fil1="234", fil2="324";
    4. INSERT INTO table SET fil1="234", fil2="324";
    5. INSERT INTO table SET fil1="234", fil2="324";
    6. INSERT INTO table SET fil1="234", fil2="324";
    7. INSERT INTO table SET fil1="234", fil2="324";
    8. INSERT INTO table SET fil1="234", fil2="324";
    9. INSERT INTO table SET fil1="234", fil2="324";
    10. INSERT INTO table SET fil1="234", fil2="324";
    и
    Код (PHP):
    1. INSERT INTO table(fil1, fil2) VALUES
    2. ("234", "324"),
    3. ("234", "324"),
    4. ("234", "324"),
    5. ("234", "324"),
    6. ("234", "324"),
    7. ("234", "324"),
    8. ("234", "324"),
    9. ("234", "324"),
    10. ("234", "324"),
    11. ("234", "324");
    Длины: 470 против 197 ))) Ну про скорость уже всё сказано до нас:
    http://dev.mysql.com/doc/refman/5.7/en/insert-speed.html

    Так что, как минимум, рассмотри альтернативы.
     
  9. tipati

    tipati Новичок

    С нами с:
    14 окт 2015
    Сообщения:
    15
    Симпатии:
    0
    А зачем... Все работает.
     
  10. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.131
    Симпатии:
    1.250
    Адрес:
    там-сям