Основы дизайна систем: базы данных

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

Реляционные базы данных

Реляционная база данных - это база данных, в которой строго установлены отношения между вещами, хранящимися в базе данных. Эти отношения обычно становятся возможными благодаря требованию, чтобы база данных представляла каждую такую вещь (называемую «сущностью» (entity)) в виде структурированной таблицы - с нулем или несколькими строками («записи», «вхождения») ("records", "entries") и одним или несколькими столбцами («атрибуты», «поля») ("attributes", "fields").

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

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

Большинство реляционных баз данных поддерживают язык запросов к базе данных, называемый SQL - язык структурированных запросов. Это язык, специально разработанный для взаимодействия с содержимым структурированной (реляционной) базы данных. Эти две концепции настолько тесно связаны, что люди часто называют реляционную базу данных «SQL базой данных».

В целом считается, что (реляционные) SQL базы данных поддерживают более сложные запросы (сочетающие различные поля, фильтры и условия), чем нереляционные базы данных. Сама база данных обрабатывает эти запросы и отправляет результаты сопоставления.

Многие фанаты SQL баз данных утверждают, что без этой функции вам пришлось бы получить все данные, а затем заставить сервер или клиент загрузить эти данные «в память» и применить условия фильтрации - что нормально для небольших наборов данных, но для большого и сложного набора данных с миллионами записей и строк это сильно повлияет на производительность. Однако это не всегда так, как мы увидим, когда узнаем о NoSQL базах данных.

Распространенным и всеми любимым примером реляционной базы данных является база данных PostgreSQL (часто называемая «Postgres»).

ACID

Транзакции ACID - это набор функций, описывающих транзакции, которые будет поддерживать хорошая реляционная база данных. ACID = "Atomic, Consistent, Isolation, Durable" (атомарный, согласованный, изолированный, долговечный). Транзакция - это взаимодействие с базой данных, обычно операции чтения или записи.

Атомарность требует, чтобы, когда одна транзакция состоит из более чем одной операции, база данных должна гарантировать, что если одна операция завершится неудачей, вся транзакция (все операции) также завершится неудачей. Это «все или ничего». Таким образом, если транзакция завершилась успешно, то по ее завершении вы знаете, что все подоперации завершились успешно, а если операция завершилась неудачно, вы знаете, что все операции, которые были с ней выполнены, завершились неудачно.

Например, если одна транзакция связана с чтением из двух таблиц и записью в три, то, если какая-либо из этих отдельных операций завершается неудачно, вся транзакция завершается неудачей. Это означает, что ни одна из этих отдельных операций не должна завершаться. Вы бы не хотели, чтобы работала даже 1 из 3 транзакций записи - это «испортит» данные в ваших базах данных!

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

Изоляция означает, что вы можете «одновременно» (concurrently) (в одно и то же время) запускать несколько транзакций в базе данных, но в конечном итоге база данных будет иметь состояние, которое выглядит так, как будто каждая операция выполнялась последовательно (в последовательности, например, очередь операций).

Долговечность - это обещание, что после того, как данные будут сохранены в базе данных, они останутся таковыми. Он будет «постоянными» - храниться на диске, а не в «памяти».

Нереляционные базы данных

Напротив, нереляционная база данных имеет менее жесткую или, другими словами, более гибкую структуру своих данных. Данные обычно представлены в виде пар "ключ-значение". Проще всего это представить в виде массива (списка) объектов пары «ключ-значение», например:

[
    { 
        name: "Jacob",
        rank: ##,
        gender: "M",
        year: ####
    },
    { 
        name: "Isabella",
        rank: ##,
        gender: "F",
        year: ####
    },
    {
      //...
    },
    
    // ...
]

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

Подобно свойствам ACID, свойства базы данных NoSQL иногда называют BASE:

В основном доступно (Basically Available), что означает, что система гарантирует доступность

Мягкое состояние (Soft State) означает, что состояние системы может меняться со временем, даже без ввода

Окончательная согласованность (Eventual Consistency) означает, что система станет согласованной в течение (очень короткого) периода времени, если не будут получены другие входные данные.

Поскольку по своей сути эти базы данных содержат данные в виде структуры, подобной хеш-таблице, они чрезвычайно быстры, просты и удобны в использовании и идеально подходят для таких случаев, как кэширование, переменные среды, файлы конфигурации, состояние сеанса и т. д. Гибкость делает их идеальными для использования в памяти (например, Memcached), а также в постоянных хранилищах (например, DynamoDB).

Существуют и другие «JSON-подобные» базы данных, называемые базами данных документов (document databases), такие как всеми любимый MongoDB, и по своей сути они также являются хранилищами «ключ-значение».

Индексирование базы данных

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

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

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

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

Репликация и сегментирование (Replication и Sharding)

Вы с большой вероятностью будете слышать их каждый день в контексте масштабирования базы данных.

Репликация означает дублирование (создание копий, репликация) вашей базы данных.

Мы рассмотрели преимущества резервирования в системе для поддержания высокой доступности. Репликация обеспечивает избыточность базы данных в случае ее выхода из строя. Но это также поднимает вопрос о том, как синхронизировать данные между репликами, поскольку они должны иметь одни и те же данные. Репликация операций записи и обновления в базу данных может происходить синхронно (одновременно с изменениями в основной базе данных) или асинхронно.

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

Но что делать, если у вас так много данных, что простая их репликация может решить проблемы с доступностью, но не решить проблемы с пропускной способностью и задержкой (скоростью)?

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

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


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

Комментарии

Популярные сообщения из этого блога

Язык поисковых запросов в Graylog

Нормальные формы, пример нормализации в базе данных

Хэш-таблица: разрешение коллизий