Глава 1. Введение

Одновременность и блокировки выступают некими наиболее сложными темами когда дело качается баз данных. Обычно исполняюшийся быстро и без проблем запрос внезапно может отнять гораздо больше времени или отказать с ошибкой когда имеющиеся обстоятельства "те что надо", а потому блокировка или состязательность превращаются в проблему. Вы можете спросить себя зачем требуются блокировки раз они способны вызывать такие проблемы. Эта и последующие 11 глав попробуют пояснить это, а также как лучше справляться с ними. Последние шесть глав этой книги представляют шесть тематический исследований, объединяющих все сведения воедино в правдоподобных сценариях совместно с анализом того как избегать таких проблем или уменьшать их.

В этой главе вы вначале изучите почему блокировки важны несмотря на вызываемые ими проблемы. Затем будет пояснена их взаимосвязь с транзакциями. Остальная глава представляет введение в то как применяются в данной книге примеры в правдоподобных ситуациях, а также базы данных world, sakila и employees, которые применяются на протяжении данной книги.

Зачем нужны блокировки

Мир может показаться исключительным, когда нет нужды в блокировках баз данных. Тем не менее стоимость этого будет столь значительна, что окажется что такую базу данных смогут применять лишь несколько вариантов использования, а для баз данных общего назначения, таких как MySQL невозможно избежать блокировок. Если у вас нет блокировок, вы не сможете обладать одновременностью. Представьте себе, что к вашей базе данных разрешено лишь одно подключение (вы можете согласиться, что это само по себе является блокировкой, тем самым такая система в любом случае не свободна от блокирований)- это не очень полезно.

[Замечание]Замечание

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

Когда у вас имеется несколько подключений, одновременно выполняющих запросы, вам потребуется некий способ, которым эти соединения не наступают друг другу на ноги. Именно здесь в картину вступают блокировки. Вы можете представлять себе блокировки аналогично дорожным сигналам при движении по трассам (Рисунок 1-1), которые регулируют доступ к имеющимся ресурсам во избежание происшествий. При пересечении дорог необходимо гарантировать что две машины не пересекутся друг с другом и не столкнутся.

 

Рисунок 1-1


Блокировки базы данных схожи со светофорами

В базе данных двум запросам на доступ требуется обеспечить что их данные не конфликтуют. Так же как и на перекрёстке существуют различные уровни управления доступом - сигналы уступи дорогу, остановись, а также светофоры - также имеются и различные типы блокировок в базе данных.

Уровни блокирования

Блокировки в MySQL бывают нескольких разновидностей, причём действующих на различных уровнях MySQL в диапазоне от блокировок уровня пользователя до блокировок записей. На самых верхних уровнях находятся блокировки уровня пользователя, которые способны защищать пути кода в его приложении целиком и любые объекты внутри самой базы данных. В середине имеются блокировки, которые работают с объектами самой базы данных. Они включают блокировки метаданных, которые защищают собственно метаданные имеющихся таблиц, а также в качестве блокировок таблиц, которые защищают все данные в этой таблице. Общими для блокировок уровня пользователя и уровня таблиц выступает то, что они реализуются на уровне SQL самой базы данных. Блокировки верхнего уровня обсуждаются в Главе 6.

На самом нижнем уровне находятся те блокировки, которые реализуются самими механизмами хранения. По своей природе эти блокировки зависят от того механизма хранения, который вы применяете. Поскольку InnoDB на сегодняшний день наиболее применяемый механизм в MySQL (и установлен по умолчанию), данная книга охватывает блокировки, специфичные для InnoDB. InnoDB включает в свой состав блокировки на своих записях, которые наиболее просты для понимания, а также более сложные понятия, такие как блокировки промежутков, блокировки следующего ключа, блокировки предикатов и блокировки намерения вставки. Кроме того, имеются взаимные исключения (мьютексы) и семафоры (они также случаются и на самом верхнем уровне SQL). Специфичные для InnoDB блокировки и взаимные исключения, семафоры обсуждаются в Главе 7.

Блокировки и транзакции

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

Само понятие уровня изоляции транзакции также играет важную роль при работе с блокировками. Значение уровня изоляции влияет как на то какие блокировки предпринимаются и насколько долго они удерживаются.

Глава 3 и Глава 4 рассматривают как отслеживать транзакции, а Глава 11 и Главв 12 покрывают то как работают транзакции, их воздействие и собственно уровни изоляции транзакции.

Примеры

