← Cloudflare D1 / d1 / best-practices
Глобальная репликация чтения
Репликация для чтения D1 может снижать задержку для запросов на чтение и повышать пропускную способность чтения, добавляя доступные только для чтения копии базы данных, называемые репликами для чтения, в разных регионах ближе к клиентам.
Чтобы использовать репликацию для чтения, необходимо использовать D1 Sessions API, иначе все запросы по-прежнему будут выполняться только основной базой данных.
Сессия объединяет все запросы одной логической сессии вашего приложения. Например, сессия может соответствовать всем запросам, поступающим из определённой сессии веб-браузера. Все запросы в рамках сессии читаются из экземпляра базы данных, который настолько актуален, насколько это нужно вашему запросу. Sessions API гарантирует последовательная согласованность для всех запросов в рамках сессии.
Чтобы опробовать репликацию чтения D1, разверните следующий код Worker с использованием Sessions API. Он предложит вам создать базу данных D1 и включить для неё репликацию чтения.
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
// A. Create the Session.
// When we create a D1 Session, we can continue where we left off from a previous
// Session if we have that Session's last bookmark or use a constraint.
const bookmark =
request.headers.get("x-d1-bookmark") ?? "first-unconstrained";
const session = env.DB01.withSession(bookmark);
try {
// Use this Session for all our Workers' routes.
const response = await withTablesInitialized(
request,
session,
handleRequest,
);
// B. Return the bookmark so we can continue the Session in another request.
response.headers.set("x-d1-bookmark", session.getBookmark() ?? "");
return response;
} catch (e) {
console.error({
message: "Failed to handle request",
error: String(e),
errorProps: e,
url,
bookmark,
});
return Response.json(
{ error: String(e), errorDetails: e },
{ status: 500 },
);
}
},
};export default {
async fetch(request, env, ctx): Promise<Response> {
const url = new URL(request.url);
// A. Create the Session.
// When we create a D1 Session, we can continue where we left off from a previous
// Session if we have that Session's last bookmark or use a constraint.
const bookmark =
request.headers.get("x-d1-bookmark") ?? "first-unconstrained";
const session = env.DB01.withSession(bookmark);
try {
// Use this Session for all our Workers' routes.
const response = await withTablesInitialized(
request,
session,
handleRequest,
);
// B. Return the bookmark so we can continue the Session in another request.
response.headers.set("x-d1-bookmark", session.getBookmark() ?? "");
return response;
} catch (e) {
console.error({
message: "Failed to handle request",
error: String(e),
errorProps: e,
url,
bookmark,
});
return Response.json(
{ error: String(e), errorDetails: e },
{ status: 500 },
);
}
},
} satisfies ExportedHandler<Env>;Основной экземпляр базы данных в сравнении с репликами для чтения
При использовании D1 без репликации чтения все запросы (на чтение и на запись) направляются к конкретному экземпляру базы данных в одно место в мире, что известно как основной экземпляр базы данных . Задержка запросов D1 зависит от физической удалённости пользователя от основного экземпляра базы данных. Пользователи, находящиеся дальше от основного экземпляра базы данных, испытывают более высокую задержку запросов из-за время кругового пути по сети ↗.
При использовании репликации чтения D1 создает несколько асинхронно реплицируемых копий основного экземпляра базы данных, обслуживающих только запросы на чтение и называемых реплики для чтения . D1 создаёт реплики для чтения в несколько регионов по всему миру в сети Cloudflare.
Даже если пользователь находится далеко от основного экземпляра базы данных, он может оказаться рядом с репликой для чтения. Когда D1 направляет запросы на чтение к реплике для чтения вместо основного экземпляра базы данных, пользователь получает более быстрые ответы на свои запросы на чтение.
D1 асинхронно реплицирует изменения из основного экземпляра базы данных во все реплики для чтения. Это означает, что в любой момент времени реплика для чтения может быть в разной степени неактуальной. Время, которое требуется для того, чтобы последние зафиксированные данные из основного экземпляра базы данных были реплицированы в реплику для чтения, называется отставание реплик . Отставание реплик и недетерминированная маршрутизация к отдельным репликам могут приводить к проблемам согласованности данных приложения. D1 Sessions API решает эту проблему, обеспечивая последовательную согласованность. Дополнительная информация: отставание реплик и модель согласованности.
| Тип экземпляра базы данных | Описание | Как это влияет на обработку запросов на запись | Как это влияет на обработку запросов на чтение |
|---|---|---|---|
| Основной экземпляр базы данных | Экземпляр базы данных, содержащий «оригинальную» копию базы данных | Может обслуживать запросы на запись | Может обслуживать запросы на чтение |
| Экземпляр базы данных, являющийся репликой для чтения | Экземпляр базы данных, содержащий копию исходной базы данных, который асинхронно получает обновления от основного экземпляра базы данных | Перенаправляет любые запросы на запись в основной экземпляр базы данных | Может обслуживать запросы на чтение, используя собственную копию базы данных |
Преимущества репликации чтения
Система с несколькими репликами для чтения, размещёнными по всему миру, повышает производительность баз данных:
- Задержка запросов снижается для пользователей, расположенных близко к репликам для чтения. За счёт сокращения физического расстояния между экземпляром базы данных и пользователем задержка запросов на чтение уменьшается, что ускоряет работу приложения.
- Пропускная способность чтения увеличивается за счёт распределения нагрузки между несколькими репликами. Поскольку несколько экземпляров базы данных могут обрабатывать запросы только на чтение, приложение способно обслуживать большее количество запросов одновременно.
Использование Sessions API
Используя Sessions API для репликации чтения все ваши запросы из одного Сессионное чтение выполняется из версии базы данных, которая обеспечивает последовательную согласованность. Это гарантирует, что читаемая вами версия базы данных логически согласована, даже если запросы обрабатываются разными репликами для чтения.
Репликация для чтения D1 достигает этого, подключая bookmark к каждому запросу в рамках сессии. Дополнительную информацию см. в Bookmarks.
Включение репликации чтения
Репликацию для чтения можно включить на уровне базы данных в Cloudflare dashboard. Ознакомьтесь с Настройки вашей базы данных D1, чтобы проверить, включена ли репликация чтения.
-
На панели управления Cloudflare перейдите к разделу D1 страницу.
Перейдите в SQL-база данных D1 ↗ -
Выберите существующую базу данных > Настройки > Enable Read Replication.
Запуск сессии без ограничений
Чтобы создать сессию на основе любой доступной версии базы данных, используйте withSession() без каких-либо параметров, в этом случае первый запрос будет направлен к любому экземпляру базы данных: либо к основному экземпляру базы данных, либо к реплике для чтения.
const session = env.DB.withSession() // synchronous
// query executes on either primary database or a read replica
const result = await session
.prepare(`SELECT * FROM Customers WHERE CompanyName = 'Bs Beverages'`)
.run()withSession()совпадает сwithSession("first-unconstrained")- Этот подход лучше всего подходит, если приложению не требуется самая последняя версия базы данных. Все запросы в рамках сессии обеспечивают последовательную согласованность.
- См. Документация по D1 Workers Binding API.
Запуск сессии с самыми последними данными
Чтобы создать сессию на основе последней версии базы данных, используйте withSession("first-primary"), которое направит первый запрос к основному экземпляру базы данных.
const session = env.DB.withSession(`first-primary`) // synchronous
// query executes on primary database
const result = await session
.prepare(`SELECT * FROM Customers WHERE CompanyName = 'Bs Beverages'`)
.run()- Этот подход лучше всего подходит, если приложению требуется самая последняя версия базы данных. Все запросы в рамках сессии обеспечивают последовательную согласованность.
- См. Документация по D1 Workers Binding API.
Запуск сессии из предыдущего контекста (bookmark)
Чтобы создать новую сессию в контексте предыдущей сессии, передайте bookmark параметр, чтобы гарантировать, что сессия начнётся с версии базы данных, не менее актуальной, чем указанный bookmark.
// retrieve bookmark from previous session stored in HTTP header
const bookmark = request.headers.get('x-d1-bookmark') ?? 'first-unconstrained';
const session = env.DB.withSession(bookmark)
const result = await session
.prepare(`SELECT * FROM Customers WHERE CompanyName = 'Bs Beverages'`)
.run()
// store bookmark for a future session
response.headers.set('x-d1-bookmark', session.getBookmark() ?? "")- Запуск сессии с
bookmarkгарантирует, что новая сессия будет не менее актуальной, чем предыдущая сессия, создавшая указанныйbookmark. - См. Документация по D1 Workers Binding API.
Проверьте, где был обработан запрос D1
Чтобы увидеть, как добавление реплик для чтения влияет на обработку запросов D1, served_by_region и served_by_primary поля возвращаются в meta объект D1 Result.
const result = await env.DB.withSession()
.prepare(`SELECT * FROM Customers WHERE CompanyName = 'Bs Beverages'`)
.run();
console.log({
servedByRegion: result.meta.served_by_region ?? "",
servedByPrimary: result.meta.served_by_primary ?? "",
});served_by_regionиserved_by_primaryполя присутствуют во всех удалённых запросах D1, независимо от того, включена ли репликация чтения или используется ли Sessions API. При локальной разработкеnpx wrangler dev, эти поляundefined.
Включение репликации чтения через REST API
В REST API задайте read_replication.mode: auto чтобы включить репликацию для чтения в базе данных D1.
Для этой конечной точки REST вам потребуется API-токен с D1:Edit разрешение. Если у вас нет токена API, следуйте руководству: Создание API-токена.
curl -X PUT "https://api.cloudflare.com/client/v4/accounts/{account_id}/d1/database/{database_id}" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"read_replication": {"mode": "auto"}}'const headers = new Headers({
"Authorization": `Bearer ${TOKEN}`
});
await fetch ("/v4/accounts/{account_id}/d1/database/{database_id}", {
method: "PUT",
headers: headers,
body: JSON.stringify(
{ "read_replication": { "mode": "auto" } }
)
}
)Отключение репликации чтения через REST API
В REST API задайте read_replication.mode: disabled чтобы отключить репликацию для чтения в базе данных D1.
Для этой конечной точки REST вам потребуется API-токен с D1:Edit разрешение. Если у вас нет токена API, следуйте руководству: Создание API-токена.
curl -X PUT "https://api.cloudflare.com/client/v4/accounts/{account_id}/d1/database/{database_id}" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"read_replication": {"mode": "disabled"}}'const headers = new Headers({
"Authorization": `Bearer ${TOKEN}`
});
await fetch ("/v4/accounts/{account_id}/d1/database/{database_id}", {
method: "PUT",
headers: headers,
body: JSON.stringify(
{ "read_replication": { "mode": "disabled" } }
)
}
)Проверьте, включена ли репликация чтения
В панели управления Cloudflare проверьте Настройки вашей базы данных D1, чтобы проверить, включена ли репликация чтения.
Как вариант, GET REST конечная точка базы данных D1 возвращает информацию о том, включена репликация для чтения или отключена.
Для этой конечной точки REST вам потребуется API-токен с D1:Read разрешение. Если у вас нет токена API, следуйте руководству: Создание API-токена.
curl -X GET "https://api.cloudflare.com/client/v4/accounts/{account_id}/d1/database/{database_id}" \
-H "Authorization: Bearer $TOKEN"const headers = new Headers({
"Authorization": `Bearer ${TOKEN}`
});
const response = await fetch("/v4/accounts/{account_id}/d1/database/{database_id}", {
method: "GET",
headers: headers
});
const data = await response.json();
console.log(data.read_replication.mode);- Проверьте
read_replicationсвойство объектаresultобъект"mode": "auto"означает, что репликация чтения включена"mode": "disabled"означает, что репликация чтения отключена
Расположения реплик для чтения
В настоящее время D1 автоматически создает реплику для чтения в каждый поддерживаемый регион, включая регион, в котором расположен основной экземпляр базы данных. Это следующие регионы:
- ENAM
- WNAM
- WEUR
- EEUR
- APAC
- OC
Observability
Чтобы оценить влияние репликации для чтения и проверить, как запросы D1 обрабатываются дополнительными экземплярами базы данных, используйте:
-
metaобъект в составеD1Resultвозвращаемый объект, который включает новые поля:served_by_regionserved_by_primary
- Панель управления Cloudflare, где можно просмотреть распределение метрик базы данных по регионам, обработавшим запросы D1.
Цены
Репликация для чтения встроена в D1, поэтому вы не платите дополнительно за хранилище или вычисления для реплик для чтения. Вы несёте точно такие же расходы на D1 тарификация использования с репликами или без них, в зависимости от rows_read и rows_written вашими запросами.
Известные ограничения
У репликации чтения D1 есть ряд известных ограничений.
- Sessions API доступен только через D1 Worker Binding и пока не доступна через REST API.
Справочная информация
Отставание реплик и модель согласованности
Чтобы учесть отставание реплик, важно учитывать модель согласованности D1. Модель согласованности представляет собой логическую основу, которая определяет, как система баз данных обрабатывает пользовательские запросы (как обновляются и считываются данные) при наличии нескольких экземпляров базы данных. Разные модели подходят для разных сценариев использования. Большинство систем баз данных предоставляют чтение зафиксированных данных ↗, изоляция снимков ↗, или сериализуемый ↗ модели согласованности в зависимости от их конфигурации.
Без модели согласованности
Подумайте, что может произойти в распределённой системе баз данных без явного механизма для соблюдения модели согласованности.
- Ваш SQL-запрос на запись обрабатывается основным экземпляром базы данных.
- Вы получаете ответ, подтверждающий выполнение запроса на запись.
- Ваш последующий SQL-запрос на чтение направляется на реплику для чтения.
- Реплика для чтения ещё не обновлена, поэтому не содержит изменений из вашего SQL-запроса на запись. С вашей точки зрения возвращённые результаты являются несогласованными.
С Sessions API
При использовании D1 Sessions API запросы получают bookmarks, что позволяет реплике для чтения обслуживать только последовательно согласованные данные.
- SQL-запрос на запись обрабатывается основным экземпляром базы данных.
- Вы получаете ответ, подтверждающий выполнение запроса на запись, а также bookmark (100), который определяет состояние базы данных после этого запроса.
- Ваш последующий SQL-запрос на чтение направляется на реплику для чтения и также передает bookmark (100).
- Реплика для чтения будет ожидать обновления до состояния не менее актуального, чем указанный bookmark (100).
- После обновления реплики для чтения (bookmark 104) она обслуживает ваш запрос на чтение, который теперь обладает последовательной согласованностью.
На диаграмме возвращенный bookmark равен 104, что отличается от bookmark, переданного в запросе на чтение (100). Это может произойти, если между выполненными вами запросами на запись и чтение были другие запросы на запись от других клиентов, которые также были реплицированы на реплику для чтения.
Sessions API обеспечивает последовательную согласованность
Репликация для чтения D1 предлагает последовательная согласованность ↗. D1 формирует глобальный порядок всех операций, выполненных над базой данных, и может определить последнюю версию базы данных, которую видел запрос, с помощью bookmarks. Затем запрос выполняется на экземпляре базы данных, который не менее актуален, чем bookmark, переданный вместе с запросом на выполнение.
Последовательная согласованность обладает такими свойствами, как:
- Монотонные чтения: Если вы выполняете два чтения одно за другим (read-1, затем read-2), read-2 не может прочитать версию базы данных старше read-1.
- Монотонные записи: Если вы выполняете write-1, а затем write-2, все процессы видят write-1 раньше write-2.
- Запись следует за чтением: Если вы читаете значение, а затем выполняете запись, эта запись должна опираться на только что прочитанное значение.
- Чтение собственных записей: Если вы записываете данные в базу, все последующие чтения увидят эту запись.
Дополнительная информация
Дополнительные сведения можно найти в следующих материалах: