Приложение на Python
На этой странице подробно разбирается код тестового приложения, доступного в составе Python SDK YDB.
Скачивание и запуск
Приведенный ниже сценарий запуска использует git и Python3. Предварительно должен быть установлен YDB Python SDK.
Создайте рабочую директорию и выполните в ней из командной строки команды клонирования репозитория с github.com и установки необходимых пакетов Python:
git clone https://github.com/ydb-platform/ydb-python-sdk.git
python3 -m pip install iso8601
Далее из этой же рабочей директории выполните команду запуска тестового приложения, которая будет отличаться в зависимости от того, к какой базе данных необходимо подключиться.
Для соединения с развернутой локальной базой данных YDB по сценарию Docker в конфигурации по умолчанию выполните следующую команду:
YDB_ANONYMOUS_CREDENTIALS=1 \
python3 ydb-python-sdk/examples/basic_example_v1/ -e grpc://localhost:2136 -d /local
Для выполнения примера с использованием любой доступной базы данных YDB вам потребуется знать эндпоинт и путь базы данных.
Если в базе данных включена аутентификация, то вам также понадобится выбрать режим аутентификации и получить секреты - токен или логин/пароль.
Выполните команду по следующему образцу:
<auth_mode_var>="<auth_mode_value>" \
python3 ydb-python-sdk/examples/basic_example_v1/ -e <endpoint> -d <database>
, где
<endpoint>
- эндпоинт.<database>
- путь базы данных.<auth_mode_var
> - переменная окружения, определяющая режим аутентификации.<auth_mode_value>
- значение параметра аутентификации для выбранного режима.
Например:
YDB_ACCESS_TOKEN_CREDENTIALS="t1.9euelZqOnJuJlc..." \
python3 ydb-python-sdk/examples/basic_example_v1/ -e grpcs://ydb.example.com:2135 -d /path/db )
Инициализация соединения с базой данных
Для взаимодействия с YDB создается экземпляр драйвера, клиента и сессии:
- Драйвер YDB отвечает за взаимодействие приложения и YDB на транспортном уровне. Драйвер должен существовать на всем протяжении жизненного цикла работы с YDB и должен быть инициализирован перед созданием клиента и сессии.
- Клиент YDB работает поверх драйвера YDB и отвечает за работу с сущностями и транзакциями.
- Сессия YDB содержит информацию о выполняемых транзакциях и подготовленных запросах и содержится в контексте клиента YDB.
Фрагмент кода приложения для инициализации драйвера:
def run(endpoint, database, path):
driver_config = ydb.DriverConfig(
endpoint, database, credentials=ydb.credentials_from_env_variables(),
root_certificates=ydb.load_ydb_root_certificate(),
)
with ydb.Driver(driver_config) as driver:
try:
driver.wait(timeout=5)
except TimeoutError:
print("Connect failed to YDB")
print("Last reported errors by discovery:")
print(driver.discovery_debug_details())
exit(1)
Фрагмент кода приложения для создания сессии:
session = driver.table_client.session().create()
Создание таблиц
Выполняется создание таблиц, которые используются в дальнейших операциях тестового приложения. В результате исполнения шага в базе данных будут созданы таблицы модели данных справочника сериалов:
series
- Сериалыseasons
- Сезоныepisodes
- Эпизоды
После создания вызывается метод получения информации об объекте схемы данных, и выводится результат его выполнения.
Для создания таблиц используется метод session.create_table()
:
def create_tables(session, path):
session.create_table(
os.path.join(path, 'series'),
ydb.TableDescription()
.with_column(ydb.Column('series_id', ydb.PrimitiveType.Uint64)) # not null column
.with_column(ydb.Column('title', ydb.OptionalType(ydb.PrimitiveType.Utf8)))
.with_column(ydb.Column('series_info', ydb.OptionalType(ydb.PrimitiveType.Utf8)))
.with_column(ydb.Column('release_date', ydb.OptionalType(ydb.PrimitiveType.Uint64)))
.with_primary_key('series_id')
)
В параметр path передаётся абсолютный путь от корня:
full_path = os.path.join(database, path)
С помощью метода session.describe_table()
можно вывести информацию о структуре таблицы и убедиться, что она успешно создалась:
def describe_table(session, path, name):
result = session.describe_table(os.path.join(path, name))
print("\n> describe table: series")
for column in result.columns:
print("column, name:", column.name, ",", str(column.type.item).strip())
Приведенный фрагмент кода при запуске выводит на консоль текст:
> describe table: series
('column, name:', 'series_id', ',', 'type_id: UINT64')
('column, name:', 'title', ',', 'type_id: UTF8')
('column, name:', 'series_info', ',', 'type_id: UTF8')
('column, name:', 'release_date', ',', 'type_id: UINT64')
Запись данных
Выполняется запись данных в созданные таблицы с использованием команды UPSERT
языка запросов YQL. Применяется режим передачи запроса на изменение данных с автоматическим подтверждением транзакции в одном запросе к серверу.
Фрагмент кода, демонстрирующий выполнение запроса на запись/изменение данных:
def upsert_simple(session, path):
session.transaction().execute(
"""
PRAGMA TablePathPrefix("{}");
UPSERT INTO episodes (series_id, season_id, episode_id, title) VALUES
(2, 6, 1, "TBD");
""".format(path),
commit_tx=True,
)
PRAGMA TablePathPrefix
добавляет указанный префикс к путям таблиц внутри БД. Работает по принципу объединения путей в файловой системе — поддерживает ссылки на родительский каталог и не требует добавления слеша справа. Например:
PRAGMA TablePathPrefix = "/cluster/database";
SELECT * FROM episodes;
Подробнее о PRAGMA YQL можно прочитать в документации YQL.
Получение выборки данных
Выполняется запрос на получение выборки данных с использованием команды SELECT
языка запросов YQL. Демонстрируется обработка полученной выборки в приложении.
Для выполнения YQL-запросов используется метод session.transaction().execute()
.
SDK позволяет в явном виде контролировать выполнение транзакций и настраивать необходимый режим выполнения транзакций с помощью класса TxControl
.
В фрагменте кода, приведенном ниже, транзакция выполняется с помощью метода transaction().execute()
. Устанавливается режим выполнения транзакции ydb.SerializableReadWrite()
. После завершения всех запросов транзакции она будет автоматически завершена с помощью явного указания флага: commit_tx=True
. Тело запроса описано с помощью синтаксиса YQL и как параметр передается методу execute
.
def select_simple(session, path):
result_sets = session.transaction(ydb.SerializableReadWrite()).execute(
"""
PRAGMA TablePathPrefix("{}");
$format = DateTime::Format("%Y-%m-%d");
SELECT
series_id,
title,
$format(DateTime::FromSeconds(CAST(DateTime::ToSeconds(DateTime::IntervalFromDays(CAST(release_date AS Int16))) AS Uint32))) AS release_date
FROM series
WHERE series_id = 1;
""".format(path),
commit_tx=True,
)
print("\n> select_simple_transaction:")
for row in result_sets[0].rows:
print("series, id: ", row.series_id, ", title: ", row.title, ", release date: ", row.release_date)
return result_sets[0]
В качестве результата выполнения запроса возвращается result_set
, итерирование по которому выводит на консоль текст:
> SelectSimple:
series, Id: 1, title: IT Crowd, Release date: 2006-02-03
Параметризованные подготовленные запросы
Параметризованные подготовленные запросы (prepared queries) записываются в форме шаблона, в котором определенного вида имена заменяются конкретными параметрами при каждом выполнении запроса. Использование параметризованных запросов может улучшить производительность за счет сокращения частоты выполнения компиляции и перекомпиляции запросов, отличающихся только значениями параметров. Подготовленный запрос хранится в контексте сессии.
Фрагмент кода, демонстрирующий возможность использования параметризованных подготовленных запросов:
def select_prepared(session, path, series_id, season_id, episode_id):
query = """
PRAGMA TablePathPrefix("{}");
DECLARE $seriesId AS Uint64;
DECLARE $seasonId AS Uint64;
DECLARE $episodeId AS Uint64;
$format = DateTime::Format("%Y-%m-%d");
SELECT
title,
$format(DateTime::FromSeconds(CAST(DateTime::ToSeconds(DateTime::IntervalFromDays(CAST(air_date AS Int16))) AS Uint32))) AS air_date
FROM episodes
WHERE series_id = $seriesId AND season_id = $seasonId AND episode_id = $episodeId;
""".format(path)
prepared_query = session.prepare(query)
result_sets = session.transaction(ydb.SerializableReadWrite()).execute(
prepared_query, {
'$seriesId': series_id,
'$seasonId': season_id,
'$episodeId': episode_id,
},
commit_tx=True
)
print("\n> select_prepared_transaction:")
for row in result_sets[0].rows:
print("episode title:", row.title, ", air date:", row.air_date)
return result_sets[0]
Приведенный фрагмент кода при запуске выводит на консоль текст:
> select_prepared_transaction:
('episode title:', u'To Build a Better Beta', ', air date:', '2016-06-05')
Скан запросы
Выполняется скан запрос данных, результатом исполнения которого является стрим. Стрим позволяет считать неограниченное количество строк и объем данных.
def executeScanQuery(driver):
query = ydb.ScanQuery("""
SELECT series_id, season_id, COUNT(*) AS episodes_count
FROM episodes
GROUP BY series_id, season_id
ORDER BY series_id, season_id
""", {})
it = driver.table_client.scan_query(query)
while True:
try:
result = next(it)
print result.result_set.rows
except StopIteration:
break
Управление транзакциями
Выполняются вызовы операторов управления транзакциями TCL - Begin и Commit.
В большинстве случаев вместо явного использования вызовов Begin и Commit лучше использовать параметры контроля транзакций в вызовах execute. Это позволит избежать лишних обращений к YDB и эффективней выполнять запросы.
Фрагмент кода, демонстрирующий явное использование вызовов transaction().begin()
и tx.Commit()
:
def explicit_tcl(session, path, series_id, season_id, episode_id):
query = """
PRAGMA TablePathPrefix("{}");
DECLARE $seriesId AS Uint64;
DECLARE $seasonId AS Uint64;
DECLARE $episodeId AS Uint64;
UPDATE episodes
SET air_date = CAST(CurrentUtcDate() AS Uint64)
WHERE series_id = $seriesId AND season_id = $seasonId AND episode_id = $episodeId;
""".format(path)
prepared_query = session.prepare(query)
tx = session.transaction(ydb.SerializableReadWrite()).begin()
tx.execute(
prepared_query, {
'$seriesId': series_id,
'$seasonId': season_id,
'$episodeId': episode_id
}
)
print("\n> explicit TCL call")
tx.commit()