На протяжении всей книги имеются примеры, которые способствуют иллюстрации обсуждаемой темы или настройки ситуации, которую вы можете расследовать. За исключением Глав17 и Глава 18 все необходимые операторы для воспроизведения всех перечисляемых тестов. В целом вам потребуется для этих примеров более одного подключения, а потому приглашения для всех запросов были настроены на то чтобы указывать какое подключение применять для какого из запросов когда это важно. Например, Connection 1> означает что этот запрос должен исполняться самым первым вашим подключением.

Все примеры из этой книги исполнялись в Оболочке MySQL. Для краткости значением приглашения в наших примерах это mysql> за исключением когда важно значение подключения или когда режим языка отличен от SQL. Эти примеры, тем не менее, также работают и со старым клиентом командной строки mysql.

[Совет]Совет

Если вы не знакомы с Оболочкой MySQL, тогда это клиент командной строки MySQL второго поколения с поддержкой обоих SQL, Python и JavaScript. Он также поставляется с несколькими встроенными утилитами, содержащими инструменты для управления Кластером InnoDB MySQL и традиционных топологий репликации. Для знакомства с Оболочкой MySQL обратитесь к руководству пользователя или к книге Чарли Белла Introducing MySQL Shell (Apress).

К тому же, данная книга поставляется с модулем Python - concurrency_book.generate - который может быть импортирован в Оболочку MySQL и применяться для воспроизводства всех не самых простых примеров. Остаток данного раздела описывает как применять этот модуль Оболочки MySQL. Приводимое здесь содержимое представляет собой отрывок Дополнения B, которое содержит более пространное справочное руководство по этому модулю, включая то как реализовать ваши собственные примеры.

[Замечание]Замечание

По своей природе некоторые данные в приводимых примерах будут отличаться для каждого исполнения. Это, в частности, имеет место для идентификаторов, а также адресов памяти и аналогичного. Поэтому не ожидайте получения идентичных результатов для всех подробностей когда вы попытаетесь воспроизвести эти примеры.

Предварительные требования для Модуля concurrency_book.generate

Наиболее важное требование для использования предоставляемого этой книгой модуля Оболочки MySQL состоит в том, что вы применяете MySQL Shell 8.0.20 более позднюю версию. Это строгое требование, ибо данный модуль изначально для создания необходимых под приводимые варианты использования пользуется методом shell.open_session(). Этот метод был введён лишь в выпуске 8.0.20. Преимущество shell.open_session() над mysql.get_classic_session() и mysqlx.get_session() состоит в том, что open_session() работает прозрачно с обоими протоколами, как классическим протоколом MySQL, так и с новым протоколом X.

Если по какой- то из причин вы остаётесь со старой версией Оболочки MySQL, вы можете обновить свои варианты применения на включение настроек протокола protocol (см. Определение рабочей нагрузки) для задания того какой из протоколов применять в явном виде.

Также требуется чтобы некое подключение от Оболочки MySQL к Серверу MySQL уже имелось, так как модуль применяет значение URI этого подключения при создании всех дополнительных подключений, требуемых для рассматриваемого примера.

Все примеры были проверены на MySQL Server 8.0.21; тем не менее, большинство примеров будут работать и с более старыми выпусками, а некоторые даже с MySQL 5.7. Тем не менее, рекомендуется применять MySQL Server 8.0.21 или более позднюю версию.

Установка Модуля concurrency_book.generate

Для применения этого модуля вам необходимо выгрузить все файлы в каталог concurrency_book из репозитория GitHub этой книги (ссылку можно обнаружить на домашней странице этой книги). Самым простым является клонирование этого репозитория или выгрузка соответствующего файла ZIP со всеми файлами при помощи показанного на Рисунке 1-2 меню.

 

Рисунок 1-2


Меню GitHub для клонирования или выгрузки необходимого репозитория

Кликните по иконке планшета для копирования указанного URL, применяемого под клонирование соответствующего репозитория при помощи программного обеспечения Git в вашей системе или воспользуйтесь ссылкой Download ZIP для выгрузки файла ZIP из соответствующего репозитория. Вы вольны выбрать любой путь в качестве его местоположения для своих файлов,поскольку сама структура вниз такого каталога concurrency_book остаётся неизменной. Для данного обсуждения предполагается что вы клонировали или распаковали все файлы этого репозитория в C:\Book\mysql\concurrency, а потому файл generate.py находится в каталоге C:\Book\mysql-concurrency\concurrency_book\.

Чтобы иметь возможность импорта этого модуля в Оболочку MySQL, откройте или создайте файл mysqlshrc.py. Оболочка MySQL выполняет поиск этого файла в четырёх местах. В путях Microsoft Windows поиск выполняется в следующем порядке:

  1. %PROGRAMDATA%\MySQL\mysqlsh\

  2. %MYSQLSH_HOME%\shared\mysqlsh\

  3. <mysqlsh binary path>\

  4. %APPDATA%\MySQL\mysqlsh\

