Тупик в vertica db для одновременного доступа к разным строкам одной и той же таблицы - PullRequest
0 голосов
/ 21 июня 2020

У меня есть таблица в базе данных Vertica, которая выглядит так:

введите описание изображения здесь

Есть четыре процесса p1, p2, p3, p4, которые выполняются одновременно и выполняют операции с соответствующими строками, т.е.

p1 ---> Выбрать последнее значение, обновить время начала , Обновить время окончания в строке p1.

p2 ---> Выбрать последнее значение, Обновить время начала, Обновить время окончания в строке p2.

p3 ---> Выбрать последнее значение, Обновить время начала, Обновить время окончания в строке p3.

p4 ---> Выбрать последнее значение, Обновить время начала, Обновить время окончания в строке p4.

Все четыре процесса имеют доступ только и только своя собственная строка c, например, p1 никогда не обращается к строке p2.

Тем не менее, я сталкиваюсь с проблемой взаимоблокировки, поскольку p1 выполняет запрос выбора в строке p1 и удерживает блокировку, в то же время p4 пытается обновить строку p4.

Уровень изоляции транзакции READ COMMITTED.

Что я могу сделать, чтобы избежать этой взаимоблокировки?

Изменить 1:

Вот пример запросы, выполняемые процессами:

UPDATE $log_table SET start_time = CURRENT_TIMESTAMP(1) WHERE process_name = '$taskname'

SELECT last_value FROM $log_table WHERE process_name ='$taskname'

UPDATE $log_table SET end_time = CURRENT_TIMESTAMP(1) WHERE process_name = '$taskname'

Edit 2:

Sample perl script

$taskname = "p1"   # respective process names. for process1 its p1. for process2 its p2 and so on. #
$log_table = "config table name"

# Update start time 
$current_timestamp = localtime();
$sqlstmt = "UPDATE $log_table SET start_time = '$current_timestamp' WHERE process_name = '$taskname'";
$db->prepare("$sqlstmt") or handle_error();
$handle->execute() or handle_error();

# do other stuff 

# Fetch last modified value
$sqlstmt = "SELECT last_value FROM $log_table WHERE process_name ='$taskname'";
$db->prepare("$sqlstmt") or handle_error();
$handle->execute() or handle_error();
$result = $handle->fetchrow();
# do other stuff


# Update end time
$current_timestamp = localtime();
$sqlstmt = "UPDATE $log_table SET end_time = '$current_timestamp' WHERE process_name = '$taskname'";
$db->prepare("$sqlstmt") or handle_error();
$handle->execute() or handle_error();
$result = $handle->fetchrow();

1 Ответ

1 голос
/ 22 июня 2020

Проблема, с которой вы столкнулись, заключается в том, что Vertica на самом деле не обновляет существующую строку, а помечает существующую строку как удаленную, добавляя новый вектор удаления в физическое хранилище, и вставляет новую строку. Это также включает исключительную блокировку стола. Ваши связи АВТОКОММИТНЫ? Если нет, попробуйте переключиться на AUTOCOMMIT и посмотрите, что произойдет. Если это не поможет, есть некоторые возможности дизайна ...

Я создал таблицу и четыре таких сценария:

SET SESSION AUTOCOMMIT TO OFF;
UPDATE log_table SET start_time = CURRENT_TIMESTAMP WHERE process_name = 'p1';
SELECT last_val FROM log_table WHERE process_name ='p1';
UPDATE log_table SET end_time   = CURRENT_TIMESTAMP WHERE process_name = 'p1';
COMMIT;

Затем я использовал драйвер запроса для запуска четырех скрипты параллельно. Ожидание в течение двух секунд между выполнением каждого оператора в скрипте. Я также чередовал, добавляя или удаляя операторы BEGIN WORK / COMMIT WORK. Боюсь, что я не смог вызвать тупик, поэтому я здесь вслепую.

Еще одна менее инвазивная альтернатива: разные контейнеры ROS на имя процесса:

ALTER TABLE log_table PARTITION BY process_name REORGANIZE;

Вам может повезти с dimini sh взаимоблокировками - хотя удаление (а обновление - это удаление, а затем вставка) всегда вызывает монопольную блокировку таблицы, но они могут блокировать друг друга немного реже.

Метод грубой силы:

CREATE TABLE log_p1 (last_val INT start_time TIMESTAMP,end_time TIMESTAMP);
CREATE TABLE log_p2 (last_val INT start_time TIMESTAMP,end_time TIMESTAMP);
CREATE TABLE log_p3 (last_val INT start_time TIMESTAMP,end_time TIMESTAMP);
CREATE TABLE log_p4 (last_val INT start_time TIMESTAMP,end_time TIMESTAMP);
CREATE VIEW log_table AS -- just for reporting, not for maintenance
          SELECT 'p1' AS proc_name, * FROM log_p1
UNION ALL SELECT 'p2' AS proc_name, * FROM log_p2
UNION ALL SELECT 'p3' AS proc_name, * FROM log_p3
UNION ALL SELECT 'p4' AS proc_name, * FROM log_p4
;

И четыре скрипта будут отличаться только p4 от p1:

SELECT * FROM p1; -- fetch single row output into your application
TRUNCATE TABLE p1;
-- bind "last_val" and "end_time" host variables
INSERT INTO p1 VALUES(? , CURRENT_TIMESTAMP, ?);
SELECT last_val FROM log_p1;
TRUNCATE TABLE p1;
-- bind "last_val" and "start_time" host variables
INSERT INTO p1 VALUES(? , ?, CURRENT_TIMESTAMP);
COMMIT;
...