START TRANSACTION с Python 3.6 mysql-connector-python не возвращает результатов - PullRequest
0 голосов
/ 06 июля 2018

Я использую Python 3.6, mysql-connector-python 8.0.11 и 8.0.11 MySQL Community Server - GPL. В рассматриваемой таблице используется механизм innoDB.

При использовании MySQL Workbench я могу ввести:

ИСПОЛЬЗОВАНИЕ теста; НАЧАТЬ СДЕЛКУ; ВЫБРАТЬ * ИЗ ЗАДАЧ ГДЕ task_status! = 1 ПРЕДЕЛ 1 ДЛЯ ОБНОВЛЕНИЯ;

И это обеспечивает 1 запись, как ожидалось:

Когда я использую скрипт, использующий python3 (с той же машины - тот же доступ и т. Д.):

  * SQL QRY: START TRANSACTION; SELECT * FROM test WHERE task_status != 1 LIMIT 1 FOR UPDATE;
  * SQL RES: No result set to fetch from.

Это отладочный вывод из моего скрипта. Если я изменю запрос на обычный SELECT, я получу вывод.

  * SQL QRY: SELECT * FROM test WHERE task_status != 1 LIMIT 1;
  * SQL RES: [(1, 0, 'TASK0001')]

Я знаю, что SELECT * это не тот путь, а просто попытка получить какой-то ответ.

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

  1. Выполните операцию выбора и строки, чтобы заблокировать задачу, чтобы запрос «ВЫБРАТЬ» других рабочих их не отображал,
  2. Установить статус задачи на «обрабатывается» и разблокировать запись.

Это мое первое занятие по запиранию, так что это новая земля. Я могу делать обычные запросы, заполнять таблицы и т. Д., Так что имею некоторый опыт, но не с блокировкой.

ТАБЛИЦА создания:

create table test
(
  id          int auto_increment
   primary key,
  task_status int         not null,
  task_ref    varchar(16) not null
);

Вопросы:

  1. Это правильное мышление? То есть Есть ли более питонский / MySQL способ сделать это?

  2. Есть ли какой-то особый способ, которым я должен инициировать соединение mysql? Почему это работает с использованием MySQL, а не с помощью сценария? Я пытался использовать прямой mysql, и это тоже работает - поэтому я думаю, что это разъем Python, который может потребоваться настроить правильно, поскольку это единственный компонент не работает.

  3. В настоящее время я использую 'autocommit = 1' для разъема и 'buffered = True' для курсора. Я знаю, что вы можете установить 'autocommit = 0' в SQL до 'START TRANSACTION', так что понимайте, для блокировки мне может понадобиться это сделать, но для всех других транзакций я бы предпочел оставить автокоммит включенным. Это нормально и / или выполнимо?

КОД:

#!/usr/bin/env python

import mysql.connector
import pprint

conn = mysql.connector.connect(user='testuser',
                               password='testpass',
                               host='127.0.0.1',
                               database='test_db',
                               autocommit=True)

dbc = conn.cursor(buffered=True)

qry = "START TRANSACTION; SELECT * FROM 'test' WHERE task_status != 1 LIMIT 1 ON UPDATE;"
sql_select = dbc.execute(qry)
try:
    output = dbc.fetchall()
except mysql.connector.Error as e:
    print("  * SQL QRY: {0}".format(qry))
    print("  * SQL RES: {0}".format(e))
    exit()
else:
    print("  * SQL QRY: {0}".format(qry))
    print("  * SQL RES: {0}".format(output))

Большое спасибо,

Frank

1 Ответ

0 голосов
/ 06 июля 2018

Итак, немного поиграв, я понял (методом проб и ошибок), что правильный способ сделать это - просто поставить FOR UPDATE в конце обычного запроса:

Полный код приведен ниже (включая возможность добавления фиктивных записей для тестирования):

#!/usr/bin/env python

import mysql.connector
import pprint
import os

conn = mysql.connector.connect(user='testuser',
                               password='testpass',
                               host='127.0.0.1',
                               database='test_db',
                               autocommit=True)

dbc = conn.cursor(buffered=True)

worker_pid = os.getpid()
all_done = False

create = False

if create:

    items = []
    for i in range(10000):
        items.append([0, 'TASK%04d' % i])

    dbc.executemany('INSERT INTO test (task_status, task_ref) VALUES (%s, %s)', tuple(items))
    conn.commit()
    conn.close
    exit()


while all_done is False:

    print(all_done)
    qry = (
        "SELECT id FROM test WHERE task_status != 1 LIMIT 1 FOR UPDATE;"
        )
    sql_select = dbc.execute(qry)

    try:
        output = dbc.fetchall()
    except mysql.connector.Error as e:
        print("      * SQL QRY: {0}".format(qry))
        print("      * SQL RES: {0}".format(e))
        exit()
    else:
        print("      * SQL QRY: {0}".format(qry))
        print("      * SQL RES: {0}".format(output))

    if len(output) == 0:
        print("All Done = Yes")
        all_done = True
        continue
    else:
        print("Not Done yet!")

    if len(output) > 0:
        test_id = output[0][0]
        print("WORKER {0} FOUND: '{1}'".format(worker_pid, test_id))
        qry = "UPDATE test SET task_status = %s, task_ref = %s WHERE id = %s;"

    sql_select = dbc.execute(qry, tuple([1, worker_pid, test_id]))
    conn.commit()

    try:
        output = dbc.fetchall()
    except mysql.connector.Error as e:
        print("      * SQL QRY: {0}".format(qry))
        print("      * SQL RES: {0}".format(e))

    else:
        print("      * SQL QRY: {0}".format(qry))
        print("      * SQL RES: {0}".format(output))

    print(all_done)

Надеюсь, это поможет кому-то сэкономить время, так как есть много мест с разной информацией, но поиск python3, mysql-connector и транзакций мне ничего не дал.

Удачи,

Frank

...