3 место МногоПоток. Исключаем параллельное использование данных. (MySQL)

AleXPrischepA

Client
Регистрация
06.05.2015
Сообщения
209
Благодарностей
97
Баллы
28
Доброго времени форумчане, коллеги и просто прохожие.... ;-)

Все мы, рано или поздно приходим к многопоточным шаблонам. А с многопотоком обязательно приходит вопрос "КААААК???" организовать ЗАПРЕТ на параллельное использование данных?! Ибо результаты "такого многопотока" никому не нужны...

photo_2024-02-24_15-16-44.jpg

Предлагаю вашему вниманию своё видение/решение этого вопроса. А вижу и понимаю я это так: если есть 2 "работника" и более, ТО уже нужен старший / управляющий, по другому практически невозможно.

И так есть 2 шаблона болванки: Manager.zp и Worker.zp (далее М и W соответственно), логика работы:
1) W генерирует уник. id_key и сохраняет его в таблицу tb_key нашей БД, с пометкой request. Также в БД сохраняется текущее время в unix формате (время требуется для контроля за ключами).
2) М видя ключ с пометкой request, берёт его и помечает свободные данные со статусом ready, в рабочих таблица tb_accounts и tb_proxy данным ключом + присваивает статус work / выдаёт работу W, и сам id_key помечается как work.
3) W получив "задание" - исполняет его, после чего помечает свой id_key как stop.
4) М видя id_key с пометкой stop, меняет статус данных на chill, а id_key удаляется из таблицы.
ключи.JPG
д32.JPG
д1.JPG

!!! В таблице tb_key, данные столбца id_key помечены как PRIMARY KEY, это означает что даже если будет сгенерирован НЕ уникальный id_key, при попытке его сохранить в БД, сама база данных не позволит этого сделать.

Кроме раздачи заданий Manager.zp:
1) Выдаёт задачи с некоторой задержкой по времени. (полезно для "нагруженных" шаблонов, дабы равномерно распределить пиковые нагрузки, в результате одновременного выполнения которых, можно "положить" всю работу)
2) Удаляет ключи из таблицы tb_key которые небыли удаленны из-за какого либо сбоя.
3) Освобождает данные в таблицах tb_accounts и tb_proxy которые заняты длительное время (в результате каких либо ошибок).
4) Следит чтоб данные "ОСТЫЛИ" некоторое время, после использования.
5) Обеспечивает равномерное использование всех данных, в работу идут первыми данные которые дольше простаивают.

Все тайминги можно регулировать прямо на лету, так как они хранятся в таблице tb_config и подтягиваются по необходимости.

!!! ОБРАТИТЕ ВНИМАНИЕ !!! При задании временных промежутков/таймингов, на соответствующий ему комментарий. Если временные промежутки буду заданы НЕ верно = параллельное использование данных.

Почему именно так?! Потому что после сброса блокировки данных, данные помечаются статусом "ready" (готовы к использованию).
оооо.JPG
Для работы шаблону необходимо:
1) База данных (у меня установлен Open Server 5.3.7, файл импорта БД приложу)
2) Для подключения к БД во входных настройках шаблона указать: имя БД, IP сервера, имя и пароль пользователя.
3) Дописать шаблоны под себя + заполнить таблицы своими данными.
4.JPG

Для удобной работы с БД можно использовать HeidiSQL (установочный файл в архиве).



 
Номер конкурса шаблонов
Двенадцатый конкурс шаблонов

Вложения

Для запуска проектов требуется программа ZennoPoster или ZennoDroid.
Это основное приложение, предназначенное для выполнения автоматизированных шаблонов действий (ботов).
Подробнее...

Для того чтобы запустить шаблон, откройте нужную программу. Нажмите кнопку «Добавить», и выберите файл проекта, который хотите запустить.
Подробнее о том, где и как выполняется проект.

Последнее редактирование модератором:

ShikoFess

Client
Регистрация
21.12.2017
Сообщения
146
Благодарностей
115
Баллы
43
А почему бы просто не сделать поле status, присвоить ему по умолчанию 0. Брать только WHERE status=0 и присваивать забранному аккаунту status = 1. Ты никак не возмешь этот же аккаунт в многопотоке
 
  • Спасибо
Реакции: DeepFarm, brun0 и Ribas

Ахилес