В Linux и Unix это

  1. /etc/mysql/mysqlsh/

  2. $MYSQLSH_HOME/shared/mysqlsh/

  3. <mysqlsh binary path>/

  4. $HOME/.mysqlsh/

Всегда выполняется поиск во всех четырёх каталогах, а если этот файл обнаружен во множестве мест, будет исполнен каждый из файлов. Это означает, что предшествует самый последний файл если эти файлы оказывают воздействие на те же самые переменные. Когда вы вносите изменения предназначенные для вас персонально, наилучшим местом для внесения таких изменений выступает четвёртое местоположение. Значение пути на шаге 5 может быть перекрыто переменной среды MYSQLSH_USER_CONFIG_HOME.

Вам требуется обеспечить добавление самого файла mysqlshrc.py в каталог с самим модулем в путь поиска Python и в качестве необязательного действия вы можете добавить оператор import чтобы сделать этот модуль доступным при запуске Оболочки MyShell. Неким примером такого файла mysqlshrc.py является


import sys
sys.path.append('C:\\Book\\mysql-concurrency')
import concurrency_book.generate
 	   

Двойные обратные слеши предназначены для Windows; в Linux и Unix вам нет необходимости экранировать слеши, которые отделяют элементы значения пути. Когда вы не включаете оператор import в файл mysqlshrc.py, вам потребуется исполнить его в Оболочке MySQL прежде чем вы воспользуетесь этим модулем.

Сбор сведений

Этот модуль содержит два метода, которые возвращают сведения относительно того как применять этот модуль. Один из них это модуль help(), который предоставляет сведения о том как применять данный модуль:


mysql-py> concurrency_book.generate.help()
		

Также существует метод show(), который перечисляет те рабочие нагрузки, которые способен исполнять метод run(), а также схемы, которые способен загружать метод load():


mysql-py> concurrency_book.generate.show()
		

Соответствующие рабочие нагрузки поименованы после перечисленного в этой книге кода, например, рабочая нагрузка с названием "Листинге 6-1" реализованную своим примером в Листинге 6-1.

Прежде чем вы сможете запускать на выполнение свои рабочие нагрузки, вам потребуется загружать некие проверочные данные, что также способен осуществлять этот модуль.

Загрузка данных проверки

Описываемый модуль concurrency_book.generate поддерживает загрузку примеров баз данных employees, sakila и world в ваш экземпляр MySQL. Для базы данных employees вы по своему желанию можете выбрать некую версию с разделами. База данных world наиболее важна для этой книги вслед за базой данных sakila. База данных employees применяется только для варианта применения в Главе 18. Каждая из этих трёх схем более подробно описывается позднее в этой главе.

[Замечание]Замечание

Если соответствующая схема уже существует, она будет сброшена как часть задания её загрузки. На практике это означает, что load() сбрасывает эту схему.

Вы загрузите схему при помощи метода load(), которая опционально возьмёт название той схемы, которую вы загружаете. Если вы не предоставляете названия схемы, тогда вы получите приглашение на её ввод. Листинг 1-1 показывает пример загрузки схемы world.

 

Листинг 1-1. Загрузка схемы world


mysql-py> concurrency_book.generate.load()
Available Schema load jobs:
===========================
 # Name                   Description
---------------------------------------------------------------------------
 1 employees              The employee database
 2 employees partitioned  The employee database with partitions
 3 sakila                 The sakila database
 4 world                  The world database
