Почему MySQL ошибается?

Castaneda

Client
Регистрация
24.05.2019
Сообщения
872
Благодарностей
299
Баллы
63
Каждый поток шаблона берёт аккаунты по порядку и пытается INSERTить в базу online.
Если duplicate, берёт следующий акк и так пока не найдёт свободный.
В середине шаблона есть проверка а не используется ли акк случайно другим потоком и если да, он самоубивается и пишет в лог предсмертную записку.

Таблица online ведётся в виде:
акк | PID | дата последней активности потока (раз в 7-10 сек отстукивает)

Возникает странный баг:
довольно часто потоки ловят дубли. То есть как-то запись успешно INSERTится. Зенка ошибается и в цикле ZennoPoster.Db.ExecuteQuery(String.Format("INSERT INTO... возвращает положительное значение в цикле.

Не понимаю это глюк MySQL или ZP..
Хелп
 

Sanekk

Client
Регистрация
24.06.2016
Сообщения
999
Благодарностей
390
Баллы
63
Каждый поток шаблона берёт аккаунты по порядку и пытается INSERTить в базу online.
Если duplicate, берёт следующий акк и так пока не найдёт свободный.
В середине шаблона есть проверка а не используется ли акк случайно другим потоком и если да, он самоубивается и пишет в лог предсмертную записку.

Таблица online ведётся в виде:
акк | PID | дата последней активности потока (раз в 7-10 сек отстукивает)

Возникает странный баг:
довольно часто потоки ловят дубли. То есть как-то запись успешно INSERTится. Зенка ошибается и в цикле ZennoPoster.Db.ExecuteQuery(String.Format("INSERT INTO... возвращает положительное значение в цикле.

Не понимаю это глюк MySQL или ZP..
Хелп
Кода нет, а гадать по тексту вообще не айс...
Локи есть в коде?
 
  • Спасибо
Реакции: Castaneda

Phoenix78

Client
Read only
Регистрация
06.11.2018
Сообщения
11 790
Благодарностей
5 720
Баллы
113
все конкурирующие действия в многопотоке должны лочится. 100500 раз уже на форуме об этом написано.
 
  • Спасибо
Реакции: Castaneda

Castaneda

Client
Регистрация
24.05.2019
Сообщения
872
Благодарностей
299
Баллы
63

Castaneda

Client
Регистрация
24.05.2019
Сообщения
872
Благодарностей
299
Баллы
63
Кода нет, а гадать по тексту вообще не айс...
Локи есть в коде?
C#:
while (project.Lists["acclst"].Count > 0)
{
    project.Variables["acc"].Value = project.Lists["acclst"].ElementAt(0);
    project.Lists["acclst"].RemoveAt(0);
    try
    {
        ZennoPoster.Db.ExecuteQuery(String.Format("INSERT INTO online (acc,last_online,pid,state) values ('{0}','{1}','{2}','{3}')",project.Variables["acc"].Value,DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),project.Variables["pid"].Value,project.Variables["last_action"].Value), null, ZennoLab.InterfacesLibrary.Enums.Db.DbProvider.MySqlClient, "server=xxxxx;user id=xxxxx;password=xxxxx;persistsecurityinfo=True;database=xxxxx", " "," ", true);
        project.SendInfoToLog("Нашли свободный акк "+project.Variables["acc"].Value);
        project.SendInfoToLog(project.Variables["acc"].Value+" is now ONLINE!",true);
        return project.Variables["acc"].Value;
    } catch{}
}

return null;
 

Sanekk

Client
Регистрация
24.06.2016
Сообщения
999
Благодарностей
390
Баллы
63
Возникает странный баг:
довольно часто потоки ловят дубли.
И почему баг странный?
У вас код берëт из списка project.Variables["acc"].Value и если в многопотоке, то несколько потоков могут взять одну и ту же строку, вот вам и дубли.
Лочте обращения к списку.

C#:
while (project.Lists["acclst"].Count > 0)
{
    project.Variables["acc"].Value = project.Lists["acclst"].ElementAt(0);
    project.Lists["acclst"].RemoveAt(0);
    try
    {
        ZennoPoster.Db.ExecuteQuery(String.Format("INSERT INTO online (acc,last_online,pid,state) values ('{0}','{1}','{2}','{3}')",project.Variables["acc"].Value,DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),project.Variables["pid"].Value,project.Variables["last_action"].Value), null, ZennoLab.InterfacesLibrary.Enums.Db.DbProvider.MySqlClient, "server=xxxxx;user id=xxxxx;password=xxxxx;persistsecurityinfo=True;database=xxxxx", " "," ", true);
        project.SendInfoToLog("Нашли свободный акк "+project.Variables["acc"].Value);
        project.SendInfoToLog(project.Variables["acc"].Value+" is now ONLINE!",true);
        return project.Variables["acc"].Value;
    } catch{}
}

return null;
 

Castaneda

Client
Регистрация
24.05.2019
Сообщения
872
Благодарностей
299
Баллы
63
И почему баг странный?
У вас код берëт из списка project.Variables["acc"].Value и если в многопотоке, то несколько потоков могут взять одну и ту же строку, вот вам и дубли.
Лочте обращения к списку.
пусть хоть 10 потоков возьмут один и тот же акк
только 1 пролезет в базу с INSERT-ом
Штатно всё работает как и задумывалось.
Но после 30-40 минут работы замечаю записки потоков-суицидников(
не понимаю откуда
 

Sanekk

Client
Регистрация
24.06.2016
Сообщения
999
Благодарностей
390
Баллы
63
пусть хоть 10 потоков возьмут один и тот же акк
только 1 пролезет в базу с INSERT-ом
Штатно всё работает как и задумывалось.
Но после 30-40 минут работы замечаю записки потоков-суицидников(
не понимаю откуда
Insert должен записать каждый акк с новой строки
 

Castaneda

Client
Регистрация
24.05.2019
Сообщения
872
Благодарностей
299
Баллы
63

Phoenix78

Client
Read only
Регистрация
06.11.2018
Сообщения
11 790
Благодарностей
5 720
Баллы
113
с чего был сделан вывод что несколько разных потоков не смогут одновременно послать INSERT в базу ?
как бы база это не локальные переменные, тут как бы есть транзакции.... и они не мгновенные.
лочить надо либо в базе, либо в коде.
 
  • Спасибо
Реакции: Castaneda

Castaneda

Client
Регистрация
24.05.2019
Сообщения
872
Благодарностей
299
Баллы
63
с чего был сделан вывод что несколько разных потоков не смогут одновременно послать INSERT в базу ?
как бы база это не локальные переменные, тут как бы есть транзакции.... и они не мгновенные.
лочить надо либо в базе, либо в коде.
Как правильно здесь локи вписать?


C#:
while (project.Lists["acclst"].Count > 0)
{
    project.Variables["acc"].Value = project.Lists["acclst"].ElementAt(0);
    project.Lists["acclst"].RemoveAt(0);
    try
    {
        ZennoPoster.Db.ExecuteQuery(String.Format("INSERT INTO online (acc,last_online,pid,state) values ('{0}','{1}','{2}','{3}')",project.Variables["acc"].Value,DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),project.Variables["pid"].Value,project.Variables["last_action"].Value), null, ZennoLab.InterfacesLibrary.Enums.Db.DbProvider.MySqlClient, "server=xxxxx;user id=xxxxx;password=xxxxx;persistsecurityinfo=True;database=xxxxx", " "," ", true);
        project.SendInfoToLog("Нашли свободный акк "+project.Variables["acc"].Value);
        project.SendInfoToLog(project.Variables["acc"].Value+" is now ONLINE!",true);
        return project.Variables["acc"].Value;
    } catch{}
}

return null;
 

Sanekk

Client
Регистрация
24.06.2016
Сообщения
999
Благодарностей
390
Баллы
63
О том что несколько одинаковых акков либо могут записатся в несколько строк, либо несколько одновременных одинаковых запросов в базу вот от этого траблы
 

Castaneda

Client
Регистрация
24.05.2019
Сообщения
872
Благодарностей
299
Баллы
63
О том что несколько одинаковых акков либо могут записатся в несколько строк, либо несколько одновременных одинаковых запросов в базу вот от этого траблы
в базе проверка на уникальность столбца акков само собой стоит
никак не запишется в несколько
 

Sanekk

Client
Регистрация
24.06.2016
Сообщения
999
Благодарностей
390
Баллы
63
Как правильно здесь локи вписать?


C#:
while (project.Lists["acclst"].Count > 0)
{
    project.Variables["acc"].Value = project.Lists["acclst"].ElementAt(0);
    project.Lists["acclst"].RemoveAt(0);
    try
    {
        ZennoPoster.Db.ExecuteQuery(String.Format("INSERT INTO online (acc,last_online,pid,state) values ('{0}','{1}','{2}','{3}')",project.Variables["acc"].Value,DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),project.Variables["pid"].Value,project.Variables["last_action"].Value), null, ZennoLab.InterfacesLibrary.Enums.Db.DbProvider.MySqlClient, "server=xxxxx;user id=xxxxx;password=xxxxx;persistsecurityinfo=True;database=xxxxx", " "," ", true);
        project.SendInfoToLog("Нашли свободный акк "+project.Variables["acc"].Value);
        project.SendInfoToLog(project.Variables["acc"].Value+" is now ONLINE!",true);
        return project.Variables["acc"].Value;
    } catch{}
}

return null;
Строку 2 и 3 оберни в sync object. listsyncer загугли как правильно пишется, я с тел. пишу не удобно
 

Castaneda

Client
Регистрация
24.05.2019
Сообщения
872
Благодарностей
299
Баллы
63
Строку 2 и 3 оберни в sync object. listsyncer загугли как правильно пишется, я с тел. пишу не удобно
там нет смысла, т.к. список акков запрашивается из таблицы со всеми акками.
Каждый поток получает полный список и по порядку пытается их воткнуть.

Нужны локи на INSERT в базу на сколько я понял
 

porileenvej

Client
Регистрация
09.05.2020
Сообщения
99
Благодарностей
131
Баллы
33
Лочить в любом случае нужно, чтобы потоки не брали один и тот же акк несколько раз.
Если у тебя есть UNIQUE индекс в таблице, то можешь использовать INSERT IGNORE
Попробуй следующий код
C#:
while (project.Lists["acclst"].Count > 0)
{
    lock (SyncObjects.ListSyncer)
    {
        project.Variables["acc"].Value = project.Lists["acclst"].ElementAt(0);
        project.Lists["acclst"].RemoveAt(0);
    }
    try
    {
        ZennoPoster.Db.ExecuteQuery(String.Format("INSERT IGNORE INTO online (acc,last_online,pid,state) values ('{0}','{1}','{2}','{3}')",project.Variables["acc"].Value,DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),project.Variables["pid"].Value,project.Variables["last_action"].Value), null, ZennoLab.InterfacesLibrary.Enums.Db.DbProvider.MySqlClient, "server=xxxxx;user id=xxxxx;password=xxxxx;persistsecurityinfo=True;database=xxxxx", " "," ", true);
        project.SendInfoToLog("Нашли свободный акк "+project.Variables["acc"].Value);
        project.SendInfoToLog(project.Variables["acc"].Value+" is now ONLINE!",true);
        return project.Variables["acc"].Value;
    } catch{}
}

return null;
 

uuw

Client
Регистрация
04.06.2020
Сообщения
146
Благодарностей
54
Баллы
28
в базе проверка на уникальность столбца акков само собой стоит
никак не запишется в несколько
Если unique индекс есть по столбцу - то будет вылет по ошибке при попытке добавить запись с дублем, все остальные самодельные проверки на уникадьность от лукавого, работать не будут.
 
  • Спасибо
Реакции: Castaneda

Castaneda

Client
Регистрация
24.05.2019
Сообщения
872
Благодарностей
299
Баллы
63
Лочить в любом случае нужно, чтобы потоки не брали один и тот же акк несколько раз.
Если у тебя есть UNIQUE индекс в таблице, то можешь использовать INSERT IGNORE
Попробуй следующий код
C#:
while (project.Lists["acclst"].Count > 0)
{
    lock (SyncObjects.ListSyncer)
    {
        project.Variables["acc"].Value = project.Lists["acclst"].ElementAt(0);
        project.Lists["acclst"].RemoveAt(0);
    }
    try
    {
        ZennoPoster.Db.ExecuteQuery(String.Format("INSERT IGNORE INTO online (acc,last_online,pid,state) values ('{0}','{1}','{2}','{3}')",project.Variables["acc"].Value,DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),project.Variables["pid"].Value,project.Variables["last_action"].Value), null, ZennoLab.InterfacesLibrary.Enums.Db.DbProvider.MySqlClient, "server=xxxxx;user id=xxxxx;password=xxxxx;persistsecurityinfo=True;database=xxxxx", " "," ", true);
        project.SendInfoToLog("Нашли свободный акк "+project.Variables["acc"].Value);
        project.SendInfoToLog(project.Variables["acc"].Value+" is now ONLINE!",true);
        return project.Variables["acc"].Value;
    } catch{}
}

