mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-04-25 16:36:21 +02:00
Native CLI i18n: The TrustGraph CLI has built-in translation support that dynamically loads language strings. You can test and use different languages by simply passing the --lang flag (e.g., --lang es for Spanish, --lang ru for Russian) or by configuring your environment's LANG variable. Automated Docs Translations: This PR introduces autonomously translated Markdown documentation into several target languages, including Spanish, Swahili, Portuguese, Turkish, Hindi, Hebrew, Arabic, Simplified Chinese, and Russian.
687 lines
38 KiB
Markdown
687 lines
38 KiB
Markdown
---
|
||
layout: default
|
||
title: "Технические спецификации: Оптимизация производительности базы знаний Cassandra"
|
||
parent: "Russian (Beta)"
|
||
---
|
||
|
||
# Технические спецификации: Оптимизация производительности базы знаний Cassandra
|
||
|
||
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
|
||
|
||
**Статус:** Черновик
|
||
**Автор:** Ассистент
|
||
**Дата:** 2025-09-18
|
||
|
||
## Обзор
|
||
|
||
Данная спецификация рассматривает проблемы производительности в реализации базы знаний TrustGraph на Cassandra и предлагает оптимизации для хранения и запросов RDF-тройных.
|
||
|
||
## Текущая реализация
|
||
|
||
### Структура данных
|
||
|
||
Текущая реализация использует структуру данных с одной таблицей в `trustgraph-flow/trustgraph/direct/cassandra_kg.py`:
|
||
|
||
```sql
|
||
CREATE TABLE triples (
|
||
collection text,
|
||
s text,
|
||
p text,
|
||
o text,
|
||
PRIMARY KEY (collection, s, p, o)
|
||
);
|
||
```
|
||
|
||
**Вторичные индексы:**
|
||
`triples_s` по `s` (субъект)
|
||
`triples_p` по `p` (предикат)
|
||
`triples_o` по `o` (объект)
|
||
|
||
### Шаблоны запросов
|
||
|
||
Текущая реализация поддерживает 8 различных шаблонов запросов:
|
||
|
||
1. **get_all(collection, limit=50)** - Получить все тройки для коллекции
|
||
```sql
|
||
SELECT s, p, o FROM triples WHERE collection = ? LIMIT 50
|
||
```
|
||
|
||
2. **get_s(collection, s, limit=10)** - Запрос по теме.
|
||
```sql
|
||
SELECT p, o FROM triples WHERE collection = ? AND s = ? LIMIT 10
|
||
```
|
||
|
||
3. **get_p(collection, p, limit=10)** - Запрос по предикату.
|
||
```sql
|
||
SELECT s, o FROM triples WHERE collection = ? AND p = ? LIMIT 10
|
||
```
|
||
|
||
4. **get_o(collection, o, limit=10)** - Запрос по объекту.
|
||
```sql
|
||
SELECT s, p FROM triples WHERE collection = ? AND o = ? LIMIT 10
|
||
```
|
||
|
||
5. **get_sp(collection, s, p, limit=10)** - Запрос по субъекту + предикату.
|
||
```sql
|
||
SELECT o FROM triples WHERE collection = ? AND s = ? AND p = ? LIMIT 10
|
||
```
|
||
|
||
6. **get_po(collection, p, o, limit=10)** - Запрос по предикату + объекту ⚠️
|
||
```sql
|
||
SELECT s FROM triples WHERE collection = ? AND p = ? AND o = ? LIMIT 10 ALLOW FILTERING
|
||
```
|
||
|
||
7. **get_os(collection, o, s, limit=10)** - Запрос по объекту + субъекту ⚠️
|
||
```sql
|
||
SELECT p FROM triples WHERE collection = ? AND o = ? AND s = ? LIMIT 10 ALLOW FILTERING
|
||
```
|
||
|
||
8. **get_spo(collection, s, p, o, limit=10)** - Точное соответствие тройке.
|
||
```sql
|
||
SELECT s as x FROM triples WHERE collection = ? AND s = ? AND p = ? AND o = ? LIMIT 10
|
||
```
|
||
|
||
### Текущая архитектура
|
||
|
||
**Файл: `trustgraph-flow/trustgraph/direct/cassandra_kg.py`**
|
||
Один класс `KnowledgeGraph`, обрабатывающий все операции
|
||
Объединение подключений через глобальный список `_active_clusters`
|
||
Фиксированное имя таблицы: `"triples"`
|
||
Пространство ключей для каждой модели пользователя
|
||
Репликация SimpleStrategy с коэффициентом 1
|
||
|
||
**Точки интеграции:**
|
||
**Путь записи:** `trustgraph-flow/trustgraph/storage/triples/cassandra/write.py`
|
||
**Путь запроса:** `trustgraph-flow/trustgraph/query/triples/cassandra/service.py`
|
||
**Хранилище знаний:** `trustgraph-flow/trustgraph/tables/knowledge.py`
|
||
|
||
## Выявленные проблемы с производительностью
|
||
|
||
### Проблемы на уровне схемы
|
||
|
||
1. **Неэффективный дизайн первичного ключа**
|
||
Текущий: `PRIMARY KEY (collection, s, p, o)`
|
||
Приводит к плохой кластеризации для распространенных шаблонов доступа
|
||
Заставляет использовать дорогостоящие вторичные индексы
|
||
|
||
2. **Чрезмерное использование вторичных индексов** ⚠️
|
||
Три вторичных индекса на столбцах с высокой кардинальностью (s, p, o)
|
||
Вторичные индексы в Cassandra дороги и плохо масштабируются
|
||
Запросы 6 и 7 требуют `ALLOW FILTERING`, что указывает на плохое моделирование данных
|
||
|
||
3. **Риск "горячих" разделов**
|
||
Один ключ раздела `collection` может создавать "горячие" разделы
|
||
Большие коллекции будут концентрироваться на отдельных узлах
|
||
Отсутствует стратегия распределения для балансировки нагрузки
|
||
|
||
### Проблемы на уровне запросов
|
||
|
||
1. **Использование ALLOW FILTERING** ⚠️
|
||
Два типа запросов (get_po, get_os) требуют `ALLOW FILTERING`
|
||
Эти запросы сканируют несколько разделов и чрезвычайно дороги
|
||
Производительность ухудшается линейно с увеличением объема данных
|
||
|
||
2. **Неэффективные шаблоны доступа**
|
||
Отсутствует оптимизация для распространенных шаблонов запросов RDF
|
||
Отсутствуют составные индексы для часто используемых комбинаций запросов
|
||
Не учитываются шаблоны обхода графов
|
||
|
||
3. **Отсутствие оптимизации запросов**
|
||
Отсутствует кэширование подготовленных выражений
|
||
Отсутствуют подсказки или стратегии оптимизации запросов
|
||
Не учитывается постраничная навигация, кроме простого LIMIT
|
||
|
||
## Описание проблемы
|
||
|
||
Текущая реализация базы знаний Cassandra имеет два критических узких места в производительности:
|
||
|
||
### 1. Неэффективная производительность запроса get_po
|
||
|
||
Запрос `get_po(collection, p, o)` крайне неэффективен, поскольку требует `ALLOW FILTERING`:
|
||
|
||
```sql
|
||
SELECT s FROM triples WHERE collection = ? AND p = ? AND o = ? LIMIT 10 ALLOW FILTERING
|
||
```
|
||
|
||
**Почему это является проблемой:**
|
||
`ALLOW FILTERING` заставляет Cassandra сканировать все разделы внутри коллекции.
|
||
Производительность ухудшается линейно с увеличением размера данных.
|
||
Это распространенный шаблон запросов RDF (поиск объектов, имеющих определенную связь "субъект-предикат-объект").
|
||
Это создает значительную нагрузку на кластер по мере роста данных.
|
||
|
||
### 2. Неоптимальная стратегия кластеризации
|
||
|
||
Текущий первичный ключ `PRIMARY KEY (collection, s, p, o)` обеспечивает минимальные преимущества кластеризации:
|
||
|
||
**Проблемы с текущей кластеризацией:**
|
||
`collection` в качестве ключа раздела не обеспечивает эффективное распределение данных.
|
||
Большинство коллекций содержат разнообразные данные, что делает кластеризацию неэффективной.
|
||
Отсутствует учет типичных шаблонов доступа в запросах RDF.
|
||
Большие коллекции создают "горячие" разделы на отдельных узлах.
|
||
Столбцы кластеризации (s, p, o) не оптимизированы для типичных шаблонов обхода графа.
|
||
|
||
**Влияние:**
|
||
Запросы не получают выгоды от локальности данных.
|
||
Плохое использование кэша.
|
||
Неравномерное распределение нагрузки между узлами кластера.
|
||
"Узкие места" масштабируемости по мере роста коллекций.
|
||
|
||
## Предлагаемое решение: Стратегия денормализации с использованием 4 таблиц
|
||
|
||
### Обзор
|
||
|
||
Замените одну таблицу `triples` на четыре специализированные таблицы, каждая из которых оптимизирована для конкретных шаблонов запросов. Это устраняет необходимость во вторичных индексах и использовании ALLOW FILTERING, обеспечивая при этом оптимальную производительность для всех типов запросов. Четвертая таблица обеспечивает эффективное удаление коллекций, несмотря на составные ключи разделов.
|
||
|
||
### Новая структура схемы
|
||
|
||
**Таблица 1: Запросы, ориентированные на субъекты (triples_s)**
|
||
```sql
|
||
CREATE TABLE triples_s (
|
||
collection text,
|
||
s text,
|
||
p text,
|
||
o text,
|
||
PRIMARY KEY ((collection, s), p, o)
|
||
);
|
||
```
|
||
**Оптимизирует:** get_s, get_sp, get_os
|
||
**Ключ разделения:** (collection, s) - Обеспечивает лучшую распределенность, чем только collection.
|
||
**Кластеризация:** (p, o) - Обеспечивает эффективный поиск по предикатам/объектам для субъекта.
|
||
|
||
**Таблица 2: Запросы предикат-объект (triples_p)**
|
||
```sql
|
||
CREATE TABLE triples_p (
|
||
collection text,
|
||
p text,
|
||
o text,
|
||
s text,
|
||
PRIMARY KEY ((collection, p), o, s)
|
||
);
|
||
```
|
||
**Оптимизирует:** get_p, get_po (исключает ALLOW FILTERING!)
|
||
**Ключ партиции:** (collection, p) - Прямой доступ по предикату
|
||
**Кластеризация:** (o, s) - Эффективный обход объектов и субъектов
|
||
|
||
**Таблица 3: Объектно-ориентированные запросы (triples_o)**
|
||
```sql
|
||
CREATE TABLE triples_o (
|
||
collection text,
|
||
o text,
|
||
s text,
|
||
p text,
|
||
PRIMARY KEY ((collection, o), s, p)
|
||
);
|
||
```
|
||
**Оптимизирует:** get_o
|
||
**Ключ разделения:** (collection, o) - Прямой доступ по объекту
|
||
**Кластеризация:** (s, p) - Эффективный обход по субъекту-предикату
|
||
|
||
**Таблица 4: Управление коллекциями и запросы SPO (triples_collection)**
|
||
```sql
|
||
CREATE TABLE triples_collection (
|
||
collection text,
|
||
s text,
|
||
p text,
|
||
o text,
|
||
PRIMARY KEY (collection, s, p, o)
|
||
);
|
||
```
|
||
**Оптимизирует:** get_spo, delete_collection
|
||
**Разделительный ключ:** только коллекция - Обеспечивает эффективные операции на уровне коллекций.
|
||
**Кластеризация:** (s, p, o) - Стандартный порядок троек.
|
||
**Назначение:** Двойное использование для точного поиска SPO и в качестве индекса удаления.
|
||
|
||
### Отображение запросов
|
||
|
||
| Исходный запрос | Целевая таблица | Улучшение производительности |
|
||
|----------------|-------------|------------------------|
|
||
| get_all(collection) | triples_s | ALLOW FILTERING (приемлемо для сканирования) |
|
||
| get_s(collection, s) | triples_s | Прямой доступ к разделу |
|
||
| get_p(collection, p) | triples_p | Прямой доступ к разделу |
|
||
| get_o(collection, o) | triples_o | Прямой доступ к разделу |
|
||
| get_sp(collection, s, p) | triples_s | Раздел + кластеризация |
|
||
| get_po(collection, p, o) | triples_p | **Больше не требуется ALLOW FILTERING!** |
|
||
| get_os(collection, o, s) | triples_o | Раздел + кластеризация |
|
||
| get_spo(collection, s, p, o) | triples_collection | Точный поиск по ключу |
|
||
| delete_collection(collection) | triples_collection | Чтение индекса, пакетное удаление всех |
|
||
|
||
### Стратегия удаления коллекций
|
||
|
||
С составными раздельными ключами мы не можем просто выполнить `DELETE FROM table WHERE collection = ?`. Вместо этого:
|
||
|
||
1. **Фаза чтения:** Запрос `triples_collection` для перечисления всех троек:
|
||
```sql
|
||
SELECT s, p, o FROM triples_collection WHERE collection = ?
|
||
```
|
||
Это эффективно, поскольку `collection` является ключом секции для этой таблицы.
|
||
|
||
2. **Фаза удаления:** Для каждой тройки (s, p, o) удалите из всех 4 таблиц, используя полные ключи секций:
|
||
```sql
|
||
DELETE FROM triples_s WHERE collection = ? AND s = ? AND p = ? AND o = ?
|
||
DELETE FROM triples_p WHERE collection = ? AND p = ? AND o = ? AND s = ?
|
||
DELETE FROM triples_o WHERE collection = ? AND o = ? AND s = ? AND p = ?
|
||
DELETE FROM triples_collection WHERE collection = ? AND s = ? AND p = ? AND o = ?
|
||
```
|
||
Обрабатывается пакетами по 100 элементов для повышения эффективности.
|
||
|
||
**Анализ компромиссов:**
|
||
✅ Поддерживает оптимальную производительность запросов с использованием распределенных разделов.
|
||
✅ Отсутствуют "горячие" разделы для больших коллекций.
|
||
❌ Более сложная логика удаления (чтение, а затем удаление).
|
||
❌ Время удаления пропорционально размеру коллекции.
|
||
|
||
### Преимущества
|
||
|
||
1. **Устраняет ALLOW FILTERING** - Каждый запрос имеет оптимальный путь доступа (за исключением полного сканирования).
|
||
2. **Не требуются вторичные индексы** - Каждая таблица ЯВЛЯЕТСЯ индексом для своего шаблона запросов.
|
||
3. **Более равномерное распределение данных** - Композитные ключи разделов эффективно распределяют нагрузку.
|
||
4. **Предсказуемая производительность** - Время выполнения запроса пропорционально размеру результата, а не общему объему данных.
|
||
5. **Использует сильные стороны Cassandra** - Разработана с учетом архитектуры Cassandra.
|
||
6. **Позволяет удалять коллекции** - `triples_collection` служит индексом для удаления.
|
||
|
||
## План реализации
|
||
|
||
### Файлы, требующие изменений
|
||
|
||
#### Основной файл реализации
|
||
|
||
**`trustgraph-flow/trustgraph/direct/cassandra_kg.py`** - Требуется полная переработка.
|
||
|
||
**Методы, требующие рефакторинга:**
|
||
```python
|
||
# Schema initialization
|
||
def init(self) -> None # Replace single table with three tables
|
||
|
||
# Insert operations
|
||
def insert(self, collection, s, p, o) -> None # Write to all three tables
|
||
|
||
# Query operations (API unchanged, implementation optimized)
|
||
def get_all(self, collection, limit=50) # Use triples_by_subject
|
||
def get_s(self, collection, s, limit=10) # Use triples_by_subject
|
||
def get_p(self, collection, p, limit=10) # Use triples_by_po
|
||
def get_o(self, collection, o, limit=10) # Use triples_by_object
|
||
def get_sp(self, collection, s, p, limit=10) # Use triples_by_subject
|
||
def get_po(self, collection, p, o, limit=10) # Use triples_by_po (NO ALLOW FILTERING!)
|
||
def get_os(self, collection, o, s, limit=10) # Use triples_by_subject
|
||
def get_spo(self, collection, s, p, o, limit=10) # Use triples_by_subject
|
||
|
||
# Collection management
|
||
def delete_collection(self, collection) -> None # Delete from all three tables
|
||
```
|
||
|
||
#### Интеграционные файлы (изменения в логике не требуются)
|
||
|
||
**`trustgraph-flow/trustgraph/storage/triples/cassandra/write.py`**
|
||
Изменения не требуются - используется существующий API KnowledgeGraph.
|
||
Автоматически получает преимущества от улучшений производительности.
|
||
|
||
**`trustgraph-flow/trustgraph/query/triples/cassandra/service.py`**
|
||
Изменения не требуются - используется существующий API KnowledgeGraph.
|
||
Автоматически получает преимущества от улучшений производительности.
|
||
|
||
### Файлы для тестирования, требующие обновления
|
||
|
||
#### Юнит-тесты
|
||
**`tests/unit/test_storage/test_triples_cassandra_storage.py`**
|
||
Обновить ожидаемые результаты тестов в связи с изменениями схемы.
|
||
Добавить тесты для обеспечения согласованности между несколькими таблицами.
|
||
Проверить отсутствие использования ALLOW FILTERING в планах запросов.
|
||
|
||
**`tests/unit/test_query/test_triples_cassandra_query.py`**
|
||
Обновить утверждения о производительности.
|
||
Протестировать все 8 шаблонов запросов для новых таблиц.
|
||
Проверить маршрутизацию запросов к правильным таблицам.
|
||
|
||
#### Интеграционные тесты
|
||
**`tests/integration/test_cassandra_integration.py`**
|
||
Комплексное тестирование с новой схемой.
|
||
Сравнение результатов бенчмаркинга производительности.
|
||
Проверка согласованности данных между таблицами.
|
||
|
||
**`tests/unit/test_storage/test_cassandra_config_integration.py`**
|
||
Обновить тесты проверки схемы.
|
||
Протестировать сценарии миграции.
|
||
|
||
### Стратегия реализации
|
||
|
||
#### Фаза 1: Схема и основные методы
|
||
1. **Переписать метод `init()`** - Создать четыре таблицы вместо одной.
|
||
2. **Переписать метод `insert()`** - Выполнять пакетные записи во все четыре таблицы.
|
||
3. **Реализовать подготовленные запросы** - Для оптимальной производительности.
|
||
4. **Добавить логику маршрутизации таблиц** - Направлять запросы к оптимальным таблицам.
|
||
5. **Реализовать удаление коллекций** - Читать из triples_collection, пакетно удалять из всех таблиц.
|
||
|
||
#### Фаза 2: Оптимизация методов запросов
|
||
1. **Переписать каждый метод get_*** для использования оптимальной таблицы.
|
||
2. **Удалить все случаи использования ALLOW FILTERING**.
|
||
3. **Реализовать эффективное использование ключей кластеризации**.
|
||
4. **Добавить логирование производительности запросов**.
|
||
|
||
#### Фаза 3: Управление коллекциями
|
||
1. **Обновить `delete_collection()`** - Удалить из всех трех таблиц.
|
||
2. **Добавить проверку согласованности** - Обеспечить синхронизацию всех таблиц.
|
||
3. **Реализовать пакетные операции** - Для атомарных операций с несколькими таблицами.
|
||
|
||
### Ключевые детали реализации
|
||
|
||
#### Стратегия пакетной записи
|
||
```python
|
||
def insert(self, collection, s, p, o):
|
||
batch = BatchStatement()
|
||
|
||
# Insert into all four tables
|
||
batch.add(self.insert_subject_stmt, (collection, s, p, o))
|
||
batch.add(self.insert_po_stmt, (collection, p, o, s))
|
||
batch.add(self.insert_object_stmt, (collection, o, s, p))
|
||
batch.add(self.insert_collection_stmt, (collection, s, p, o))
|
||
|
||
self.session.execute(batch)
|
||
```
|
||
|
||
#### Логика маршрутизации запросов
|
||
```python
|
||
def get_po(self, collection, p, o, limit=10):
|
||
# Route to triples_p table - NO ALLOW FILTERING!
|
||
return self.session.execute(
|
||
self.get_po_stmt,
|
||
(collection, p, o, limit)
|
||
)
|
||
|
||
def get_spo(self, collection, s, p, o, limit=10):
|
||
# Route to triples_collection table for exact SPO lookup
|
||
return self.session.execute(
|
||
self.get_spo_stmt,
|
||
(collection, s, p, o, limit)
|
||
)
|
||
```
|
||
|
||
#### Логика удаления коллекции
|
||
```python
|
||
def delete_collection(self, collection):
|
||
# Step 1: Read all triples from collection table
|
||
rows = self.session.execute(
|
||
f"SELECT s, p, o FROM {self.collection_table} WHERE collection = %s",
|
||
(collection,)
|
||
)
|
||
|
||
# Step 2: Batch delete from all 4 tables
|
||
batch = BatchStatement()
|
||
count = 0
|
||
|
||
for row in rows:
|
||
s, p, o = row.s, row.p, row.o
|
||
|
||
# Delete using full partition keys for each table
|
||
batch.add(SimpleStatement(
|
||
f"DELETE FROM {self.subject_table} WHERE collection = ? AND s = ? AND p = ? AND o = ?"
|
||
), (collection, s, p, o))
|
||
|
||
batch.add(SimpleStatement(
|
||
f"DELETE FROM {self.po_table} WHERE collection = ? AND p = ? AND o = ? AND s = ?"
|
||
), (collection, p, o, s))
|
||
|
||
batch.add(SimpleStatement(
|
||
f"DELETE FROM {self.object_table} WHERE collection = ? AND o = ? AND s = ? AND p = ?"
|
||
), (collection, o, s, p))
|
||
|
||
batch.add(SimpleStatement(
|
||
f"DELETE FROM {self.collection_table} WHERE collection = ? AND s = ? AND p = ? AND o = ?"
|
||
), (collection, s, p, o))
|
||
|
||
count += 1
|
||
|
||
# Execute every 100 triples to avoid oversized batches
|
||
if count % 100 == 0:
|
||
self.session.execute(batch)
|
||
batch = BatchStatement()
|
||
|
||
# Execute remaining deletions
|
||
if count % 100 != 0:
|
||
self.session.execute(batch)
|
||
|
||
logger.info(f"Deleted {count} triples from collection {collection}")
|
||
```
|
||
|
||
#### Оптимизация подготовленных выражений
|
||
```python
|
||
def prepare_statements(self):
|
||
# Cache prepared statements for better performance
|
||
self.insert_subject_stmt = self.session.prepare(
|
||
f"INSERT INTO {self.subject_table} (collection, s, p, o) VALUES (?, ?, ?, ?)"
|
||
)
|
||
self.insert_po_stmt = self.session.prepare(
|
||
f"INSERT INTO {self.po_table} (collection, p, o, s) VALUES (?, ?, ?, ?)"
|
||
)
|
||
self.insert_object_stmt = self.session.prepare(
|
||
f"INSERT INTO {self.object_table} (collection, o, s, p) VALUES (?, ?, ?, ?)"
|
||
)
|
||
self.insert_collection_stmt = self.session.prepare(
|
||
f"INSERT INTO {self.collection_table} (collection, s, p, o) VALUES (?, ?, ?, ?)"
|
||
)
|
||
# ... query statements
|
||
```
|
||
|
||
## Стратегия миграции
|
||
|
||
### Подход к миграции данных
|
||
|
||
#### Вариант 1: Развертывание Blue-Green (Рекомендуется)
|
||
1. **Разверните новую схему параллельно с существующей** - Временно используйте разные имена таблиц.
|
||
2. **Период двойной записи** - Записывайте данные как в старую, так и в новую схемы во время перехода.
|
||
3. **Фоновая миграция** - Скопируйте существующие данные в новые таблицы.
|
||
4. **Перенаправление операций чтения** - Направляйте запросы к новым таблицам после миграции данных.
|
||
5. **Удаление старых таблиц** - После периода проверки.
|
||
|
||
#### Вариант 2: Миграция на месте
|
||
1. **Добавление схемы** - Создайте новые таблицы в существующем пространстве ключей.
|
||
2. **Скрипт миграции данных** - Пакетная копия из старой таблицы в новые таблицы.
|
||
3. **Обновление приложения** - Разверните новый код после завершения миграции.
|
||
4. **Очистка старой таблицы** - Удалите старую таблицу и индексы.
|
||
|
||
### Обратная совместимость
|
||
|
||
#### Стратегия развертывания
|
||
```python
|
||
# Environment variable to control table usage during migration
|
||
USE_LEGACY_TABLES = os.getenv('CASSANDRA_USE_LEGACY', 'false').lower() == 'true'
|
||
|
||
class KnowledgeGraph:
|
||
def __init__(self, ...):
|
||
if USE_LEGACY_TABLES:
|
||
self.init_legacy_schema()
|
||
else:
|
||
self.init_optimized_schema()
|
||
```
|
||
|
||
#### Скрипт миграции
|
||
```python
|
||
def migrate_data():
|
||
# Read from old table
|
||
old_triples = session.execute("SELECT collection, s, p, o FROM triples")
|
||
|
||
# Batch write to new tables
|
||
for batch in batched(old_triples, 100):
|
||
batch_stmt = BatchStatement()
|
||
for row in batch:
|
||
# Add to all three new tables
|
||
batch_stmt.add(insert_subject_stmt, row)
|
||
batch_stmt.add(insert_po_stmt, (row.collection, row.p, row.o, row.s))
|
||
batch_stmt.add(insert_object_stmt, (row.collection, row.o, row.s, row.p))
|
||
session.execute(batch_stmt)
|
||
```
|
||
|
||
### Стратегия проверки
|
||
|
||
#### Проверки согласованности данных
|
||
```python
|
||
def validate_migration():
|
||
# Count total records in old vs new tables
|
||
old_count = session.execute("SELECT COUNT(*) FROM triples WHERE collection = ?", (collection,))
|
||
new_count = session.execute("SELECT COUNT(*) FROM triples_by_subject WHERE collection = ?", (collection,))
|
||
|
||
assert old_count == new_count, f"Record count mismatch: {old_count} vs {new_count}"
|
||
|
||
# Spot check random samples
|
||
sample_queries = generate_test_queries()
|
||
for query in sample_queries:
|
||
old_result = execute_legacy_query(query)
|
||
new_result = execute_optimized_query(query)
|
||
assert old_result == new_result, f"Query results differ for {query}"
|
||
```
|
||
|
||
## Стратегия тестирования
|
||
|
||
### Тестирование производительности
|
||
|
||
#### Сценарии для бенчмаркинга
|
||
1. **Сравнение производительности запросов**
|
||
Показатели производительности до и после для всех 8 типов запросов
|
||
Акцент на улучшении производительности запроса `get_po` (удаление `ALLOW FILTERING`)
|
||
Измерение задержки запросов при различных объемах данных
|
||
|
||
2. **Тестирование нагрузки**
|
||
Одновременное выполнение запросов
|
||
Скорость записи с использованием пакетных операций
|
||
Использование памяти и ЦП
|
||
|
||
3. **Тестирование масштабируемости**
|
||
Производительность при увеличении размеров коллекций
|
||
Распределение запросов по нескольким коллекциям
|
||
Использование узлов кластера
|
||
|
||
#### Наборы тестовых данных
|
||
**Маленький:** 10 тысяч троек на коллекцию
|
||
**Средний:** 100 тысяч троек на коллекцию
|
||
**Большой:** 1 миллион и более троек на коллекцию
|
||
**Несколько коллекций:** Тестирование распределения партиций
|
||
|
||
### Функциональное тестирование
|
||
|
||
#### Обновления модульных тестов
|
||
```python
|
||
# Example test structure for new implementation
|
||
class TestCassandraKGPerformance:
|
||
def test_get_po_no_allow_filtering(self):
|
||
# Verify get_po queries don't use ALLOW FILTERING
|
||
with patch('cassandra.cluster.Session.execute') as mock_execute:
|
||
kg.get_po('test_collection', 'predicate', 'object')
|
||
executed_query = mock_execute.call_args[0][0]
|
||
assert 'ALLOW FILTERING' not in executed_query
|
||
|
||
def test_multi_table_consistency(self):
|
||
# Verify all tables stay in sync
|
||
kg.insert('test', 's1', 'p1', 'o1')
|
||
|
||
# Check all tables contain the triple
|
||
assert_triple_exists('triples_by_subject', 'test', 's1', 'p1', 'o1')
|
||
assert_triple_exists('triples_by_po', 'test', 'p1', 'o1', 's1')
|
||
assert_triple_exists('triples_by_object', 'test', 'o1', 's1', 'p1')
|
||
```
|
||
|
||
#### Обновления результатов интеграционного тестирования
|
||
```python
|
||
class TestCassandraIntegration:
|
||
def test_query_performance_regression(self):
|
||
# Ensure new implementation is faster than old
|
||
old_time = benchmark_legacy_get_po()
|
||
new_time = benchmark_optimized_get_po()
|
||
assert new_time < old_time * 0.5 # At least 50% improvement
|
||
|
||
def test_end_to_end_workflow(self):
|
||
# Test complete write -> query -> delete cycle
|
||
# Verify no performance degradation in integration
|
||
```
|
||
|
||
### План отката
|
||
|
||
#### Быстрая стратегия отката
|
||
1. **Переключение переменной окружения** - Немедленный возврат к устаревшим таблицам.
|
||
2. **Сохранение устаревших таблиц** - Не удалять до тех пор, пока производительность не будет подтверждена.
|
||
3. **Сигналы мониторинга** - Автоматический запуск отката на основе показателей ошибок/задержек.
|
||
|
||
#### Проверка отката
|
||
```python
|
||
def rollback_to_legacy():
|
||
# Set environment variable
|
||
os.environ['CASSANDRA_USE_LEGACY'] = 'true'
|
||
|
||
# Restart services to pick up change
|
||
restart_cassandra_services()
|
||
|
||
# Validate functionality
|
||
run_smoke_tests()
|
||
```
|
||
|
||
## Риски и соображения
|
||
|
||
### Риски производительности
|
||
**Увеличение времени записи** - 4 операции записи на каждую вставку (на 33% больше, чем при использовании 3-х таблиц)
|
||
**Избыточность хранения** - В 4 раза больше места для хранения (на 33% больше, чем при использовании 3-х таблиц)
|
||
**Сбои пакетной записи** - Требуется правильная обработка ошибок
|
||
**Сложность удаления** - Удаление коллекции требует цикла "чтение-удаление"
|
||
|
||
### Операционные риски
|
||
**Сложность миграции** - Миграция данных для больших наборов данных
|
||
**Проблемы с согласованностью** - Обеспечение синхронизации всех таблиц
|
||
**Недостаточность мониторинга** - Необходимы новые метрики для операций с несколькими таблицами
|
||
|
||
### Стратегии смягчения
|
||
1. **Постепенное внедрение** - Начните с небольших коллекций
|
||
2. **Комплексный мониторинг** - Отслеживайте все метрики производительности
|
||
3. **Автоматизированная проверка** - Постоянная проверка согласованности
|
||
4. **Возможность быстрого отката** - Выбор таблицы на основе среды
|
||
|
||
## Критерии успеха
|
||
|
||
### Улучшения производительности
|
||
[ ] **Устранение ALLOW FILTERING** - Запросы get_po и get_os выполняются без фильтрации
|
||
[ ] **Сокращение задержки запросов** - Улучшение времени отклика запросов на 50% или более
|
||
[ ] **Более равномерное распределение нагрузки** - Отсутствие "горячих" партиций, равномерная нагрузка на узлы кластера
|
||
[ ] **Масштабируемая производительность** - Время запроса пропорционально размеру результата, а не общему объему данных
|
||
|
||
### Функциональные требования
|
||
[ ] **Совместимость API** - Весь существующий код продолжает работать без изменений
|
||
[ ] **Согласованность данных** - Все три таблицы остаются синхронизированными
|
||
[ ] **Отсутствие потери данных** - Миграция сохраняет все существующие тройки
|
||
[ ] **Обратная совместимость** - Возможность отката к устаревшей схеме
|
||
|
||
### Операционные требования
|
||
[ ] **Безопасная миграция** - Развертывание по принципу "синий-зеленый" с возможностью отката
|
||
[ ] **Покрытие мониторингом** - Комплексные метрики для операций с несколькими таблицами
|
||
[ ] **Покрытие тестами** - Все шаблоны запросов протестированы с использованием эталонных показателей производительности
|
||
[ ] **Документация** - Обновленные процедуры развертывания и эксплуатации
|
||
|
||
## План
|
||
|
||
### Фаза 1: Реализация
|
||
[ ] Переписать `cassandra_kg.py` с использованием схемы нескольких таблиц
|
||
[ ] Реализовать пакетные операции записи
|
||
[ ] Добавить оптимизацию с использованием подготовленных выражений
|
||
[ ] Обновить модульные тесты
|
||
|
||
### Фаза 2: Интеграционное тестирование
|
||
[ ] Обновить интеграционные тесты
|
||
[ ] Эталонное тестирование производительности
|
||
[ ] Тестирование нагрузки с использованием реалистичных объемов данных
|
||
[ ] Скрипты проверки согласованности данных
|
||
|
||
### Фаза 3: Планирование миграции
|
||
[ ] Скрипты развертывания по принципу "синий-зеленый"
|
||
[ ] Инструменты миграции данных
|
||
[ ] Обновления панели мониторинга
|
||
[ ] Процедуры отката
|
||
|
||
### Фаза 4: Развертывание в производственной среде
|
||
[ ] Поэтапное развертывание в производственной среде
|
||
[ ] Мониторинг и проверка производительности
|
||
[ ] Очистка устаревших таблиц
|
||
[ ] Обновление документации
|
||
|
||
## Заключение
|
||
|
||
Эта стратегия денормализации с использованием нескольких таблиц напрямую решает две критические проблемы производительности:
|
||
|
||
1. **Устраняет дорогостоящий ALLOW FILTERING**, предоставляя оптимальные структуры таблиц для каждого шаблона запроса
|
||
2. **Улучшает эффективность кластеризации** за счет составных ключей партиций, которые правильно распределяют нагрузку
|
||
|
||
Этот подход использует сильные стороны Cassandra, сохраняя при этом полную совместимость API, что обеспечивает автоматическое получение преимуществ от улучшений производительности для существующего кода.
|