Многопользовательские базы данных должны иметь механизмы защиты и обеспечения консистенции данных для ситуаций, когда множество пользователей и процессов пакетной обработки пытаются одновременно модифицировать данные в базе данных. Помимо механизма журналирования транзакций, механизм блокировок является ключевым методом обеспечения целостности данных в многопользовательском окружении.
Многопользовательские базы данных должны иметь механизмы защиты и обеспечения консистенции данных для ситуаций, когда множество пользователей и процессов пакетной обработки пытаются одновременно модифицировать данные в базе данных. Помимо механизма журналирования транзакций, механизм блокировок является ключевым методом обеспечения целостности данных в многопользовательском окружении.
Данная статья объясняет принцип работы механизма блокировок в IDS, что поможет вам в анализе конфликтов блокировок и ситуаций ожидания.
Примеры основаны на базе данных stores_demo, которая может быть создана при помощи выполнения команды dbaccessdemo stores_demo -log.
Динамическое выделение блокировок
Параметр onconfig LOCKS (максимальное значение 8000000) определяет начальное количество блокировок для всего инстанса. При этом в IDS имеется механизм динамического выделения блокировок. Если значение, установленное параметром LOCKS, достигнуто, IDS удваивает размер внутренней таблицы блокировок. Эта процедура может быть выполнена не более 15 раз. Не более 100000 дополнительных блокировок может быть выделено единовременно. Таким образом максимальное количество блокировок, доступное в IDS ограничено 9500000:
8000000 + (15 х 100000) = 9500000
Механизм динамического выделения блокировок позволяет приложению продолжить работу, несмотря на то, что изначально заданное количество блокировок уже было достигнуто.
Иногда динамическое выделение блокировок скрывает плохо написанные приложения. Разумным предложением было бы добавить в IDS возможность контролировать максимальное число блокировок, которое может быть выделено для одной сессии. Это бы помогло администраторам баз данных выявлять и блокировать плохо написанные приложения не влияя на работу других сессий базы данных.
На данный момент, если IDS 10 достигает предела в 9500000 блокировок, все работающие клиентские приложения при запросе блокировки получают ошибку, указывающую на переполнение таблицы блокировок. Это означает, что все сессии могут столкнуться с проблемой, которую вызвало одно единственное "плохое" приложение.
Настройки, влияющие на сессии к базам данных
Каждая сессия может установить для себя индивидуальное время ожидания, что может помочь избежать переполнения таблицы блокировок и сбоя работы всех приложений. По-умолчанию в IDS устанавливается режим блокировок not wait, который означает что, сервер вернет приложениею следующие сообщения об ошибке, как только произойдет конфликт блокировок:
-
-244: Could not do a physical-order read to fetch next row -
107: ISAM error: record is locked
Вы можете указать необходимый режим блокировок для сессии с помощью следующего расширения Informix для ANSI SQL:
- set lock mode to not wait
- set lock mode to wait
- set lock mode to wait <#sec>
Параметр конфигурации DEADLOCK_TIMEOUT определяет максимальный интервал времени, в течении которого IDS, выполняя распределенную транзакцию, будет ожидать получения блокировки на удаленном инстансе IDS перед тем, как вернуть приложению код ошибки.
Не рекомендуется указывать бесконечное время ожидания блокировки (set lock mode to wait) Это увеличивает вероятность возникновения конфликтов блокировок и взаимоблокировок (deadlock). Также это скрывает некачественный код приложений.
Нормальной практикой для OLTP-систем считается интервал ожидания блокировок, равный 5-10 секундам. Если IDS не удается получить блокировку в установленный промежуток времени, приложению возвращается соответствующий код ошибки SQL. В этой ситуации приложения должны решать, повторить ли запрос, или же сделать откат (rollback) текущей транзакции.
Вы можете проверить текущий уровень изоляции и режим блокировок для конкретной сессии базы данных с помощью команды onstat -g sql:
Листинг 1. Уровень изоляции сессии - onstat -g sql
Вывод команды onstat -g sql: -------------------------- Sess SQL Current Iso Lock SQL ISAM F.E. Id Stmt type Database Lvl Mode ERR ERR Vers Explain 18 - stores_demo CR Not Wait 0 0 9.03 Off 16 - stores_demo RR Wait 10 0 0 9.03 Off |
Данный пример показывает две сессии базы данных с разными уровнями изоляции:
- Session 18, CR Committed Read, Not Wait
- Session 16, RR Repeatable Read, Wait 10
Еще одной полезной возможностью IDS могла бы стать возможность установки режима блокировки по-умолчанию для базы данных или же для всего инстанса, в случае, если приложение не выполняет явно выражение set lock mode to wait.
Взаимоблокировка может возникнуть, когда два пользователя удерживают блокировки, и, при этом каждый пользователь хочет получить блокировку, которую удерживает другой пользователь. Для предотвращения подобной ситуации IDS сканирует внутреннюю таблицу блокировок перед тем как предоставить запрашиваемую блокировку. В случае обнаружения потенциальной тупиковой блокировки, IDS возвращает пользовательской сессии код ошибки ISAM -143 (deadlock detected).
Количество обнаруженных взимоблокировок
Команда onstat -p показывает общее число взаимоблокировок (поле deadlks), обнаруженных c момента запуска сервера, или с момента выполнения команды onstat -z (обнуление статистики). Последнее время рестарта сервера может быть определено с помощью следующего запроса SQL:
- select dbinfo("utc_to_datetime", sh_pfclrtime) from sysmaster:sysshmvals
К сожалению IDS не позволяет получить достаточное количество детальной информации об обнаруженных взаимоблокировках, и о связанных с ними приложениях. Из-за этого бывает сложно определить причину возникновения взаимоблокировки. База данных sysmaster содержит несколько таблиц, которые содержат дополнительную информацию:
-
sysprofile: Содержит общее число обнаруженных взаимоблокировок, которое соответствует полю
deadlksвывода команды onstat -p - syssesprof: Хранит число взаимоблокировок, обнаруженное в конкретной сесcии базы данных
- sysptprof: Содержит число обнаруженных взаимоблокировок, связанных с конкретной таблицей
Возможность IDS, позволяющая устанавливать "ловушку" для конкретной ошибки бывает очень полезным в анализе тупиковых взаимоблокировок. Как уже было сказано, при обнаружении взаимоблокировки IDS возвращает приложению код ошибки ISAM -143. Установить "ловушку" для взаимоблокировки можно с помощью команды onmode -I 143. При обнаружении взаимоблокировки, IDS создаст так называемый assertion failure файл (AF, для краткости). В данном диагностическом файле сохраняется вывод команды onstat -a, полученный момент возниконовения взаимоблокировоки. Вы можете проанализировать данный файл на предмет выражения SQL, которое выполнялось пользовательской сессией в момент возникновения проблемы. Также благодаря информации, содержащейся в таком файле можно определить блокировки, которыми обладала "проблемная" сессия, и блокировки остальных сессий.
Параметр файла конфигурации onconfig DEADLOCK_TIMEOUT не влияет на время определения взаимоблокировки или на промежуток времени, которое ожидает приложение при возникновении локальной взаимоблокировки. IDS всегда немедленно разрешает ситуации с локальными взаимоблокировками.
В случае, если ситуация ожидания блокировки возникает при работе с данными на удаленном сервере IDS, параметр конфигурации DEADLOCK_TIMEOUT определяет верхний предел времени в секундах, которое локальный сервер IDS будет ожидать для получения запрашиваемой блокировки. По истечении этого времени сервер решает, что возникла ситуация с взаимоблокировки, и возвращает клиентскому приложению код ошибки ISAM -154 (deadlock timeout expired - possible deadlock).
Как уже было сказано, IDS не делает перекрестных проверок внутренних таблиц блокировок между отдельными инстансами СУБД. Вместо этого используется параметр DEADLOCK_TIMEOUT, который определяет время ожидания взаимоблокировки. Данный параметр переопределяет значение времени ожидания блокировки, устанавливаемое на уровне приложения выражением set lock mode to wait для случая с распределенной транзакцией.
В OLTP окружении с сотнями параллельно выполняемых клиентских сессий зачастую сразу несколько транзакций пытаются получить доступ к одной и той же записи таблицы единовременно. Поэтому хорошей практикой считается создание как можно более коротких транзакций, что позволяет избежать конфликтов блокировок.
Если ваше приложение не использует выражение set lock mode to wait, вы получите сообщение об ошибке SQL, как только ваше приложение запросит блокировку для записи, текущая блокировка которой несовместима с запрашиваемой. Во избежание прерывания выполнения операции обычно используется выражение set lock mode to wait, указывающее, что IDS должен приостановить сессию базы данных (нить sqlexec), пока блокировка не предоставлена, или пока не истек указанный интервал ожидания. Здесь следует учитывать, что подобное решение может привести к множественным ожиданиям блокировок, что, в свою очередь, может снизить пропускную способность инстанса IDS. В результате чего, конечные пользователи могут испытывать неудобства из-за большого времени отклика или более продолжительного выполнения пакетных операций. Поэтому не рекомендуется устанавливать бесконечное время ожидания (см. рекомендации).
Анализ конфликтов блокировок может быть весьма непросты занятием в динамическом окружении с работающими транзакциями. Наиболее эффективно иметь в наличии набор скриптов, позволяющих проанализировать ситуацию ожидания блокировки в реальном времени. В следующем разделе представлены несколько команд утилиты onstat, которые могут быть полезны. Вы можете использовать их как основу для написания более сложных скриптов. Также вы можете воспользоваться преимуществами утилиты lockwt.
Полезные команды утилиты onstat
Анализ конфликтов блокировок следует начать с вывода команды onstat -u. Он содержит следующие интересующие нас значения:
-
Текущее количество блокировок, удерживаемых конкретной сессией
-
Поле
locksпоказывает количество блокировок для каждой сессии. Сессии с большим количеством блокировок являются наиболее вероятными кандидатами для конфликтных ситуаций. Хотя это и не всегда так, но большое число блокировок часто указывает на плохо написанное приложение.
-
Поле
-
Сессии, ожидающие блокировок
-
Сессии баз данных, ожидающие блокировок отмечены флагом 'L' в первой позиции колонки
flags.
-
Сессии баз данных, ожидающие блокировок отмечены флагом 'L' в первой позиции колонки
Листинг 2. Вывод команды onstat -u
Результат работы команды onstat -u: --------------------- address flags sessid user tty wait tout locks nreads nwrites 4506b44c L-BPR-- 20 informix 11 440cfac4 -1 17 19 0 4506b978 Y--P--D 16 informix - 4407d138 0 0 0 0 ... ... |
Вы можете выполнить команду onstat -k | grep 'L-', чтобы определить все текущие сессии, которые ожидают получения блокировок. С помощью команды onstat -g ses <sessid> вы можете наблюдать запросы SQL, выполняемые сессиями в данный момент времени. С помощью этой команды также можно узнать имя базы данных (Current Database), текущий уровень изоляции (Iso Lvl), и установленное время ожидания блокировки (Lock Mode). В поле status вы можете увидеть количество секунд, оставшееся до того, как IDS вернет ошибку приложению, если блокировка так и не будет получена.
Листинг 3. Вывод команды onstat -g ses <sessid>
Результат работы команды onstat -g ses 20: ----------------------------- ... ... 42 sqlexec 4506b44c L-BPR-- 7168 sleeping(secs: 9) ... ... Sess SQL Current Iso Lock SQL ISAM F.E. Id Stmt type Database Lvl Mode ERR ERR Vers Explain 20 DELETE (all) stores_demo CR Wait 60 0 0 9.03 Off ... ... Current SQL statement: delete from customer |
Следующим шагом необходимо определить сессию, удерживающую блокировку и вызвавшую ожидание блокировки другой сессией. Команда onstat -k показывает все текущие блокировки инстанса IDS:
Листинг 4. Вывод команды onstat -k
Результат работы команды onstat -k: -------------------------- Locks address wtlist owner lklist type tblsnum rowid key#/bsiz 440cf9bc 0 4506b44c 0 HDR+S 100002 207 0 440cfa14 0 4506b44c 440cf9bc HDR+IX 10015f 0 0 440cfa6c 0 4506b44c 440cfa14 HDR+X 10015f 101 0 440cfac4 4506b44c 0 440cfa6c HDR+ 10015f 106 0 440cfb1c 0 4506b44c 440cfa6c HDR+X 100160 101 K- 1 440cfb74 0 4506b44c 440cfb1c HDR+X 100175 101 K- 1 440cfbcc 0 4506b44c 440cfb74 HDR+X 10015f 102 0 ... ... |
Нас интересует поле wtlist. Оно содержит адрес пользовательской нити, ожидающей блокировки. В зависимости от уровня текущей активности, вывод onstat -k может быть весьма объемным. Вы можете использовать следующие команды, чтобы определить, какую именно блокировку ожидает пользовательская сессия с адресом 4506и44с:
- AWK : onstat -k | awk '$2 ~/4506b44c/ { print }'
- PERL: onstat -k | perl -ane 'print if $F[1] eq "4506b44c"'
Поле owner содержит адрес пользовательской нити, удерживающей блокировку. Определить номер сессии очень легко, если воспользоваться командой onstat -u и утилитой grep. Вы можете проанализировать причину конфликта, изучив вывод команды onstat -g <sessid>. Если сессия сама ожидает блокировки (поле flags вывода onstat -u содержит 'L' на первой позиции), вам придется повторить вышеописанные шаги чтобы определить сессию, которая стала причиной данной ситуации.
Утилита lockwt, написанная на языке Esql/C, позволяет достаточно комфортно анализировать конфликты блокировок. Чтобы начать использовать эту утилиту, вам нужно установить Informix Client SDK и компилятор для языка C, который нужен для того, чтобы скомпилировать lockwt. Данная утилита проводит поиск через пару таблиц базы данных sysmaster на предмет ситуаций ожидания блокировок.
Программа выводит каждую пользовательскую сессию, удерживающую блокировку, а также сессии, ожидающие освобождения этой блокировки. Утилиту можно запускать с опцией, аналогичной onstat -r, которая позволяет повторять поиск блокировок через указанный промежуток времени: lockwt -r <#sec>.
Утилита lockwt позволяет в реальном времени осуществлять мониторинг комплексных ситуаций ожидания блокировок, представляя собранную информацию в доступном для чтения формате.
Листинг 5. Описание вывода утилиты lockwt
Результат работы утилиты lockwt:
-------------------
(0) (1) (2) (3) (4) (5)
(6) (7) (8) (9)
WAIT SID :PID PROCNAME USERNAME
LKTYPE DATABASE:TABLENAME LKOBJ
0 - 13900:12303 workprocess3 dbuser
X rome :orders row
1 W 53600:23613 batchp12
dbuser rome :orders
Описание полей
(0) Порядковый номер
(1) Флаг ожидания. Возможные значения:
"-" - эта сессия держит блокировку; она всегда идет первой в выводе.
"W" - эта сессия ожидает вышеследующую сессию.
(2) Идентификатор сессии сервера баз данных.
(3) Идентификатор процесса UNIX,
удаленные подключения имеют идентификатор -1.
(4) Имя процесса UNIX.
В случае с удаленным подключением, это поле будет пустым.
(5) Имя пользователя UNIX,
ассоциированного с данной сессией.
(6) Тип блокировки. Возможные значения:
"X" - exclusive lock
"S" - shared lock
"U" - update lock
Для дополнительных типов блокировок можно выполнить следующий запрос:
-> select txt from sysmaster:flags_text where tabname = "syslcktab"
(7) Имя базы данных
(8) Имя таблицы, для которой удерживается блокировка. В случае, если
это блокировка индекса и, если индекс хранится отдельно от таблицы
(detached index), имя индекса будет отображаться в данном поле.
(9) Тип заблокированного объекта:
"table" - блокировка таблицы
"idx" - блокировка индекса
"page" - блокировка страницы
"row" - блокировка записи
|
Листинг 6. Вывод lockwt в случае ожидания блокировки - пример 1
Результат работы утилиты lockwt: ------------------- WAIT SID :PID PROCNAME USERNAME LKTYPE DATABASE:TABLENAME LKOBJ 0 - 13900:12303 workprocess3 dbuser X rome :orders row 1 W 53600:23613 batchp12 dbuser rome :orders |
В данном примере сессия 13900 (процесс "workprocess3") удерживает блокировку для записи таблицы orders. Сессия 53600 ожидает снятия этой блокировки. Вам нужно проанализировать сессию 13900 с помощью команды onstat -g ses 13900.
Листинг 7. Вывод lockwt в случае ожидания блокировки - пример 2
Результат работы утилиты lockwt: ------------------- WAIT SID :PID PROCNAME USERNAME LKTYPE DATABASE:TABLENAME LKOBJ 0 W 3894: -1 (remote) eherber1 X rome :status row 1 W 17048: 3140 batchp3 dbuser rome :status 0 - 63296: -1 (remote) eherber1 X rome :customer_order row 1 W 3894: -1 (remote) eherber1 rome :customer_order |
Данный пример несколько более сложный. Сессия 17048 ожидает, пока сессия 3890 не снимет блокировку с таблицы status. Но если посмотреть на следующую пару блокировок, то можно заметить, что сессия 3894 ожидает сессию 64296. Это типичная ситуация эскалации блокировок. Необходимо понять, что делает сессия 63296. Это можно сделать с помощью команды onstat -g ses 63296.
В исходном коде утилиты lockwt можно обнаружить несколько интересных запросов к базе данных sysmaster, которые могут быть полезны.
Обратитесь к разделу Downloads, чтобы скачать утилиту lockwt бесплатно.
Возможно, что вам уже довелось столкнуться со странной проблемой при выполнении alter table: несмотря на то, что таблица заблокирована вами в эксклюзивном режиме, alter table не может быть выполнен. Ниже приведен пример того, как это может выглядеть:
Листинг 8. Не эксклюзивный доступ к таблице
Результат работы команды dbaccess -e stores_demo <script.sql>: -------------------------------------------------- begin; Started transaction. lock table customer in exclusive mode; Table locked. alter table customer add (mycol integer); 242: Could not open database table (informix.customer). 106: ISAM error: non-exclusive access. |
Это происходит, если курсор был открыт в таблице customer другим пользователем. Курсор сам по себе не устанавливает никаких блокировок строк (иначе мы бы не смогли получить эксклюзивную блокировку для всей таблицы), но он запрещает IDS модифицировать информацию о разделе.
Для решения данной проблемы необходимо идентифицировать сессию, открывшую курсор на этой таблице:
-
Определите номер раздела (partnum) для таблицы customer:
- Select hex(partnum) from systables where tabname = "customer".
- Если номер раздела равен нулю, это означает, что мы имеем дело с фрагментированной таблицей. В этом случае необходимо выполнить следующий запрос SQL, чтобы определить номера разделов для фрагментов таблицы:
Select st.tabname, dbinfo("dbspace", sf.partn), hex(sf.partn) from systables st, sysfragments sf, where st.tabid = sf.tabid and sf.fragtype = "T"and st.tabname = "customer".
-
Полученный номер(а) раздела необходимо использовать для поиска среди используемых в данный момент таблиц::
onstat -g opn | grep -i <hex_partnum>
-
Используйте значение поля
rstcb(это адрес соответствующей пользовательской нити в разделяемой памяти IDS), полученное в предыдущем пункте, для поиска в выводе onstat -u
:onstat -u | grep <rstcb_without_leading_0x>
После того, как вы установили номер сессии, открывшей курсор, ее можно завершить выполнив команду onmode -z <sessid>.
Если вы используете IDS версии 7.31.xD5, 9.40 или 10, вы можете воспользоваться преимуществами переменной окружения IDX_DIRTY_WAIT. Эта переменная окружения может быть установлена на сервере или в клиентском окружении. Переменная окружения IFX_DIRTY_WAIT определяет интервал в секундах, который будет ожидать сессия, выполняющая DDL выражение, пока существующие сессии не закончат "грязные" чтения таблицы (dirty reads). По истечении установленного интервала IDS вернет ту же ошибку, которую он возвращает в случае, если переменная окружения IFX_DIRTY_WAIT не установлена.
Анализ конфликтов блокировок в реальном времени с сотнями транзакций, обрабатываемых параллельно, может быть весьма непростой задачей. Данная статья кратко описывает механизм блокировок IDS, что сделает решение конфликтов блокировок более скорым.
На момент написания данной статьи следующая версия IDS (сервер баз данных, который IBM позиционирует как решение для высокопроизводительное OLTP и встраиваемых систем) входит в стадию бета-тестирования. Новая версия привнесет множество новых опций касательно блокировок.
- Образец кода: Скачать утилиту lockwt
Научиться
- Оригинал статьи: "" IDS locking, Part 2: Understand locking behavior and analyze lock conflicts in IDS. (EN)
- IDS 10 Information Center предоставляет удобный веб-интерфейс для доступа к полной документации IDS Version 10. (EN)
- Посетите
The Informix Zone: Здесь технологии встречаются с маркетингом (EN)
- developerWorks Informix zone: Вашему вниманию представлены статьи и инструкции, а также множество других ресурсов, посвященных Informix. (EN)
- IDS Support: Получите полезную техническую и иную информацию, связанную с IDS. (EN)
- Informix Webcasts: Получите информацию о свежих вебкастах, посвященных Informix. (EN)
-
Будте в курсе мероприятий и вебкастов на developerWorks . (EN)
Получить продукты и технологии
- Скачайте бесплатную пробную версию IDS Version 10. (EN)
- Скачайте
другие пробные продукты Informix.
-
Создайте свой следующий проект с использованием пробного ПО IBM, доступного для скачивания на developerWorks. (EN)
Обсудить
- Примите участие в обсуждении материала на форуме.
- Форум разработчиков и пользователей IDS: Откройте для себя место, где разработчики и пользователи IDS делятся своими вопросами, мыслями и идеями. (EN)
- Посетите International Informix User Group, чтобы стать одним из членов многотысячного сообщества пользователей Informix. (EN)
- Посетите
Informix-Newsgroup comp.databases.informix via Google для обсуждения систем управления базами данных. (EN)
-
Заведите свой блог на developerWorks и станьте членом сообщества developerWorks (EN)