Client
Read only
Регистрация
11.11.2020
Сообщения
956
Благодарностей
392
Баллы
63
А почему бы просто не сделать поле status, присвоить ему по умолчанию 0. Брать только WHERE status=0 и присваивать забранному аккаунту status = 1. Ты никак не возмешь этот же аккаунт в многопотоке
в легкую возьмешь одну и туже строку в работу, да еще и не один раз :bk:
странное конечно утверждение... но ладно, каждый нарабатывает опыт своим путем ;-)
 
  • Спасибо
Реакции: Jeronimo и AleXPrischepA

AleXPrischepA

Client
Регистрация
06.05.2015
Сообщения
209
Благодарностей
97
Баллы
28
А почему бы просто не сделать поле status, присвоить ему по умолчанию 0. Брать только WHERE status=0 и присваивать забранному аккаунту status = 1. Ты никак не возмешь этот же аккаунт в многопотоке
Какое время пройдет между получением данных со status=0 и последующим присвоением этим данным status = 1 ?
При +/- параллельном старте потоков, как с добрым утром получишь параллельное использование.
 
  • Спасибо
Реакции: Jeronimo

Jeronimo

Client
Регистрация
07.01.2014
Сообщения
236
Благодарностей
205
Баллы
43
AleXPrischepA намудрили вы конечно)

Задача решается гораздо проще с помощью блокировки таблицы через запрос, и статус аккаунта в таблице БД.
В таблице с аккаунтами должен быть столбец "Статус".
Обычно он принимает следующие значения: free, busy, ban.

Алгоритм работы:
1. Рабочий поток отправляет SQL запрос на получение username аккаунта (с которым он будет в дальнейшем работать) по определённым критериям (например, только со статусом free). Открывается сессия, выполняет блокировка таблицы (чтобы другие потоки не могли к ней обращаться) по заданным критериям в переменную заносится username аккаунта. Далее в этой же сессии происходит смена статуса аккаунта (username которого получили ранее) на busy. Далее разблокировка таблицы и закрытие сессии. Все эти действия выполняются в одном c# кубике за доли секунды, что позволяет с высокой скоростью обращаться за нужными данными, избегая работы одних и тех же аккаунтов в разных потоках.
2. Выполняется SQL запрос на получение нужных данных по полученному username (куки, пароль и т.п.). Далее уже текущий поток начинает работу с аккаунтом. По завершении работы отправляет SQL запрос на смену статуса аккаунта на free.

Работаю по такому алгоритму достаточно давно. Нагрузка на БД большая, запросов очень много, но при этом ни один поток не работает одновременно с одним и тем же аккаунтом.
 

AleXPrischepA

Client
Регистрация
06.05.2015
Сообщения
209
Благодарностей
97
Баллы
28
AleXPrischepA намудрили вы конечно)
Согласен... :-), про блокировку и c# слышал, знаю... Но нужно знание c#, коих у меня маловато..., а стандартными кубиками блокировки нет, в рамках одного подключения к БД, два и более запроса не выполнить.
 

Ахилес

Client
Read only
Регистрация
11.11.2020
Сообщения
956
Благодарностей
392
Баллы
63
Jeronimo про

Согласен... :-), про блокировку и c# слышал, знаю... Но нужно знание c#, коих у меня маловато..., а стандартными кубиками блокировки нет, в рамках одного подключения к БД, два и более запроса не выполнить.
не крутится весь мир вокруг C#....
изучите как работает механизм транзакций (ну хотя бы для начала https://blog-kopilka.ru/blog/transaction) и сразу станет ясно что речь идет про одну атомарную транзакцию, которую можно и нужно выполнять за один раз... ну и естественно стандартный кубик легко с этим справится... ведь он отправляет только текст... а вот смысловой текст уже зависит от уровня знания SQL языка. :bk:
 
  • Спасибо
Реакции: indigo666

ShikoFess

Client
Регистрация
21.12.2017
Сообщения
146
Благодарностей
115
Баллы
43
Согласен... :-), про блокировку и c# слышал, знаю... Но нужно знание c#, коих у меня маловато..., а стандартными кубиками блокировки нет, в рамках одного подключения к БД, два и более запроса не выполнить.
Так я тебе тоже самое и написал, ок про блокировку не указал в сообщении. Мудреный шаб получается слишком. https://zenno.club/discussion/threads/zagotovka-dlja-raboty-s-bd-mysql-v-kubike-c.64766/ Вот тут все есть просто 1 кубиком C# решается с локом бд
 
  • Спасибо
Реакции: webposter

Jeronimo

