NoSQL: понятие, виды баз данных и их особенности

NoSQL (что значит «Не только SQL») представляет новый класс систем управления данными, отходящих от реляционного подхода к хранению информации. В противовес традиционным СУБД, таким как MySQL или PostgreSQL, где все данные размещаются в таблицах с фиксированной структурой и строгими связями, NoSQL предлагает более гибкие способы организации и хранения информации. Эта технология не отрицает SQL, а расширяет возможности работы с данными.

Происхождение термина NoSQL имеет интересную историю, которая началась не с технологии, а с названия технической конференции. В 2009 году в Сан-Франциско организаторы мероприятия по базам данных выбрали это название, и оно неожиданно прижилось в индустрии. Любопытно, что за десятилетие до этого, в 1998 году, разработчик Карло Строцци уже использовал название NoSQL для своего проекта, не имевшего отношения к современным нереляционным системам.

Современные NoSQL-решения представлены несколькими основными категориями систем хранения данных. К ним относятся базы данных, работающие с документами (MongoDB возглавляет это направление), хранилища пар ключ-значение (яркий пример — Redis), системы управления графами (где лидирует Neo4j) и колоночные хранилища (такие как ClickHouse). Объединяющей характеристикой этих систем является отказ от классического языка SQL в пользу собственных методов обработки данных.

В отличие от реляционных СУБД, где SQL выступает стандартизированным языком для работы с данными, обеспечивая возможности выборки информации и объединения таблиц посредством операций JOIN и UNION, нереляционные системы разработали собственные уникальные языки запросов. Каждая NoSQL-база данных предлагает свой специализированный синтаксис для манипуляции данными. Рассмотрим конкретные примеры:

// MongoDB (использует JavaScript-подобный синтаксис): 
db.users.find({ age: { $gt: 21 } }) 

// Redis (использует специальные команды): 
HGET user:1000 email 
SET session:token "abc123" 

NoSQL-решения демонстрируют максимальную эффективность в сценариях обработки масштабных массивов неструктурированной информации. Характерный пример — архитектура современных социальных платформ, где MongoDB позволяет объединить в единый документ профиль пользователя, его публикации, отклики и активность, обеспечивая тем самым оптимальную производительность при извлечении данных.

❯ Отличия реляционных и нереляционных баз данных 

Эволюция NoSQL-систем шла параллельно с ростом технологических потребностей и усложнением бизнес-требований. Современный цифровой мир, генерирующий терабайты данных ежесекундно, потребовал новых подходов к обработке информации. В результате сформировались две фундаментально различные концепции управления данными. Первая — классический реляционный метод, делающий упор на целостность и достоверность информации. Вторая — инновационный NoSQL-подход, приоритизирующий адаптивность и возможности роста. Каждая концепция опирается на собственные базовые принципы, определяющие их практическое применение.

Традиционные реляционные системы следуют принципам ACID:

  • Atomicity (Атомарность) обеспечивает неделимость транзакций — либо полное выполнение, либо полный откат.

  • Consistency (Согласованность) поддерживает непротиворечивость данных на всех этапах.

  • Isolation (Изоляция) гарантирует независимое выполнение параллельных транзакций.

  • Durability (Надежность) обеспечивает сохранность результатов успешных транзакций.

В свою очередь, NoSQL-системы построены на принципах BASE:

  • Basically Available — приоритет отдается постоянной доступности данных.

  • Soft state — состояние системы может меняться со временем.

  • Eventually consistent — согласованность достигается с течением времени.

Фундаментальные отличия между классическими реляционными и современными NoSQL-системами проявляются в нескольких ключевых аспектах:

  1. Организация данных 

  • Реляционные системы требуют четкой табличной структуры с заранее определенными столбцами и типами данных.

    1. NoSQL-платформы допускают произвольный формат хранения, включая слабоструктурированные данные.

  1. Подходы к масштабированию

  • Реляционные СУБД наращивают мощность через усиление серверного оборудования.

    1. NoSQL-решения расширяются путем добавления новых серверов в кластер.

  1. Обеспечение целостности 

  • Реляционные базы поддерживают целостность на уровне ядра СУБД.

    1. NoSQL-системы делегируют контроль целостности прикладному уровню.

  1. Особенности производительности 

  • Реляционные системы эффективны при выполнении комплексных транзакций.

    1. NoSQL-базы демонстрируют высокую скорость в базовых операциях ввода-вывода.

  1. Способы хранения

  • Реляционные СУБД распределяют данные по множеству связанных таблиц.

    1. NoSQL-платформы объединяют связанные данные в единые информационные блоки.

Такие принципиальные различия определяют оптимальные сферы применения: реляционные системы незаменимы там, где требуется абсолютная точность данных (финансовые операции), а NoSQL-решения идеальны для обработки больших потоков информации (социальные медиа, аналитические системы).

❯ Основные особенности и преимущества NoSQL

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

Гибкость схемы данных

Одним из главных преимуществ NoSQL-систем является их адаптивная структура данных (schema-free-подход). В отличие от реляционных баз, где изменение схемы требует модификации всех существующих записей, NoSQL позволяет динамически расширять структуру документов, добавляя новые атрибуты без реорганизации всего хранилища. Такая гибкость особенно востребована в проектах с активно эволюционирующими требованиями к данным.

// MongoDB: Гибкая схема позволяет хранить разные структуры в одной коллекции db.users.insertMany([ 
  { name: "Иван", email: "ivan@mail.ru" }, 
  { name: "Мария", email: "maria@mail.ru", phone: "+7999999999" }, 
  { name: "Петр", social: { twitter: "@petr", facebook: "petr.fb" }
} ]) 

Горизонтальное масштабирование

NoSQL-системы реализуют принципиально иной подход к наращиванию производительности по сравнению с реляционными базами данных. Если традиционные СУБД увеличивают мощность путем обновления аппаратных ресурсов единого сервера, то NoSQL-архитектура позволяет создавать распределенные кластеры. Такой подход дает возможность повышать общую производительность системы за счет добавления новых серверных узлов, между которыми автоматически распределяется рабочая нагрузка.

Шардинг и репликация

NoSQL-базы данных предлагают встроенную поддержку шардинга — распределения данных между несколькими серверами. Этот подход концептуально похож на работу RAID 0, где данные «разрезаются» на полосы (stripes) и распределяются между несколькими дисками для повышения производительности. Аналогично, при шардинге каждый сервер отвечает за свою часть данных, что позволяет:

  • Увеличивать производительность системы 

  • Обеспечивать отказоустойчивость 

  • Эффективно распределять нагрузку 

Высокая производительность

Благодаря специализированным механизмам хранения информации и отказу от ресурсоемких операций объединения данных (JOIN), системы NoSQL обеспечивают исключительную скорость работы в следующих сценариях:

  • Базовые операции доступа к данным (чтение и запись)

  • Управление масштабными информационными массивами

  • Одновременная обработка множественных пользовательских запросов

Работа с неструктурированными данными

NoSQL особенно эффективны при работе с:

  • Большими объемами неструктурированной информации 

  • Разнородными типами данных 

  • Динамически меняющимися структурами 

Поддержка современных технологий

NoSQL базы данных хорошо интегрируются с:

  • Облачными платформами 

  • Микросервисной архитектурой 

  • Системами обработки больших данных 

  • Современными фреймворками разработки 

Экономическая эффективность

Использование NoSQL решений может быть экономически выгодным благодаря:

  • Открытому исходному коду многих решений 

  • Эффективному использованию аппаратных ресурсов 

  • Возможности масштабирования на обычных серверах 

  • Снижению затрат на администрирование

❯ Основные виды баз данных NoSQL

В современной разработке распределенных систем выделяют несколько основных типов NoSQL-решений, каждый из которых обладает развитой экосистемой и поддержкой сообщества инженеров.

Документоориентированные базы данных 

Системы на основе документов представляют собой наиболее зрелый и широко применяемый тип NoSQL решений. MongoDB, занимающая лидирующую позицию в этом сегменте, служит эталонным примером реализации документоориентированной архитектуры хранения данных.