return null;
не не не
это не так у меня работает

1. SELECT acc FROM accs WHERE state='active'
забираем из базы все акки и закидываем их в акклист (не привязан к файлу. т.е. просто временный для потока)

2. В цикле берём из списка акклист один акк и пытаемся его INSERT INTO online в try блоке
если в таблице не было такого акка, то он успешно выполнится и сделает return и пойдет дальше,
а если пройдет все акки и ни одного не воткнёт, то выйдет из кубика по return null и подождёт 30 сек вдруг что появится.

Вроде бы Phoenix78 правильно говорит. Два потока делают INSERT и MySQL почему-то пропускает оба.
Ну либо он неправ и это ZP делает INSERT, думает, что он успешно выполнился и идёт дальше, хотя на самом деле это не так.
 

Castaneda

Client
Регистрация
24.05.2019
Сообщения
872
Благодарностей
299
Баллы
63
Если unique индекс есть по столбцу - то будет вылет по ошибке при попытке добавить запись с дублем, все остальные самодельные проверки на уникадьность от лукавого, работать не будут.
вооооот я об этом и говорю.
Но в реальности получается что-то не так.

Ну вот прям сейчас дубли пошли:
81679

строка PID для сравнения дубль или нет формируется один раз при старте потока:
Environment.MachineName + " > " + instance.FormTitle
 