Choose Schema load job (# or name - empty to exit): 4
2020-07-20 21:27:15.221340  0 [INFO] Downloading https://downloads.mysql.com/docs/world.sql.zip to C:\Users\myuser\AppData\Roaming\mysql_concurrency_book\sample_data\world.sql.zip
2020-07-20 21:27:18.159554  0 [INFO] Processing statements in world.sql
2020-07-20 21:27:27.045219  0 [INFO] Load of the world schema completed
Available Schema load jobs:
===========================
 # Name                   Description
---------------------------------------------------------------------------
 1 employees              The employee database
 2 employees partitioned  The employee database with partitions
 3 sakila                 The sakila database
 4 world                  The world database
Choose Schema load job (# or name - empty to exit):
 	   

Метод load() выгружает необходимый файл с определением соответствующей схемы, если только он уже не имеется. Такой выгружаемый файл сохраняется в %APPDATA\mysql_concurrency_book\sample_data\ для Microsoft Windows и в ${HOME}/.mysql_concurrency_book/sample_data/ для прочих платформ. Если вы желаете выгрузить этот файл повторно, удалите его из этого каталога.

[Совет]Совет

Поскольку в Python Оболочки MySQL доступны лишь процедуры относительно нижнего сетевого уровня, когда у вас имеется медленное или нестабильное соединение, выгрузка базы данных employees может завершиться неудачей. Ещё один вариант - отличающийся от установки этой схемы вручную - состоит в выгрузке https://github.com/datacharmer/test_db/archive/master.zip и его сохранении в каталоге sample_data. После этого метод load() подхватит его и не будет предпринимать попыток его повторной выгрузки.

Если вы желаете выгрузить единственную схему, вы можете определить её название в качестве аргумента для load(). В частности, это может быть полезно при первоначальной загрузки некой схемы в виде команды, представляемой непосредственно в вашей командной строке при вызове оболочки MySQL, например:


shell> mysqlsh --user=myuser --py -e "concurrency_book.generate.load('world')"
		

Когда вы выполните загрузку необходимых вам схем, вы можете откликнуться пустым ответом для выхода. Теперь вы готовы выполнять свои рабочие нагрузки.

[Замечание]Замечание

Если в процессе загрузки происходит сбой, например по причине того что этот файл не является файлом ZIP, это означает что такой файл повреждён или неполный. В таком случае удалите этот файл и повторите выгрузку или попробуйте выгрузить этот файл вручную при помощи своего браузера.

Выполнение рабочей нагрузки

Вы исполняете рабочую нагрузку при помощи метода run(). Если вы определите название известной рабочей нагрузки, тогда такая рабочая нагрузка исполнится немедленно. В противном случае будут перечислены все доступные рабочие нагрузки и вы получите приглашение на ввод необходимой рабочей нагрузки. В этом случае вы можете определить такую рабочую нагрузку по её номеру (например, 15 для Листинга 6-1) или по её названию. При использовании названия значение числа пробелов между Listing и значение номера не принимается во внимание если имеется по крайней мере один пробел. Когда вы выбираете соответствующую нагрузку при помощи приглашения на ввод, после того ка завершится предыдущая, вы можете выбрать другую рабочую нагрузку.

После завершения выбранной рабочей нагрузки, для некоторых из таких рабочих потоков вам будет представлен перечень предложений для расследований, которые вы можете осуществлять. Например, это может быть запрос на блокировки, удерживаемые теми подключениями, которые применяются в этом примере. Эти расследования служат в качестве стимула и вам предлагается изучить эту рабочую нагрузку для применения своих собственных запросов. Некоторые из таких расследований также используются при обсуждении выбранного примера. Листинг 1-2 отображает некий пример выполнения рабочей нагрузки при помощи приглашения на ввод.

 

Листинг 1-2. Исполнение рабочей нагрузки при помощи приглашения на ввод


mysql-py> concurrency_book.generate.run()
Available workloads:
====================
 # Name                   Description
---------------------------------------------------------------------------
 1 Listing  2-1           Example use of the metadata_locks table
 2 Listing  2-2           Example of using the table_handles table
 3 Listing  2-3           Using the data_locks table
...
14 Listing  5-2           Example of obtaining exclusive locks
15 Listing  6-1           A deadlock for user-level locks
...
Choose workload (# or name - empty to exit): 15
Password for connections: ********
2020-07-20 20:50:41.666488  0 [INFO] Starting the workload Listing 6-1
****************************************************
*                                                  *
*   Listing 6-1. A deadlock for user-level locks   *
*                                                  *
****************************************************
-- Connection   Processlist ID   Thread ID   Event ID
-- --------------------------------------------------
--          1              105         249          6
--          2              106         250          6
-- Connection 1
Connection 1> SELECT GET_LOCK('my_lock_1', -1);
+---------------------------+
| GET_LOCK('my_lock_1', -1) |
+---------------------------+
|                         1 |
+---------------------------+
1 row in set (0.0003 sec)
-- Connection 2
Connection 2> SELECT GET_LOCK('my_lock_2', -1);
+---------------------------+
| GET_LOCK('my_lock_2', -1) |
+---------------------------+
|                         1 |
+---------------------------+
1 row in set (0.0003 sec)
Connection 2> SELECT GET_LOCK('my_lock_1', -1);
-- Connection 1
Connection 1> SELECT GET_LOCK('my_lock_2', -1);
ERROR: 3058: Deadlock found when trying to get user-level lock; try rolling back transaction/releasing locks and restarting lock acquisition.
Available investigations:
=========================
 # Query
--------------------------------------------------
 1 SELECT *
     FROM performance_schema.metadata_locks
    WHERE object_type = 'USER LEVEL LOCK'
          AND owner_thread_id IN (249, 250)
 2 SELECT thread_id, event_id, sql_text,
          mysql_errno, returned_sqlstate, message_text,
          errors, warnings
     FROM performance_schema.events_statements_history
    WHERE thread_id = 249 AND event_id > 6
    ORDER BY event_id
...
Choose investigation (# - empty to exit): 2
-- Investigation #2
-- Connection 3
Connection 3> SELECT thread_id, event_id, sql_text,
                     mysql_errno, returned_sqlstate, message_text,
                     errors, warnings
                FROM performance_schema.events_statements_history
               WHERE thread_id = 249 AND event_id > 6
               ORDER BY event_id\G
*************************** 1. row ***************************
        thread_id: 249
         event_id: 7
         sql_text: SELECT GET_LOCK('my_lock_1', -1)
      mysql_errno: 0
returned_sqlstate: NULL
     message_text: NULL
           errors: 0
         warnings: 0
*************************** 2. row ***************************
        thread_id: 249
         event_id: 8
         sql_text: SELECT GET_LOCK('my_lock_2', -1)
      mysql_errno: 3058
returned_sqlstate: HY000
     message_text: Deadlock found when trying to get user-level lock; try rolling back transaction/releasing locks and restarting lock acquisition.
           errors: 1
         warnings: 0
*************************** 3. row ***************************
        thread_id: 249
         event_id: 9
         sql_text: SHOW WARNINGS
      mysql_errno: 0
returned_sqlstate: NULL
     message_text: NULL
           errors: 0
         warnings: 0
3 rows in set (0.0009 sec)
Available investigations:
=========================
 # Query
--------------------------------------------------
...
Choose investigation (# - empty to exit):
2020-07-20 20:50:46.749971  0 [INFO] Completing the workload Listing 6-1
-- Connection 1
Connection 1> SELECT RELEASE_ALL_LOCKS();
+---------------------+
| RELEASE_ALL_LOCKS() |
+---------------------+
|                   1 |
+---------------------+
1 row in set (0.0004 sec)
-- Connection 2
Connection 2> SELECT RELEASE_ALL_LOCKS();
+---------------------+
| RELEASE_ALL_LOCKS() |
+---------------------+
|                   2 |
+---------------------+
1 row in set (0.0002 sec)
2020-07-20 20:50:46.749971  0 [INFO] Disconnecting for the workload Listing 6-1
2020-07-20 20:50:46.749971  0 [INFO] Completed the workload Listing 6-1
Available workloads:
====================
 # Name                   Description
---------------------------------------------------------------------------
 1 Listing  2-1           Example use of the metadata_locks table
 2 Listing  2-2           Example of using the table_handles table
 3 Listing  2-3           Using the data_locks table
...
Choose workload (# or name - empty to exit):
mysql-py>
 	   

Имеется несколько моментов, на которые стоит обратить внимание. После выбора соответствующей рабочей нагрузки у вас запрашивается пароль. Это пароль для учётной записи MySQL, которой вы пользуетесь. Прочие параметры подключения берутся из свойства session.uri в Оболочке MySQL, однако по причинам безопасности значение пароля не хранится. Если вы в одном обращении к run() вы исполняете множество рабочих нагрузок, приглашение на ввод пароля от вас будет выдано лишь один раз.

В самом начале выполнения такой рабочей нагрузки имеется некий обзор списка значений идентификаторов процессов ( как из SHOW PROCESSLIST), значения идентификаторы потоков и идентификаторы значений последнего события прежде чем запустится соответствующая рабочая нагрузка для каждого используемого ею подключения:


-- Connection   Processlist ID   Thread ID   Event ID
-- --------------------------------------------------
--          1              105         249          6
--          2              106         250          6
 	   

Вы можете применять эти идентификаторы для выполнения своих собственных запросов расследования и вы можете применять это обзор для идентификации листингов, которые были реализованы в некой рабочей нагрузке в concurrency_book.generate.run().

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


-- Investigation #2
 	   

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

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

Если же вы желаете выполнить единственную нагрузку, вы можете определить её название в качестве параметра для run(). Это, в частности, может быть полезно при выполнении некой рабочей нагрузки в качестве непосредственно задаваемой команды в своей командной строке при обращении к Оболочке MySQL, например:


shell> mysqlsh --user=myuser --py -e "concurrency_book.generate.run('Listing 6-1')"
		

Остаток главы описывает те три схемы, которые применяются для всех примеров в данной книге.

Данные проверки: Схема мира

Пример базы данных world это одна из наиболее распространённых баз данных под простое тестирование. Она состоит из трёх таблиц со строками от нескольких сотен до нескольких тысяч. Это создаёт небольшой набор данных, что означает, что он запросто может применяться даже в небольших экземплярах проверки.

Схема

Эта база данных состоит из таблиц city, country и countrylanguage. Взаимосвязи между этими таблицами показаны на Рисунке 1-3.

 

Рисунок 1-3


База данных world

Таблица country содержит сведения относительно 239 стран и служит родительской таблицей во внешних ключах из таблиц city и countrylanguage. В общей сложности имеется 4079 городов и 984 комбинаций стран и языков.

Установка

Вы можете выгрузить файл с определениями таблицы и данными с https://dev.mysql.com/doc/index-other.html. С этой страницы Oracle предоставляет доступ к нескольким примерам баз данных в разделе Example Databases, как это показано на Рисунке 1-4.

 

Рисунок 1-4


Таблица со ссылками на примеры баз данных

Выгружаемый файл состоит из единственного файла с названием world.sql.gz или world.sql.zip, в зависимости от того выбираете ли вы ссылку Gzip или ZIP. В любом случае выгружаемый архив содержит файл world.sql. Собственно установка достаточно прямолинейна, ибо всё что требуется, так это исполнение данного сценария.

Вы можете указать источником world.sql либо из Оболочки MySQL, либо из командной строки mysql клиента. Из Оболочки MySQL для загрузки всех данных вы пользуетесь командой \source:


MySQL [localhost ssl] SQL> \source world.sql
		

Когда вы применяете наследуемого клиента командной строки mysql, вместо этого примените команду SOURCE:


mysql> SOURCE world.sql
		

В любом случае добавьте значен е пути к файлу world.sql когда он не расположен в том каталоге, из которого вы запускаете Оболочку MySQL или mysql.

Если вы предпочитаете применять графический интерфейс, тогда вы имеете также возможность запуска базы данных world при помощи Рабочего места MySQL. При наличии подключения к своему экземпляру MySQL, в который вы желаете загрузить обсуждаемую схему world, вы кликаете по File в меню, следующем за Run SQL Script , как это показано на Рисунке 1-5.

 

Рисунок 1-5


Запуск сценария SQL с рабочего места MySQL

Это открывает проводник файлов, в котором вы можете отыскать искомый файл. Переместитесь в тот каталог, в котором вы сохранили раскрытый файл world.sql и выберите его. Получаемый в результате диалог отображён на Рисунке 1-6 в котором вы можете просмотреть самую первую часть этого сценария и по желанию установить значение названия схемы по умолчанию и набор символов.

 

Рисунок 1-6


Диалог в рабочем месте MySQL для просмотра тескста сценария

В случае схемы world и название необходимой схемы и набор символов уже включены в этом сценарии, а потому нет необходимости (и это не окажет никакого воздействия) изменять эти настройки. Кликните по Run для исполнения данного сценария. при исполнении этого сценария MySQL диалог отобразит сведения по мере развития событий. Когда это действие завершится, закройте данный диалог. По желанию вы можете обновить перечень схем в боковой врезке кликнув по двум преследующим друг друга стрелкам, как это показано на Рисунке 1-7.

 

Рисунок 1-7


Обновление списка схем через клик по двум стрелкам

В то время как схема world великолепна при большинстве проверок по причине простоты и малого размера, эти обстоятельства также ограничивают её пользу и требуются какие- то слегка более сложные базы данных.

Данные проверки: Схема sakila

База данных sakila это правдоподобная база данных, которая содержит схему ведения дел проката фильмов со сведениями о самих фильмах, описью, историями, персоналом и потребителями. Это добавляет полнотекстовую индексацию, некую частичную индексацию, представления и сохранённые программы для предоставления более сложных примеров использования функциональных возможностей MySQL. Значения размера базы данных всё ещё очень умеренные, что делает её пригодной для небольших экземпляров.

Схема

База данных sakila состоит из 16 таблиц, семи представлений, трёх сохранённых процедур, трёх сохранённых функций и шести триггеров. Все таблицы могут быть разбиты на три группы, данные потребителя, ведение дел и опись. Для краткости в наши схемы включены не все столбцы и не отображено большинство индексов. Рисунок 1-8 показывает полный обзор имеющихся таблиц, представлений и сохранённых подпрограмм.

 

Рисунок 1-8


Обзор базы данных sakila

Все таблицы с относящимися к потребителям сведениями (плюс адреса для персонала и хранилищ) расположены в области верхнего левого угла. Область внизу слева содержит относящиеся к ведению дел сведения, а область справа вверху состоит из информации относительно фильмов и учёта. Низ справа применяется для имеющихся представлений и сохранённых подпрограмм.

[Совет]Совет

Вы можете просмотреть всю схему целиком (хотя и представленную в ином формате) раскрыв файл sakila.mwb, включённый в саму установку в Рабочем месте MySQL. Это также хороший образец того как вы можете применять расширенные схемы взаимосвязей логических элементов (EER, enhanced entity-relationship) в Рабочем месте MySQL для документирования своей схемы.

Поскольку имеется достаточно большое число объектов, при обсуждении данной схемы их можно разделить на пять групп (каждую из групп таблиц, представления и сохранённые подпрограммы). Первая группа это относящиеся к потребителю данные с отображаемыми на Рисунке 1-9 таблицами.

 

Рисунок 1-9


Таблицы с данными потребителя в базе данных sakila

Для относящихся к потребителям сведений имеются четыре таблицы. Главной таблицей выступает таблица потребителей (customer), а информация об адресах хранится в таблицах адресов (address), городов (city) и стран (country).

Имеются внешние ключи между потребителями и бизнес группами с внешним ключом из таблицы потребителя в таблицу хранилища в бизнес группе. Также имеются четыре внешних ключа из таблиц в бизнес группе к таблицам адресов и потребителей. Бизнес группа отображена на Рисунке 1-10.

 

Рисунок 1-10


Таблицы с данными ведения дел в базе данных sakila

Таблица ведения дел (business) содержит сведения относительно хранилищ, персонала, проката и платежей. Таблицы хранилища (store) и персонала (stuff) обладают внешними ключами в обоих направлениях, причём персонал относится к хранилищу, а хранилище обладает управляющим, который состоит в персонале. Прокат (rental) и платежи (payment) обрабатываются составом из персонала и тем самым косвенным образом связаны с хранилищем, а также платежи относятся к прокату.

Группа таблиц ведения дел это одна из большинства взаимосвязей с прочими группами. Таблицы персонала и хранилищ обладают внешними ключами к таблице адресов, а таблицы проката и платежей связаны м потребителями. наконец, таблица проката обладает неким внешним ключом в таблице описи (inventory), которая пребывает в группе учёта. Схема для этой группы учёта отражена на Рисунке 1-11.

 

Рисунок 1-11


Таблицы с данными описи дел в базе данных sakila

Основная таблица в группе учёта (описи, inventory) это таблица фильмов (film), которая содержит необходимые метаданные относительно всех предлагаемых хранилищами фильмов. Кроме того, имеется таблица film_text с заголовком и описанием при полнотекстовой индексации.

Между таблицами фильмов (film), категории (category) и актёров (actor) имеются взаимоотношения многие- ко- многим. Наконец, существует внешний ключ от таблицы описи к таблице хранилища из группы ведения дел.

Это описывает все таблицы в базе данных sakila, но также имеется и ряд представлений, как это отображено на Рисунке 1-12.

 

Рисунок 1-12


Представления в базе данных sakila

Эти представления могут применяться в качестве отчётов и их можно подразделить на две категории. film_list, nicer_but_slower_film_list и actor_info относятся к хранимым в базе данных фильмам. Вторая категория содержит сведения, относящиеся к хранилищам в представлениях sales_by_store, sales_by_film_category, staff_list и customer_list.

Чтобы покончить с этой базой данных, стоит отметить, что также имеются и сохранённые функции и процедулы, показанные на Рисунке 1-13.

 

Рисунок 1-13


Сохранённые процедуры в базе данных sakila

Процедуры film_in_stock() и film_not_in_stock() возвращают набор результатов, составленный из идентификаторов описи для заданного фильма и хранилища на основе того присутствует такой фильм на складе или нет. Общее число записей описи возвращается как параметр вывода. Процедура rewards_report() вырабатывает некий отчёт на основании минимума трат времени за последний месяц.

Функция get_customer_balance() возвращает значение баланса для определённого пользователя на заданную дату. Две оставшиеся функции проверяют значение состояния некого идентификатора описи с inventory_held_by_customer(), возвращающим идентификатор потребителя в настоящее время взявшего в прокат этот элемент (и NULL если никакой потребитель не арендует его), а если вы желаете проверить имеется ли определённый идентификатор описи на складе, вы можете воспользоваться функцией inventory_in_stock().

Установка

Вы можете выгрузить файл со сценариями установки для инсталляции схемы sakila из https://dev.mysql.com/doc/index-other.html как и для базы данных world.

Выгруженный файл распаковывается в некий каталог с тремя файлами, два из которых создают необходимую схему и данные, а последний файл содержит схему ETL в формате, применяемом Рабочим местом MySQL.

[Замечание]Замечание

База данных sakila также доступна с выгрузкой базы данных employees; тем не менее, этот раздел и все приводимые далее в этой книге примеры пользуются копией базы данных sakila, которая выгружена с домашней страницы MySQL.

Вот эти файлы:

  • sakila-data.sql: Для заполнения всех таблиц и определения необходимых триггеров требуются операторы INSERT.

  • sakila-schema.sql: Операторы определения данной схемы.

  • sakila.mwb: ETLсхема абочего места MySQL. Она аналогична тому, что отображено на Рисунке 1-7 с детализацией в Рисунках с 1-8 по 1-12.

Вы устанавливаете собственно базу данных sakila определяя источником файл sakila-schema.sql, а затем файл sakila-data.sql. Например ниже приводится использование Оболочки MySQL:


MySQL [localhost+ ssl] SQL> \source sakila-schema.sql
MySQL [localhost+ ssl] SQL> \source sakila-data.sql
		

Когда необходимые файлы расположены не в текущем каталоге, добавьте пути к этим файлам.

Данные проверки: Схема служащих

База данных employees (вызывающая сведения о служащих на странице выгрузки документации самого MySQL; её название в репозитории GitHub test_db) первоначально была создана Фушенгом Ванг и Карло Дзаньоло и это самый крупный набор данных из присоединённых к домашней странице MySQL. Она поставляется с выбором применения не разбитых на части таблиц или с разбитыми на части двумя из крупнейших таблиц. Общий размер файлов данных составляет около 189 MiB для версии без разделов и 440 MiB для версии с разделами.

Схема

База данных employees состоит из шести таблиц и двух представлений. Вы можете по своему усмотрению установить два дополнительных представления, пять сохранённых функций и две сохранённые процедуры. Все таблицы показаны на Рисунке 1-14.

 

Рисунок 1-14


Таблицы, представления и процедуры в базе данных employees

По стандартам текущего дня это всё ещё относительно небольшой объём данных для базы данных, но он достаточно велик чтобы вы могли начать рассматривать состязательность на нижнем уровне, а по этой причине именно эта схема применяется для вызова ожиданий семафоров в Главе 18.

Установка

Вы можете выгрузить файл ZIP со всеми необходимыми для вашей установки файлами или вы можете клонировать репозиторий GitHub по адресуhttps://github.com/datacharmer/test_db. На момент написания книги имелась лишь одна ветвь с названием master. Если выгрузите необходимый файл ZIP, он будет распакован в каталог с названием test_db-master.

Имеется несколько файлов. Двумя относящимися к установке базы данных employees в MySQL 8 выступают employees.sql и employees_partitioned.sql. Основное отличие состоит в том разбиты ли на разделы таблицы salaries и titles. Данная книга пользуется схемой без разбиения на разделы. (Также существует employees_partitioned_5.1.sql, которая подразумевает MySQL 5.1, в которой не поддерживается схема с разделами, применяемая в employees_partitioned.sql.)

Все данные загружаются установкой источника на файлы .dump при помощи команды SOURCE, которая поддерживается в Оболочке MySQL 8.0.19 (по причине ошибки, на практике 8.0.20) и выше. Перейдите в каталог с необходимыми файлами источника и выберите файл employees.sql или employees_partitioned.sql, в зависимости от того, желаете ли вы применять разбиение на раделы или нет, например:


mysql> \source employees.sql
		

Импорт займёт немного времени и завершится демонстрацией того сколько его потребовалось:


+---------------------+
| data_load_time_diff |
+---------------------+
| 00:02:50            |
+---------------------+
1 row in set (0.0085 sec)
 	   

В случае желания, вы можете загрузить дополнительные представления и сохранённые подпрограммы поставим источником файл objects.sql:


mysql> \source objects.sql
		

Когда вы загружаете схему employees с применением метода concurrency_book.generate.load(), этот файл objects.sql всегда входит в состав загружаемых.

Теперь вы готовы окунуться в мир одновременности MySQL.

Выводы

Эта глава начинает ваше путешествие в осознание одновременности MySQL, для чего важными темами выступают блокировки и транзакции. Вначале она обсудила зачем нужны блокировки и на каких уровнях они имеются. Затем было рассмотрено, что следует включить в обсуждение транзакции, поскольку некоторые блокировки удерживаются на протяжении всей транзакции и значение уровня изоляции транзакции влияет на величину продолжительности такой блокировки, а также на значение числа блокировок.

Остаток главы обсуждал как в этой книге применяются примеры и ввёл три набора данных для проверки, которые требуются для воспроизводства вариантов использования. Для упрощения загрузки необходимых данных и выполнения вариантов использования также был введён модуль concurrency_book.generate для Оболочки MySQL.

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