Принцип хранения данных

Image1
Изображение: yandex.cloud

В документоориентированных базах данных информация хранится в виде документов, объединенных в коллекции. В отличие от реляционных баз данных, где данные распределены по таблицам, здесь вся информация об объекте хранится в одном документе.

Пример документа пользователя с заказами:

{
    "_id": ObjectId("507f1f77bcf86cd799439011"),
    "user": {
        "username": "alexander",
        "email": "alex@example.com",
        "registered": "2024-02-01"
    },
    "orders": [
        {
            "orderId": "ORD-001",
            "date": "2024-02-02",
            "items": [
                {
                    "name": "Смартфон",
                    "price": 799.99,
                    "quantity": 1
                }
            ],
            "status": "delivered"
        }
    ],
    "preferences": {
        "notifications": true,
        "language": "ru"
    }
}

Основные операции с MongoDB 

// Создание документа
db.users.insertOne({
    username: "alexander",
    email: "alex@example.com"
})

// Поиск документов
db.users.find({ "preferences.language": "ru" })

// Обновление данных
db.users.updateOne(
    { username: "alexander" },
    { $set: { "preferences.notifications": false }}
)

// Удаление документа
db.users.deleteOne({ username: "alexander" })

Преимущества документоориентированного подхода

  1. Гибкая схема данных 

  • Каждый документ может иметь свою структуру 

    1. Легко добавлять новые поля 

    2. Не требуется изменение схемы базы данных 

  1. Естественное представление данных 

  • Документы похожи на объекты в программировании 

    1. Интуитивно понятная структура 

    2. Удобство работы для разработчиков 

  1. Производительность 

  • Быстрое чтение всех данных объекта 

    1. Эффективная работа с вложенными структурами 

    2. Возможность горизонтального масштабирования 

  1. Работа с иерархическими данными 

  • Естественное хранение древовидных структур 

    1. Удобное представление вложенных объектов 

    2. Эффективная обработка сложных структур

Особенности применения

Архитектура особенно эффективна в следующих условиях:

  • Разработка систем с динамически эволюционирующей структурой данных

  • Обработка крупных массивов нестандартизированной информации

  • Построение высоконагруженных распределенных платформ

Типичные сценарии использования

  • Платформы управления цифровым контентом

  • Распределенные социальные платформы

  • Системы организации корпоративного контента

  • Сервисы агрегации и анализа событий

  • Комплексные аналитические платформы

❯ Хранилища типа ключ-значение

Среди хранилищ типа ключ-значение система Redis (расшифровывается как Remote Dictionary Server) занимает лидирующую позицию на рынке NoSQL-решений. Ключевой архитектурной особенностью этой технологии является размещение всего набора данных в оперативной памяти, что обеспечивает исключительную производительность операций.

Принцип работы

Архитектура хранилищ типа ключ-значение базируется на трех фундаментальных компонентах каждой записи данных:

  • Идентификатор записи (уникальный ключ)

  • Ассоциированные данные (значение)

  • Параметр временного существования (TTL — Time To Live, опционально)

Image3
Изображение: yandex.cloud

Типы данных в Redis

# Строки (Strings)
SET user:name "Alexander"
GET user:name

# Списки (Lists)
LPUSH notifications "New message"
RPUSH notifications "Payment received"

# Множества (Sets)
SADD user:roles "admin" "editor"
SMEMBERS user:roles

# Хеш-таблицы (Hashes)
HSET user:1000 name "Alex" email "alex@example.com"
HGET user:1000 email

# Упорядоченные множества (Sorted Sets)
ZADD leaderboard 100 "player1" 85 "player2"
ZRANGE leaderboard 0 -1

Основные преимущества

  1. Высокая производительность 

  • Работа в оперативной памяти 

    1. Простая структура данных 

    2. Минимальные накладные расходы 

  1. Гибкость хранения 

  • Поддержка различных типов данных 

    1. Возможность установки времени жизни 

    2. Атомарные операции 

  1. Надежность 

  • Возможность персистентности данных 

    1. Репликация master-slave 

    2. Кластеризация 