Phoenix78

Client
Read only
Регистрация
06.11.2018
Сообщения
11 790
Благодарностей
5 720
Баллы
113
блин, да за залоч все в одном кубе и не будет проблем.
прямо все пихай в лок, получение аккаунтов из базы и весь код выбора аккаунта с инсертом в базу. и не будет проблем.
 

Sanekk

Client
Регистрация
24.06.2016
Сообщения
999
Благодарностей
390
Баллы
63
вооооот я об этом и говорю.
Но в реальности получается что-то не так.

Ну вот прям сейчас дубли пошли:
Посмотреть вложение 81679

строка PID для сравнения дубль или нет формируется один раз при старте потока:
Environment.MachineName + " > " + instance.FormTitle
Да если залочиш список с акками, то и не будет два одинаковых обращения
 

Sanekk

Client
Регистрация
24.06.2016
Сообщения
999
Благодарностей
390
Баллы
63
блин, да за залоч все в одном кубе и не будет проблем.
прямо все пихай в лок, получение аккаунтов из базы и весь код выбора аккаунта с инсертом в базу. и не будет проблем.
Лочить цикл так себе, если список большой и потоков много, может сказаться на производительности
 

Phoenix78

Client
Read only
Регистрация
06.11.2018
Сообщения
11 790
Благодарностей
5 720
Баллы
113
Да если залочиш список с акками, то и не будет два одинаковых обращения
да он уже несколько раз написал что у него список НЕ ОБЩИЙ. в памяти временный список, куда он складывает выборку из базы.