Client
Регистрация
07.01.2014
Сообщения
236
Благодарностей
205
Баллы
43
Согласен... :-), про блокировку и c# слышал, знаю... Но нужно знание c#, коих у меня маловато..., а стандартными кубиками блокировки нет, в рамках одного подключения к БД, два и более запроса не выполнить.
Честно говоря, и я не знаток c#. Лет 5 назад мне за 20$ написали кусочек кода, который юзаю по сей день.
Готов поделиться им с комьюнити бесплатно:

C#:
project.Variables["username"].Value = null; //не обязательно!!! Только для обнуления переменной, если не перезапускаете шаблон полностью

Sql sqlConn = new Sql(project); //Подключаемся к БД
var cmd = sqlConn.Cmd();
cmd.Connection.Open(); //открываем сессию
cmd.DbLock("" + project.Variables["bd_tableName"].Value + " WRITE"); //Блокируем таблицу

var reader = cmd.ExReader(String.Format("SELECT username FROM "+ project.Variables["bd_tableName"].Value +" WHERE STATUS='free' LIMIT 1;")); //Получаем username аккаунта, подходящего по условиям запроса.

if(reader.HasRows) //проверяем имеются ли данные в ответе. Если имеются, то переходим к считыванию
{
    while (reader.Read()) // построчно в цикле (while) считываем данные. Получим максимум 1 строку, т.к. в запросе стоит LIMIT 1
    {
        string dbUsername = reader["username"].ToString(); //получаем задание. В нашем случае это ссылка для парсинга.
        project.Variables["username"].Value = dbUsername; //сохраняем link в переменную зеннопостера
    }
}
reader.Close();

//Обновление статуса. Выполняется только если было получено задание (если id не пустое!)
if(!string.IsNullOrEmpty(project.Variables["username"].Value))
{
    //после получения задания необходимо поменять статус задания, чтобы другие потоки больше его не видели после разблокировки таблицы
    cmd.ExNonQ(String.Format("UPDATE "+ project.Variables["bd_tableName"].Value +" SET status='busy' WHERE username= '" + project.Variables["username"].Value + "';"));
}
    
cmd.DbUnLock(); //разблокировка всех таблиц
cmd.Connection.Close(); //закрываем сессию
 

Ахилес

Client
Read only
Регистрация
11.11.2020
Сообщения
956
Благодарностей
392
Баллы
63
Честно говоря, и я не знаток c#. Лет 5 назад мне за 20$ написали кусочек кода, который юзаю по сей день.
Готов поделиться им с комьюнити бесплатно:

C#:
project.Variables["username"].Value = null; //не обязательно!!! Только для обнуления переменной, если не перезапускаете шаблон полностью

Sql sqlConn = new Sql(project); //Подключаемся к БД
var cmd = sqlConn.Cmd();
cmd.Connection.Open(); //открываем сессию
cmd.DbLock("" + project.Variables["bd_tableName"].Value + " WRITE"); //Блокируем таблицу

var reader = cmd.ExReader(String.Format("SELECT username FROM "+ project.Variables["bd_tableName"].Value +" WHERE STATUS='free' LIMIT 1;")); //Получаем username аккаунта, подходящего по условиям запроса.

if(reader.HasRows) //проверяем имеются ли данные в ответе. Если имеются, то переходим к считыванию
{
    while (reader.Read()) // построчно в цикле (while) считываем данные. Получим максимум 1 строку, т.к. в запросе стоит LIMIT 1
    {
        string dbUsername = reader["username"].ToString(); //получаем задание. В нашем случае это ссылка для парсинга.
        project.Variables["username"].Value = dbUsername; //сохраняем link в переменную зеннопостера
    }
}
reader.Close();

//Обновление статуса. Выполняется только если было получено задание (если id не пустое!)
if(!string.IsNullOrEmpty(project.Variables["username"].Value))
{
    //после получения задания необходимо поменять статус задания, чтобы другие потоки больше его не видели после разблокировки таблицы
    cmd.ExNonQ(String.Format("UPDATE "+ project.Variables["bd_tableName"].Value +" SET status='busy' WHERE username= '" + project.Variables["username"].Value + "';"));
}
   
cmd.DbUnLock(); //разблокировка всех таблиц
cmd.Connection.Close(); //закрываем сессию
Sql sqlConn = new Sql(project); //Подключаемся к БД

а толку от этого кусочка кода, когда нет кастомного класса Sql из общего кода ? :bk:
 
  • Спасибо
Реакции: eagleowl и seodamage

AleXPrischepA

Client
Регистрация
06.05.2015
Сообщения
209
Благодарностей
97
Баллы
28
изучите как работает механизм транзакций (ну хотя бы для начала https://blog-kopilka.ru/blog/transaction) и сразу станет ясно что речь идет про одну атомарную транзакцию, которую можно и нужно выполнять за один раз... ну и естественно стандартный кубик легко с этим справится...
Благодарю за наводку...
 

ShikoFess

Client
Регистрация
21.12.2017
Сообщения
146
Благодарностей
115
Баллы
43
Работают со множествов шабов в многопотоке по примерам из ссылке выше. Ни разу не было замечено чтобы разные потоки брали один аккаунт. Все работает как часы
 

WebBot

Client
Регистрация
04.04.2015
Сообщения
1 763
Благодарностей
1 391
Баллы
113
Все это делается в два запроса, без всяких блокировок таблиц и прочих танцев с бубном

первый запрос меняет статус строки .. при UPDATE происходит автоматическая блокировка на уровне строк таблицы (не всей таблицы, а только строки которая апдейтиться), поэтому 1 и ту же строку не могут апдейтить одновременно несколько потоков, а после апдейта и разблокировки строки она уже не будет подходить по параметрам ( status уже будет не 0 )

UPDATE tbl SET id=LAST_INSERT_ID(id), status=1 WHERE status=0 LIMIT 1

следующей функцией получаем id той строки статус которой был изменен

SELECT LAST_INSERT_ID()

такое работает если нужно что бы каждый поток брал 1 строку ... и UPDATE и SELECT должны выполняться в одной сессии
 

Ахилес

Client
Read only
Регистрация
11.11.2020
Сообщения
956
Благодарностей
392
Баллы
63
Все это делается в два запроса, без всяких блокировок таблиц и прочих танцев с бубном

первый запрос меняет статус строки .. при UPDATE происходит автоматическая блокировка на уровне строк таблицы (не всей таблицы, а только строки которая апдейтиться), поэтому 1 и ту же строку не могут апдейтить одновременно несколько потоков, а после апдейта и разблокировки строки она уже не будет подходить по параметрам ( status уже будет не 0 )

UPDATE tbl SET id=LAST_INSERT_ID(id), status=1 WHERE status=0 LIMIT 1

следующей функцией получаем id той строки статус которой был изменен

SELECT LAST_INSERT_ID()

такое работает если нужно что бы каждый поток брал 1 строку ... и UPDATE и SELECT должны выполняться в одной сессии
а что бы не зависеть от сессий.... потому что это та еще негарантированная хрень.... лучше использовать транзакции и не парить себе мозг

START TRANSACTION;

UPDATE tbl
SET id = LAST_INSERT_ID(id), status = 1
WHERE status = 0
LIMIT 1;

SELECT LAST_INSERT_ID();

COMMIT;
 

DeepFarm

Seller
Регистрация
11.05.2023
Сообщения
117
Благодарностей
24
Баллы
18
string zapros = @"LOCK TABLES webhook WRITE;
SELECT data FROM webhook WHERE mode=""odobr"" LIMIT 1;
DELETE FROM webhook WHERE mode=""odobr"" LIMIT 1;
UNLOCK TABLES;";

project.Variables["responce"].Value = ZennoPoster.Db.ExecuteQuery(zapros, null, ZennoLab.InterfacesLibrary.Enums.Db.DbProvider.MySqlClient, "server="+project.Variables["hostMySQL"].Value+";user id="+project.Variables["loginMySQL"].Value+";password="+project.Variables["passMySQL"].Value+";database="+project.Variables["baseMySQL"].Value+";port="+project.Variables["portMySQL"].Value+"", " ", "");

Вот запрос в c# без колдовства... берет одну строку - одним потоком
 

Petr_G

Client
Регистрация
20.10.2017
Сообщения
96
Благодарностей
53
Баллы
18

marushin

Client
Регистрация
12.01.2015
Сообщения
193
Благодарностей
60
Баллы
28
И open server совсем не нужен! Mysql прекрасно ставится на windows.
и намертво приколачивается к системе, быкапы только через сам мускул, нельзя скопировать просто директорию. с open server, копируй - переноси куда хочешь

По теме, в MySQL уже всё придумано, нет смысла что то изобретать. Как выше ответили, транзакции решают вопрос многопотока, собственно они для этого и нужны.
 
Последнее редактирование:

Wide

Client
Регистрация
04.02.2013
Сообщения
945
Благодарностей
257
Баллы
63

marushin

Client
Регистрация
12.01.2015
Сообщения
193
Благодарностей
60
Баллы
28

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