Типичные сценарии использования

Кэширование 

# Сохранение результатов запроса
SET "query:users:active" "{json_result}"
EXPIRE "query:users:active" 3600  # Удаление через час

Управление сессиями 

# Сохранение сессии

HSET "session:token123" "user_id" "1000" "login_time" "2024-02-04"
EXPIRE "session:token123" 86400  # Сутки

Счетчики и рейтинги 

# Увеличение счетчика просмотров 
INCR "views:article:1234" 

# Обновление рейтинга 
ZADD "top_articles" 156 "article:1234" 

Очереди сообщений 

# Добавление задачи в очередь 
LPUSH "task_queue" "process_order:1234" 

# Получение задачи 
RPOP "task_queue" 

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

❯ Графовые базы данных

Графовые СУБД (Graph Databases) выделяются среди NoSQL-решений своей специализацией на управлении взаимосвязями между сущностями данных. В этом сегменте технология Neo4j завоевала лидирующую позицию благодаря эффективной работе с комплексными сетевыми структурами данных, где принципиальное значение имеют отношения между объектами.

Image4
Изображение: yandex.cloud

Основные компоненты

  1. Узлы (Nodes) 

  • Представляют сущности 

    1. Содержат свойства 

    2. Имеют метки (labels) 

  1. Отношения (Relationships) 

  • Соединяют узлы 

    1. Имеют направление 

    2. Могут содержать свойства 

    3. Определяют тип связи

Пример графовой модели в Neo4j

// Создание узлов
CREATE (john:Person { name: 'John', age: 30 })
CREATE (mary:Person { name: 'Mary', age: 28 })
CREATE (post:Post { title: 'Graph Databases', date: '2024-02-04' })

// Создание отношений
CREATE (john)-[:FRIENDS_WITH]->(mary)
CREATE (john)-[:AUTHORED]->(post)
CREATE (mary)-[:LIKED]->(post)

Типичные запросы

// Найти друзей друзей
MATCH (person:Person {name: 'John'})-[:FRIENDS_WITH]->(friend)-[:FRIENDS_WITH]->(friendOfFriend)
RETURN friendOfFriend.name

// Найти самые популярные посты
MATCH (post:Post)<-[:LIKED]-(person:Person)
RETURN post.title, count(person) as likes
ORDER BY likes DESC
LIMIT 5

Основные преимущества

  1. Естественное представление связей 

  • Интуитивно понятная модель данных 

    1. Эффективное хранение отношений 

    2. Простота в понимании и работе 

  1. Производительность при обходе графа 

  • Быстрый поиск связанных данных 

    1. Эффективная работа со сложными запросами 

    2. Оптимизация для рекурсивных запросов

Практические применения

  1. Социальные сети

// Рекомендации друзей
MATCH (user:Person)-[:FRIENDS_WITH]->(friend)-[:FRIENDS_WITH]->(potentialFriend)
WHERE user.name = 'John' AND NOT (user)-[:FRIENDS_WITH]->(potentialFriend)
RETURN potentialFriend.name
  1. Системы рекомендаций 

// Рекомендации на основе интересов
MATCH (user:Person)-[:LIKES]->(product:Product)<-[:LIKES]-(otherUser)-[:LIKES]->(recommendation:Product)
WHERE user.name = 'John' AND NOT (user)-[:LIKES]->(recommendation)
RETURN recommendation.name, count(otherUser) as frequency
  1. Маршрутизация

// Поиск кратчайшего пути
MATCH path = shortestPath(
    (start:Location {name: 'A'})-[:CONNECTS_TO*]->
    (end:Location {name: 'B'})
)
RETURN path

Особенности использования

  • Незаменим при работе со сложными взаимосвязанными структурами данных

  • Максимальная производительность при обработке циклических и вложенных запросов

  • Обеспечивает гибкое проектирование и управление многоуровневыми связями

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

Колоночные базы данных 