вообще очень странный метод работать с базой таким образом. лучше уж посидеть, подумать над составлением правильного запроса из 2-х таблиц, что бы в выборке был только один правильный результат. без всяких манипуляций с выборкой в памяти и попытках найти тут же самый аккаунт.
по хорошему тут лок>выборка аккаунта по правильному запросу> тут же размещение его в нужной таблице>закрытие лока.

а вообще у себя я вообще не перемещаю аккаунты. у меня в таблице с аккаунтами есть поле статуса работы с ними. у меня такая схема выборки аккаунта
лок>выборка аккаунта по нужным условиям > тут же меняю у него поле статуса по его UNIQ полю>закрытие лока.
работает как часики.

ну конечно есть и размещение в других таблицах, но там я не лочу, так как размещение не конкурирующие.
 
  • Спасибо
Реакции: Sanekk

Castaneda

Client
Регистрация
24.05.2019
Сообщения
872
Благодарностей
299
Баллы
63
да он уже несколько раз написал что у него список НЕ ОБЩИЙ. в памяти временный список, куда он складывает выборку из базы.

вообще очень странный метод работать с базой таким образом. лучше уж посидеть, подумать над составлением правильного запроса из 2-х таблиц, что бы в выборке был только один правильный результат. без всяких манипуляций с выборкой в памяти и попытках найти тут же самый аккаунт.
по хорошему тут лок>выборка аккаунта по правильному запросу> тут же размещение его в нужной таблице>закрытие лока.

а вообще у себя я вообще не перемещаю аккаунты. у меня в таблице с аккаунтами есть поле статуса работы с ними. у меня такая схема выборки аккаунта
лок>выборка аккаунта по нужным условиям > тут же меняю у него поле статуса по его UNIQ полю>закрытие лока.
работает как часики.

ну конечно есть и размещение в других таблицах, но там я не лочу, так как размещение не конкурирующие.
мне так удобнее смотреть на картину онлайна в HEIDISQL
в таблице online только нужные поля

в общем почитал про локи. Сейчас добавил перед кодом, который выше писал LOCK TABLE online WRITE
и на выходах из него анлоки

тестирую...
 

gmr

Client
Регистрация
05.12.2014
Сообщения
55
Благодарностей
12
Баллы
8
запрос вероятно падает, но ты этого не видишь, выведи в лог блок "catch". правильное проектирование таблиц в БД позволит и масштабироваться безболезненно, и вопрос с занятостью аккаунтов решить
мне так удобнее смотреть на картину онлайна в HEIDISQL
в таблице online только нужные поля
напиши запрос с получением занятых аккаунтов и выведи интересующие колонки, получишь то же представление
 

Sanekk

Client
Регистрация
24.06.2016
Сообщения
999
Благодарностей
390
Баллы
63
да он уже несколько раз написал что у него список НЕ ОБЩИЙ. в памяти временный список, куда он складывает выборку из базы.
Чë то упустил про этот момент

а вообще у себя я вообще не перемещаю аккаунты. у меня в таблице с аккаунтами есть поле статуса работы с ними. у меня такая схема выборки аккаунта
лок>выборка аккаунта по нужным условиям > тут же меняю у него поле статуса по его UNIQ полю>закрытие лока.
работает как часики.
Так же делаю
лок>получаю акк>апдейт статус акка в этой же строке>закрываю лок
 

Phoenix78

Client
Read only
Регистрация
06.11.2018
Сообщения
11 790
Благодарностей
5 720
Баллы
113
мне так удобнее смотреть на картину онлайна в HEIDISQL
в таблице online только нужные поля
а разве в Мускуле нет View таблиц ?
даже в SQLite они есть.

81682

сделал нужный View, с нужными полями и выводи сколько угодно. хоть 100500 таблиц можно свести в 1 простое представление.
 

Castaneda

Client
Регистрация
24.05.2019
Сообщения
872
Благодарностей
299
Баллы
63
а разве в Мускуле нет View таблиц ?
даже в SQLite они есть.

Посмотреть вложение 81682

сделал нужный View, с нужными полями и выводи сколько угодно. хоть 100500 таблиц можно свести в 1 простое представление.
не знал об этом

в общем херня какая-то у меня получилась:

C#:
//вроде как зенке не понравились залоченные таблицы и она начала кубик по красной ветке выкидывать в ждущих потоках
//так что сделал попытки залочить в течение 30 сек пока не пройдет
//в статье по локам пишут, что если один лок уже есть, то остальные будут отклоняться

for (int i=0;i<30;i++)
{
    try
    {
        ZennoPoster.Db.ExecuteNonQuery(@"LOCK TABLE accs,online WRITE", null, ZennoLab.InterfacesLibrary.Enums.Db.DbProvider.MySqlClient, "server=192.168.0.150;user id=admin;password=pass;persistsecurityinfo=True;database=alien", true);
        break;
    }
    catch
    {
        project.SendWarningToLog("can not lock table");
        Thread.Sleep(1000);
    }
}

while (project.Lists["acclst"].Count > 0)
{
    project.Variables["acc"].Value = project.Lists["acclst"].ElementAt(0);
    project.Lists["acclst"].RemoveAt(0);
    try
    {
        ZennoPoster.Db.ExecuteQuery(String.Format("INSERT INTO online (acc,last_online,pid,state) values ('{0}','{1}','{2}','{3}')",project.Variables["acc"].Value,DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),project.Variables["pid"].Value,project.Variables["last_action"].Value), null, ZennoLab.InterfacesLibrary.Enums.Db.DbProvider.MySqlClient, "server=xxxxx;user id=xxxxx;password=xxxxx;persistsecurityinfo=True;database=xxxxx", " "," ", true);
        project.SendInfoToLog("Нашли свободный акк "+project.Variables["acc"].Value);
        project.SendInfoToLog(project.Variables["acc"].Value+" is now ONLINE!",true);
        ZennoPoster.Db.ExecuteNonQuery(@"UNLOCK TABLES", null, ZennoLab.InterfacesLibrary.Enums.Db.DbProvider.MySqlClient, "server=xxxxx;user id=xxxxx;password=xxxxx;persistsecurityinfo=True;database=xxxxx", true);
        return project.Variables["acc"].Value;
    } catch{}
}

ZennoPoster.Db.ExecuteNonQuery(@"UNLOCK TABLES", null, ZennoLab.InterfacesLibrary.Enums.Db.DbProvider.MySqlClient, "server=xxxxx;user id=xxxxx;password=xxxxx;persistsecurityinfo=True;database=xxxxx", true);

return null;
и пошло всё по одному месту
22:45:18 Выполнение действия Db Table 'accs' was not locked with LOCK TABLES

действие - кубик, который стоит еще до локов. (там где все акки запрашиваются из таблицы accs в список)
не понимаю при чем он здесь, но потоки вылетают с ошибкой на нём.
 
Последнее редактирование:

RoyalBank

Client
Регистрация
07.09.2015
Сообщения
557
Благодарностей
550
Баллы
93
  • Спасибо
Реакции: Sanekk

gmr

Client
Регистрация
05.12.2014
Сообщения
55
Благодарностей
12
Баллы
8
нет смысла в отдельной блокировке таблицы перед вставкой в MySQL
 

Кто просматривает тему: (Всего: 1, Пользователи: 0, Гости: 1)