Архитектура этих систем основана на поколоночном хранении информации, в противовес традиционному построчному подходу, что обеспечивает значительный прирост производительности для специализированных запросов. Среди лидеров данного направления особенно выделяются ClickHouse и HBase, зарекомендовавшие себя как надежные решения корпоративного уровня.

Image2
Изображение: yandex.cloud

Принцип работы

Традиционное хранение (по строкам):
Row1: [id1, name1, email1, age1]
Row2: [id2, name2, email2, age2]

Колоночное хранение:
Column1: [id1, id2]
Column2: [name1, name2]
Column3: [email1, email2]
Column4: [age1, age2]

Основные характеристики

  1. Структура хранения 

  • Данные группируются по колонкам 

    1. Эффективное сжатие однотипных данных 

    2. Быстрое чтение определенных полей 

  1. Масштабирование 

  • Горизонтальное масштабирование 

    1. Распределенное хранение 

    2. Высокая доступность

Пример работы с ClickHouse

-- Создание таблицы
CREATE TABLE users (
    user_id UUID,
    name String,
    email String,
    registration_date DateTime
) ENGINE = MergeTree()
ORDER BY (registration_date, user_id);

-- Запись данных
INSERT INTO users (user_id, name, email, registration_date)
VALUES (generateUUIDv4(), 'John Doe', 'john@example.com', now());

-- Аналитический запрос
SELECT 
    toDate(registration_date) as date,
    count(*) as users_count
FROM users 
GROUP BY date
ORDER BY date;

Основные преимущества

  1. Эффективность аналитики 

  • Быстрое чтение конкретных колонок 

    1. Оптимизация агрегационных запросов 

    2. Эффективная работа с большими данными 

  1. Компрессия данных 

  • Лучшее сжатие однотипных данных 

    1. Экономия дискового пространства 

    2. Оптимизация ввода/вывода

Типичные сценарии использования

  1. Большие данные 

-- Анализ логов с использованием эффективной агрегации
SELECT 
    event_type,
    count() as events_count,
    uniqExact(user_id) as unique_users
FROM system_logs 
WHERE toDate(timestamp) >= '2024-01-01'
GROUP BY event_type
ORDER BY events_count DESC;
  1. Временные ряды 

-- Агрегация метрик по временным интервалам
SELECT 
    toStartOfInterval(timestamp, INTERVAL 5 MINUTE) as time_bucket,
    avg(cpu_usage) as avg_cpu,
    max(cpu_usage) as max_cpu,
    quantile(0.95)(cpu_usage) as cpu_95th
FROM server_metrics
WHERE server_id = 'srv-001'
    AND timestamp >= now() - INTERVAL 1 DAY
GROUP BY time_bucket
ORDER BY time_bucket;
  1. Аналитические системы

-- Расширенная статистика пользователей
SELECT 
    country,
    count() as users_count,
    round(avg(age), 1) as avg_age,
    uniqExact(city) as unique_cities,
    sumIf(purchase_amount, purchase_amount > 0) as total_revenue,
    round(avg(purchase_amount), 2) as avg_purchase
FROM user_statistics
GROUP BY country
HAVING users_count >= 100
ORDER BY total_revenue DESC
LIMIT 10;

Особенности применения

  • Максимальная производительность в системах с доминированием запросов на извлечение данных

  • Проверенная масштабируемость при работе с масштабными наборами информации

  • Отличная интеграция в распределенные вычислительные среды

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

❯ Полнотекстовые базы данных (OpenSearch) 

Платформа OpenSearch, созданная на основе архитектурных принципов Elasticsearch, представляет собой комплексную экосистему для высокопроизводительного текстового поиска и многомерного анализа информации. Данное решение, построенное по принципам распределенных систем, выделяется своими возможностями в области обработки, интеллектуального поиска и создания интерактивных визуализаций для масштабных информационных массивов.

Основные возможности

  1. Полнотекстовый поиск

// Поиск с поддержкой различных языков
GET /products/_search
{
  "query": {
    "multi_match": {
      "query": "беспроводные наушники",
      "fields": ["title", "description"],
      "type": "most_fields"
    }
  }
}
  1. Аналитика данных

// Агрегация по категориям
GET /products/_search
{
  "size": 0,
  "aggs": {
    "popular_categories": {
      "terms": {
        "field": "category",
        "size": 10
      }
    }
  }
} 

Ключевые преимущества

  1. Эффективный поиск 

  • Поддержка нечеткого поиска 

    1. Ранжирование результатов 

    2. Подсветка совпадений 

    3. Автодополнение 

  1. Аналитические возможности 

  • Сложные агрегации 

    1. Статистический анализ 

    2. Визуализация данных 

    3. Мониторинг в реальном времени 

Типичные сценарии использования

  1. E-commerce-поиск 

  • Поиск товаров 

    1. Фасетная навигация 

    2. Рекомендации товаров 

    3. Анализ поведения пользователей 

  1. Мониторинг и логирование 

  • Сбор метрик 

    1. Анализ производительности 

    2. Обнаружение аномалий 

    3. Отслеживание ошибок 

  1. Аналитические дашборды 

  • Визуализация данных 

    1. Бизнес-метрики 

    2. Отчетность 

    3. Real-time-аналитика

OpenSearch особенно эффективен в проектах, требующих продвинутого поиска и аналитики данных. В Timeweb Cloud OpenSearch доступен как управляемый сервис, что упрощает его интеграцию и обслуживание. 

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

❯ Когда стоит выбирать NoSQL?

Системы NoSQL не предоставляют универсального решения для всех типов задач. При проектировании архитектуры хранения данных необходимо учитывать специфику проекта и стратегию его долгосрочного развития.

Выбирайте NoSQL, когда для вас важны:

  1. Масштабные информационные потоки

  • Эффективная обработка петабайтных хранилищ

    1. Интенсивный поток транзакций чтения и записи

    2. Потребность в горизонтальной масштабируемости

  1. Динамическая структура информации

  • Эволюционирующие требования к данным

    1. Гибкость в условиях неопределенности требований

  1. Приоритизация производительности

  • Системы с высокой нагрузкой

    1. Приложения реального времени

    2. Сервисы с требованиями высокой доступности

  1. 4. Нестандартные форматы данных

  • Сетевые структуры взаимосвязей

    1. Последовательности с временными метками

    2. Пространственное позиционирование

Оставайтесь с реляционными БД, когда для вас важны:

  1. Гарантированная целостность

  • Банковские транзакции

    1. Электронные медицинские карты

    2. Системы стратегического значения

  1. Комплексные взаимосвязи

  • Многоуровневые объединения данных

    1. Сложные транзакционные операции

    2. Обязательное соответствие ACID

  1. Неизменная структура

  • Фиксированная спецификация требований

    1. Унифицированные бизнес-процессы

    2. Формализованная система отчетности

❯ Практические рекомендации

  1. Гибридный подход 

// Использование Redis для кэширования
// вместе с PostgreSQL для основных данных
const cached = await redis.get(`user:${id}`);
if (!cached) {
    const user = await pg.query('SELECT * FROM users WHERE id = $1', [id]);
    await redis.set(`user:${id}`, JSON.stringify(user));
    return user;
}
return JSON.parse(cached);
  1. Постепенный переход

  • Начните с пилотного проекта 

    1. Тестируйте производительность 

    2. Оценивайте затраты на поддержку

Факторы для принятия решения

  1. Технические аспекты 

  • Объем данных 

    1. Типы запросов 

    2. Требования к масштабированию 

    3. Модель согласованности 

  1. Бизнес-требования 

  • Бюджет проекта 

    1. Сроки разработки 

    2. Требования к надежности 

    3. Планы по развитию 

  1. Команда разработки 

  • Опыт работы с технологиями 

    1. Доступность специалистов 

    2. Сложность поддержки


Новости, обзоры продуктов и конкурсы от команды Timeweb.Cloud — в нашем Telegram-канале 

Перейти ↩

Перед оплатой в разделе «Бонусы и промокоды» в панели управления активируйте промокод и получите кэшбэк на баланс.

📚 Читайте также: