Atomicity — Атомарность
Гарантирует, что каждая транзакция будет выполнена полностью или не будет выполнена совсем.
Consistency — Согласованность
Гарантирует, что транзакция переводит базу данных из одного согласованного состояния в другое.
Isolation — Изолированность
Во время выполнения транзакции параллельные транзакции не должны оказывать влияния на её результат.
Durability — Надёжность
Гарантирует, что после успешного завершения транзакции все изменения, сделанные в рамках этой транзакции, сохраняются в базе данных даже в случае сбоя системы.
Две транзакции меняют один и тот же блок данных параллельно и одно из изменений теряется
Первая транзакция поменяла данные, вторая их прочла, но первая откатилась
Первая транзакция прочла это поле, вторая транзакция изменила это поле, первая транзакция спустя время снова прочла это поле
Как неповторяющееся чтение, но тут не изменили поля, а добавили новые поля из-за которых выборка поменяла результат
SUM() стал другим из-за новых чисел
Блокировка данных, на время выполнения команды изменения.
Помогает предотвратить проблему потерянного обновления
Помогает предовратить грязное чтение
Уровень, при котором читающая транзакция «не видит» изменения данных, которые были ею ранее прочитаны.
Помогает предотвратить неповторяющееся чтение
Результат выполнения нескольких параллельных транзакций должен быть таким, как если бы они выполнялись последовательно.
Помогает предотвратить фантомное чтение
Кластеризованный индекс VS индекс
Кластеризованный индекс — это индекс, который сортирует строки с данными в таблице. Он хранит данные в листьях индекса, где все значения отсортированы в определённом порядке — либо по возрастанию, либо по убыванию. Благодаря этому существенно возрастает скорость поиска данных (при условии последовательного доступа к данным). В таблице может присутствовать только один кластеризованный индекс.
Некластеризованный индекс — индекс, который используется для применения индексов к неключевым столбцам. Главное отличие от кластеризованного индекса заключается в том, что некластеризованный индекс не упорядочивает данные физически. Он хранит данные и индексы в разных местах. Листья некластеризованного индекса содержат только те столбцы таблицы, по которым определён данный индекс. Это означает, что системе запросов необходима дополнительная операция для извлечения требуемых данных. Некластеризованные индексы нельзя отсортировать, в отличие от кластеризованных, однако существует возможность создания более одного некластеризованного индекса.
Каждая запись в таблице А, связана с одной и только одной записью в таблице B (уникальность записи)
Каждая запись в таблице А может быть связана с одной или несколькими записями через внешний ключ
Любая запись в таблице А может быть связана с любой записью в таблице B (промежуточная таблица)
FROM
JOIN
WHERE
GROUP BY
HAVING
SELECT
COUNT(*)OVER() – если есть оконная ф-ция
QUALIFY --фильтрация по оконной ф-ции (в таких бд как Clickhouse или Snowflake)
DISTINCT
ORDER BY
OFFSET
LIMIT
B-tree: Самый популярный. Поддерживает >, < =, >=, <=, LIKE ‘abc%’, NULL, IS NULL, логарифмическая сложность O(log n)
Hash: Используется для быстрого поиска по равенству. (когда много операций =)
GIN: Подходит для массивов и полнотекстового поиска.
GiST: Используется для геометрических данных и полнотекстового поиска.
SP-GiST: Для наборов данных, которые подразумевают естественную упорядоченность, например телефонная книга
BRIN: Полезен на огромных наборов данных, которые упорядочены
Оконная ф-ция определяется ключевым словом OVER.
Планировщик будет решать какое сканирование использовать для извлечения данных(если индексов нет, то полное, а если есть то он будет думать)
index scan - Если есть большой набор проиндексированных данных и мы осуществляем поиск по индексу и потенциально в результирующий набор попадает малое кол-во строк(например 1), а в таблице содержится 1000000 строк, то оптимизатор включит index scan.
index only scan - Некоторые индексы(не все), хранят вместе с идентификаторами строк сами проиндексированные значения и это позволяет им просто читать сам индекс без обращения к таблицам и забирать результат прямо из самого индекса. Вместо чтения значения из таблиц при таком сканировании необходимо лишь заглянуть в карту видимости, чтобы выяснить актуальность индексных записей. Потому что карта видимости ставит 1, показывая что можно выполнить такое сканирование и данные актуальные
bitmap scan - надо выбрать кол-во строк не так мало как при index scan, но и не так много как при sequential scan. Например 300к из млн
sequential scan - Даже если поиск идёт по индексированному столбцу, а там куча данных и так забирать надо, то планировщик может наплевать и выбрать последовательное сканирование.
DDL - для работы с объектами БД(такие как таблицы, функции, индексы)
DML - для работы с данными внутри объектов (записи)
DCL - привилегии ролям даём (GRANT - дать, REVOKE - забрать привилегии у роли)
TCL - для контроля любой транзакции (автокоммит по дефолту есть, который выполняет за вас СУБД)
Self JOIN (Таблица соединяется сама с собой)
Одна таблица(например какого-то подразделения) где надо найти у какого начальника какой подчиненный
Right JOIN
Берутся все данные с правой таблицы и объединяются с теми данные которые есть в левой таблице(по ключу). Если есть значения в правой таблице, но по ключу в левой нет к ним данных, то будет NULL писаться
Full JOIN
Полное объединение левой и правой таблицы, если есть не совпадающие значения по ключам, то будут NULL
Inner JOIN
Только пересечение
CROSS JOIN
Каждая строка одной таблицы, совмещается с каждой строкой другой таблицы (декартово произведение).
Например (найти пару самых продаваемых продуктов, cross join + distinct)
Left JOIN
Как Right JOIN но наоборот
Nested loops join (соединения вложенных циклов)
Вложенные циклы. каждая строка из одной таблицы сравнивается с каждой строкой из другой таблицы и соединяются
Merge join (соединение слиянием)
Самые быстрый. Используется когда 2 большие таблицы. Требует чтобы обе таблицы имели индекс по ключу(т.е отсортированы).
Одна строка сравнивается с другой, когда нет совпадения, то переключается на другую строку, которая сравнивается с этой
Если повторяющиеся значения:
Записывает любые повторяющиеся значения из второй таблицы во временную таблицу и выполняет сравнения там. Затем, если эти значения также дублируются в первой таблице, сравнивает их со значениями, которые уже сохранены во временной таблице.
Hash match join (хеш-соединение)
Используется для больших таблиц, когда отсутствуют индексы или когда данные не отсортированы, а условие соединения предполагает точное совпадение
Берем меньшую из таблиц, для каждой строки создаем запись в хэш-таблице. Затем берем большую, сканируем и проверяем каждую строку на совпадение с хэш-таблицей, если совпадают, то соединяем
Есть внешняя таблица, есть внутренняя. Из внешней мы берем одно значение, отправляем через какую-то хэш-функцию и результат этой хэш-функции приходит в какой-то бакет. Потом мы получаем ключ от другой таблицы и проверяем, есть ли этот ключ в бакете, если есть, то попадается в результат
varchar - значение переменной длины(можем ограничивать значение которое мы можем вставлять) [переменные символы, например ФИО]
char - значение фиксированной длины(именно такой длины, не больше, не меньше) [когда определенно ограниченной длины, например номер телефона]
Разница VARCHAR(10) и CHAR(10) в том что CHAR(10) добивает пустые значения до 10 байт. Считывание по CHAR быстрее, т.к. VARCHAR имеет доп информацию о том сколько места занимает это значение
text - сколько хотим, столько и пишем
interval - нужен чтобы к дате можно было прибавить определенный промежуток времени
uuid - позволяет строить уникальный идентификатор вне зависимости от значения(для хэш индексов например)
В разных БД(Например Clickhouse) кол-во агрегатных ф-ций больше
В RANK пропускаем значение при дубликатов, в отличие от DENCE_RANK
Если нам важна последовательность то DENCE_RANK
ROW_NUMBER просто считает строки последовательно
LAG - колонка сдвинется на 1(или n) вверх
LEAD - колонка сдвинется на 1(или n) вниз
FIRST_VALUE - первое значение из окна таблицы
LAST_VALUE - последнее значение из окна таблицы
Оконная ф-ция определяется ключевым словом OVER. А само окно определяется с помощью PARTITION BY
COUNT по столбцу не учитывает NULL
EXPLAIN - дает план запроса, который БД будет выполнять на основе статистических данных, которые БД сама высчитывает.
EXPLAIN ANALYZE - фактическое время и затраты при выполнении запроса. Если запрос выполняется очень долго, то делать EXPLAIN ANALYZE нет смысла.
VACUUM - проверяет индексы, удаленные строки, неудаленные строки.(DELETE не удаляет строку физически[типо просто помечает галкой], её можно откатить)
ANALYZE - обновляет статистику. Если вы вставили очень много строк, не поленитесь сделать ANALYZE
Хинты в Greenplum
https://habr.com/ru/companies/X5Tech/articles/851386/
Сначала посмотрю. Если запрос выполнялся долго то выполню команду EXPLAIN, посмотрю на cost'ы, если они огромные в каком-то шаге, то буду этот запрос переписывать(именно этот промежуток), посмотрю на подзапросы, возможно человек не делал CTE(а сделал 5 одинаковых подзапросов, которые на каждом шаге выполнялся заново и строил временную таблицу).
Я бы выполнил команду ANALYZE, может у меня до этого миллион строк залили, потом удалили через DELETE команду и залили миллиард, а статистика осталась на том миллионе, поэтому я бы выполнил команду ANALYZE или VACUUM ANALYZE
Можно много всего накидывать тут
Откуда берутся cost. Это мнимые стоимости, но на них надо смотреть
В плане запросов может быть написано SPILL и кол-во строк. SPILL file означает, что не хватило у нас оперативной памяти и БД пришлось выгружать какое-то кол-во данных на диск локальный(SSD или HDD) это плохая тенденция. Надо делать так, чтобы их не было.
Все арифметические действия с NULL вернут NULL
Могут спросить (в постгресе нет функции div0, деления на 0) а как сделать так, чтобы деление на 0 возвращало пустое значение, без ошибки.
Надо чтобы делитель был = NULL. Можно достичь этого с помощью ф-ции NULLIF. Либо с помощью CASE WHEN
При COUNT(*) считаются все строки в столбце id, не важно NULL они или нет
При COUNT(id) с указанием конкретной колонки уже не учитываются NULL значения
SUM, AVG, MIN, MAX тоже не учитывают NULL
Когда мы выполняем AVG по колонке, мы не учитываем значения NULL, это эквивалентно SUM(id)::numeric/COUNT(id)
Но если мы хотим при подсчете среднего учитывать NULL значения то необходимо писать SUM(id)::numeric/COUNT(*)
Либо второй вариант, где мы NULL заменяем 0, AVG(COALESCE(id, 0))
Примечание: numeric пишется, чтобы при делении целого числа на целое число вернулось с остатком(одно из чисел должно быть десятичным)
Мы не можем ставить операции >, <, = NULL и т.д. Фильтровать можно только IS NULL или IS NOT NULL
На id != null некоторые СУБД дают ошибку, постгрес на это выдаст NULL
id != null
Могут спросить, а как отсортировать чтобы в вначале были NULL, либо в конце. В Postgres, Greenplum и Clickhouse
Разница COUNT(id) в том что он не считает NULL, в отличие от остальных.
COUNT(*) отличается от COUNT(1) ничем. cost у них одинаковый. Можно что угодно вставлять в COUNT() он будет всё равно воспринимать его как COUNT(*), константа
SELECT col1, COUNT(1) FROM t1 GROUP BY col2
SELECT DISTINCT col1, COUNT(*) OVER(PARTITION BY col2) FROM t1
Ничем не отличаются, в плане результата, в плане выполнения с оконной ф-цией медленнее будет выполняться.
Эта проверка что оконная ф-ция проставляет своё значение для каждого значения
Есть 2 таблицы 10 строк и 100 строк. Какое максимальное и минимальное кол-во возвращаемых строк при разных видах JOIN будет?
Вариативный вопрос. Может быть 10 строк и 10 строк, 5 строк и 7 строк. Ответ не меняется
У тебя есть 100 миллионов строк и тебе необходимо удалить 90 миллионов, как ты это сделаешь и почему?
Проверка на понимание работы БД. Пример с постгресом, таблицы хранятся как heap(куча). Есть сегмент = 1 ГБ, который внутри состоит из страниц данных, вроде 1 стр = 8 Кб. Есть определенный заголовок версии строки и сама строка данных. Надо понимать, если мы хотим 90 миллионов просто удалить, например пишем DELETE FROM .. TABLE WHERE id > 10.. То физически они не удалятся из этой таблицы. В заголовке строки есть 2 параметра типо xmin и xmax. в xmin ставится номер транзакции когда данная строка обновлена или удалена или что-то ещё. А в xmax ставится дата окончания, когда эту строку либо обновили, либо удалили. То есть номер транзакции которая удалила эту строчку. Мы просто в эти 90 миллионов ставим xmax(Номер транзакции которая удалила эту строку). Соответственно это долго. Есть пример 2х способов(на самом деле их больше)
В основе хотят услышать, что мы должны не просто в тупую удалять строки через DELETE, а именно удалить саму таблицу, потому что DROP TABLE и TRUNCATE TABLE работаю так, что они удаляют таблицу.
TRUNCATE(запоминает структуру таблицы, удаляет сегментный файл, а потом пересоздает его, DDL, но индексы и статистику не обновляет)
DROP(удаляет всё и строки и статистику и индексы)
Снизу 2 способа.
Есть запрос, который работает ночью и строит отчет. Ежедневно он работал нормально и создавал отчет за 2 часа. Сегодня утром ты пришёл на работу, а отчета нет. Смотришь свой пайплайн, а он все еще крутится на чтении запроса. Что могло произойти?
Этот вопрос на воображение. Обычно от нас хотят услышать, что мы залезем в EXPLAIN, посмотрим какой у нас скрипт, посмотрим cost, не навернулась ли у нас статистика, проверить SPILL есть ли и от этого уже отталкиваться.
Что могло произойти?
Например товарищ что-то так настроил и допустил ошибку, что записи повторяются циклично и кучу раз повторились
Вакуум не успел собрать статистику
Что такое и для чего нужен подзапрос? Что такое коррелируемый и не коррелируемый подзапрос? В чем отличие СТЕ от подзапроса?
Коррелируемый подзапрос — Внутри подзапроса пишем колонку из внешнего запроса. Подзапрос выполняется для каждой строчки. Часто используются в конструкции EXIST
Пример коррелируемого подзапроса:
Некоррелируемый подзапрос — Выполнятся 1 раз и результат используется внутри внешнего запроса
Пример некоррелируемого подзапроса:
Общие табличные выражения (CTE) — Создает временную таблицу, которую можно переиспользовать. [Сохраняется в кэше и можно переиспользовать]
Когда использовать?
Хорошая читаемость кода
Идёт какой-то большой джойн, а потом по этому джойну идут какие-то дополнительные вычисления(дальнейшие).
Для чего нужна команда UNION и в чем её различим с UNION ALL? Какая команда работает быстрее и почему? Какие еще операции над множествами ты знаешь и что они делают?
UNION:
Удаляет дублирующиеся строки.
Может быть медленнее из-за необходимости проверки на дубликаты.
UNION ALL:
Включает все строки, включая дубликаты.
Обычно работает быстрее, так как не требует проверки на дубликаты.
INTERSECT - выбирает общие значения из 2х множеств
EXCEPT - вычитает одно множество из другого
Расскажи про условия в SQL. Для чего нужна конструкция CASE, как она записывается и в каких конструкциях запроса её можно использовать?
Может использоваться в различных частях SQL-запроса, таких как SELECT, WHERE, ORDER BY и других.
Различие между конструкциями WHERE, HAVING, QUALIFY?
WHERE фильтрует данные до группировки
HAVING фильтрует данные после группировки
QUALIFY фильтрует данные после применения оконных ф-ций
Пример QUALIFY
Что такое HDFS?
HDFS (Hadoop Distributed File System) — это распределенная файловая система, разработанная для хранения и обработки больших объемов данных на кластерах из недорогого оборудования.
Характеристики:
Масштабируемость (увеличение узлов)
Отказоустойчивость (репликации)
Высокая пропускная способность (Оптимизирована для пакетной обработки больших объемов данных)
Что такое spark и какие ядра в него входят?
Это фреймворк с открытым исходным кодом для реализации распределённой обработки данных.
Он был разработан как альтернатива Hadoop MapReduce, предлагая более высокую производительность и удобство для сложных задач анализа данных
В него входят
MLlib. Набор библиотек для машинного обучения.
SQL. Компонент, который отвечает за запрос данных.
GraphX. Модуль для работы с графами (абстрактными представлениями связей между множеством объектов).
Streaming. Средство для обработки потоков Big Data в реальном времени.
Что такое map reduce?
MR - модель распределённых вычислений. Либо парадигма обработки данных
Суть MapReduce состоит в разделении информационного массива на части, параллельной обработке каждой части на отдельном узле и финальном объединении всех результатов.
Map - предварительная обработка данных в виде большиго списка значений.
Master распределяет данные по Worker-ам, worker-ы делают Map.
Shuffle - рабочие узлы (Spark Worker) перераспределяют данные на основе ключей (ранее созданных в Map) таким образом, чтобы все данные одного ключа лежали на одном работчем узле.
Reduce - параллельная обработка каждым Worker-ом по порядку следования ключей и склеивание на Master Node.
Что такое спарк сессия и какими параметрами она задается? В чем отличие спарк сессии от спарк контекста?
SparkSession — это точка входа для использования всех функций Apache Spark
Параметры, которыми задаётся SparkSession:
appName — имя приложения
config — параметры конфигурации, например, для настройки источника данных (CSV-файлы, JSON-файлы, базы данных) и записи данных в эти источники.
SparkContext использовали до версии 2.0
Как выполняется работа приложения в спарке (workflow) ? (Когда выделяется память, когда отдаются ресурсы)
Workflow приложения в Spark:
Приложение запускается и инициализирует SparkContext.
Программа-драйвер запрашивает у менеджера кластеров ресурсы для запуска исполнителей.
Менеджер кластеров запускает исполнителей.
Драйвер запускает код Spark.
Исполнители запускают задания и отправляют результаты драйверу.
SparkContext останавливается, а исполнители закрываются и возвращают ресурсы обратно в кластер.
Выделение ресурсов в Spark может происходить двумя способами:
Статическое выделение. Объём памяти и количество ядер выделяют на этапе запуска приложения и оставляют неизменными в течение всего выполнения. Подходит для задач, в которых требуется предсказуемое и стабильное распределение ресурсов на протяжении всего выполнения.
Динамическое выделение. Система автоматически адаптирует объём памяти и количество ядер исполнителей, чтобы удовлетворить текущим потребностям приложения. Этот метод позволяет управлять ресурсами в режиме реального времени, что делает его подходящим для приложений с переменной нагрузкой.
Когда отдаются ресурсы, при динамическом выделении приложение может возвращать ресурсы в кластер, если они больше не используются, и запрашивать их снова позже, когда появится спрос.
Что такое преобразование и действие в спарке?
Ленивые вычисления
Преобразование в Spark — это операция над коллекциями данных, результатом которой служат новые данные. Вычисление преобразованных данных откладывается до того момента, когда к ним будут применены действия.
Действие в Spark — это тип операции, который возвращает конкретное значение. Действия применяются, когда необходимо вывести конкретное значение в консоль. Примеры действий: collect(), take(n) и count() и show().
Что такое драйвер и экзекьютор? Spark
Драйвер в Spark — это управляющий процесс, который запускает метод main() приложения. Он создаёт объекты SparkSession и SparkContext, а также преобразует код в операции преобразования и действия. Также драйвер создаёт логические и физические планы, планирует и координирует задачи с менеджером кластера.
Исполнитель в Spark — распределённый процесс, который отвечает за выполнение задач. У каждого приложения Spark собственный набор исполнителей. Они делают всю обработку данных задания Spark. Исполнители сохраняют результаты в памяти, а на диске — только тогда, когда это специально указывается в программе-драйвере. Возвращает результаты драйверу после их завершения.
Что такое rdd, dataset и dataframe? Как rdd работает "под капотом"?
RDD (Resilient Distributed Dataset) — это неизменяемая распределённая совокупность элементов данных, которые могут храниться в памяти или на диске в кластере машин. RDD позволяют совершать низкоуровневые трансформации над неструктурированными данными (как медиа или текст).
DataFrame — это распределённая коллекция данных, организованная в именованные столбцы. Концептуально он соответствует таблице в реляционной базе данных с оптимизацией для распределённых вычислений.
Dataset — это расширение API DataFrame, обеспечивающее функциональность объектно ориентированного подхода, производительность оптимизатора запросов Catalyst и механизм хранения вне кучи.
RDD работают «под капотом» путём разделения данных на несколько разделов (Partition), которые хранятся на каждом узле-исполнителе. Каждый узел выполняет работу только на собственных разделах. RDD отказоустойчивы, так как отслеживают поток данных для автоматического восстановления потерянных данных в случае сбоя.
В чем разница форматов avro, parquet, orc? Когда что лучше использовать?
Avro — это строковый формат, который хранит схему в формате JSON, облегчая её чтение и интерпретацию любой программой. Сами данные лежат в двоичном формате, компактно и эффективно. Лучше подходит для операций записи, где данные нужно быстро сериализовать и десериализовать.
Parquet — колоночный формат, который оптимизирован для операций чтения и аналитики. Он эффективен в плане сжатия и кодирования, что делает его идеальным для сценариев, где важна эффективность чтения и хранения.
ORC — колоночный формат, похожий на Parquet, но оптимизированный для операций чтения и записи. Он эффективен в плане сжатия, что снижает затраты на хранение и ускоряет извлечение данных.
Что такое hdfs dfs?
Команда для взаимодействия с HDFS
Джоба работала 7 дней, потом упала, заказчик не получил результат. В чем проблема и где начать смотреть?
Возможные причины проблемы:
1. Ошибка в коде:
Непредвиденная ошибка в логике программы
Проблема с обработкой данных или выходными данными
2. Отсутствие ресурсов:
Выделение недостаточных ресурсов для выполнения задачи
Перегрузка кластера Hadoop
3. Проблемы с конфигурацией:
Неправильная настройка параметров Spark или Hadoop
Конфликт между версиями компонентов
4. Сбой инфраструктуры:
Проблемы с сетевым соединением
Сбой узлов кластера
5. Проблемы с мониторингом:
Отсутствие эффективного мониторинга процесса
Неисправность системы мониторинга
Где начать поиск:
1. Логи:
Проверьте логи джобы и кластера
Используйте команды:`cat /path/to/job/logs/`* `tail -f /path/to/job/logs/latest.log`
2. Метрики производительности:
Проверьте метрики CPU, памяти и дискового пространства
Используйте инструменты мониторинга, такие как Nagios или Prometheus
3. Статус задачи:
Проверьте статус задачи через интерфейс управления задачами (например, YARN UI)Используйте команду:
`yarn application -list`4. Результаты выполнения:
Проверьте директорию с результатами выполнения
Используйте команду:
`hadoop fs -ls /path/to/results/directory`
5. Мониторинг ресурсов:
Проверьте использование ресурсов кластера
Используйте команду:* `yarn cluster-info`
В чем разница между CTE и подзапросами?
Некоторые другие отличия:
CTE определяются в начале запроса, а подзапросы — inline.
CTE всегда имеют имя, а подзапросы — только в PostgreSQL.
CTE можно использовать много раз внутри запроса, а подзапрос — только один раз.
Подзапросы можно использовать в предложении WHERE в сочетании с ключевыми словами IN или EXISTS, а с CTE это сделать нельзя.
Можно ли совмещать использование GROUP BY и оконных функций? Как это сделать?
Совмещать использование GROUP BY и оконных функций в SQL можно, но с определёнными ограничениями.
В соответствии с порядком логической обработки запросов, оконные функции обрабатываются на этапе SELECT или ORDER BY, то есть после GROUP BY. Поэтому при оценке GROUP BY оконные функции ещё не вычислены.
Один из способов совместить эти понятия — использовать подзапрос. В нём вычислить оконную функцию, а затем использовать её в основном запросе. Например:
SELECT quartile, min(points), max(points), count(*) FROM (SELECT ntile(4) OVER (ORDER BY points) AS quartile, points FROM midterm) groups GROUP BY quartile
В подзапросе используется функция NTILE() для разделения студентов на группы, а в основном запросе вычисляется статистика: минимум, максимум и количество студентов.
Ещё одна возможность — использовать общее табличное выражение (CTE). Его можно определить на основе запроса, вычисляющего групповой агрегат, а во внешнем запросе вычислить оконный агрегат.
В чем разница при использовании GROUP BY и оконных функций?
Разница между использованием GROUP BY и оконных функций в SQL заключается в следующем:
GROUP BY сокращает количество строк в запросе с помощью их группировки. После группировки остаётся только одна запись для каждого значения, использованного в столбце.
Оконные функции не уменьшают количество строк в запросе по сравнению с исходной таблицей. Они работают с выделенным набором строк (окном) и выполняют вычисление для этого набора строк в отдельном столбце. Результат работы оконных функций добавляется к результатирующей выборке в ещё одно поле.
Кроме того, оконные функции могут вычислять скользящие средние и кумулятивные суммы, а также обращаться к другим строкам.
В чем разница между COUNT(*) и COUNT(название_столбца)?
Разница между функциями COUNT(*) и COUNT(имя столбца) в SQL заключается в том, что COUNT(*) возвращает общее количество строк в таблице, включая те, где есть значения NULL, а COUNT(имя столбца) — количество всех ненулевых значений в определённой колонке.
Таким образом, COUNT(*) учитывает все без исключения, а COUNT(имя столбца) исключает пустые значения и подсчитывает только заполненные поля.
Чем отличаются функции и хранимые процедуры?
Функции и хранимые процедуры отличаются по следующим параметрам:
Возвращаемые значения. Функции всегда возвращают значение. Хранимые процедуры не обязательно возвращают значения.
Вызов. Функции могут вызываться внутри SELECT-запросов, либо вызываться как результат самого запроса. Хранимые процедуры вызываются командой CALL название_процедуры()
Как удалить дубликаты строк в SQL?
Для удаления дубликатов строк в SQL можно использовать следующие методы:
Метод с дублирующей таблицей. Нужно переместить один экземпляр любой повторяющейся строки в исходной таблице в дублирующую таблицу, затем удалить все строки из исходной таблицы, которые также находятся в дублирующей таблице. После этого строки в дублирующей таблице перемещают обратно в исходную таблицу и удаляют дублирующую таблицу.
Метод с функцией ROW_NUMBER. Функция ROW_NUMBER разделяет данные на основе определённого столбца (или нескольких столбцов, разделённых запятыми) и удаляет все записи, которые получили значение, превышающее 1. Это указывает на то, что записи являются дубликатами.
Перед удалением данных рекомендуется создать резервную копию таблицы.
Как найти медиану в таблице? SQL
Также в PostgreSQL для нахождения медианы можно использовать функцию percentile_count:
SELECT percentile_cont (0.5) WITHIN GROUP (ORDER BY sales) FROM table
Что такое колоночная база данных, в чем ее преимущества и недостатки?
Колоночная база данных — это система управления базами данных, которая хранит данные столбцами, а не строками, как в традиционных реляционных базах данных.
Преимущества колоночных баз данных:
Ускорение аналитических запросов. При агрегациях и фильтрациях задействуются только те столбцы, которые участвуют в запросе. При этом данные одного типа лежат последовательно.
Эффективное сжатие данных. Данные в одном столбце имеют схожую природу, что позволяет применять более эффективные алгоритмы сжатия.
Снижение нагрузки на I/O. Извлечение только нужных столбцов уменьшает объём операций ввода-вывода.
Недостатки колоночных баз данных:
Меньшая эффективность для транзакционных операций. Записи и обновления, которые влияют на большое количество столбцов, требуют больше операций, чем в реляционных базах.
Усложнение работы с несжатыми или разреженными данными. Если данные столбца не поддаются сжатию или содержат много пропусков, преимущества колоночного подхода снижаются.
Основные движки ClickHouse
MergeTree. Базовый движок для создания таблиц в ClickHouse. Обеспечивает быструю вставку, поддерживает большие объёмы данных.
ReplacingMergeTree. Позволяет «схлапывать» значения по ключу сортировки. Это помогает исключить дублирование значений по ключу сортировки и держать таблицу в актуальном состоянии.
Log. Простые движки с минимальной функциональностью. Они эффективны, когда нужно быстро записать много небольших таблиц (до примерно 1 миллиона строк) и прочитать их позже целиком.
Движки для интеграции. Используются для связи с другими системами хранения и обработки данных. Например, Kafka, MySQL, ODBC, JDBC.
Memory. Хранит данные в памяти, подходит для небольших таблиц объёмом менее 100 миллионов строк, которые не требуют сохранения данных.
Какие бывают магические функции в Python?
Некоторые виды магических методов в Python:
Метод init. Используется для инициализации объекта. Вызывается автоматически при создании нового экземпляра класса.
Методы int, float и complex. Преобразуют сложные объекты в примитивный тип int, float и complex соответственно.
Метод bool. Принимает один позиционный аргумент и возвращает либо true, либо false. Его цель — проверить, является ли объект true или false, либо явно преобразовать в логическое значение.
Методы str и repr. Определяют строковое представление объекта и его машиночитаемое представление соответственно.
Методы getattr(self, name), setattr(self, name, value) и delattr(self, name). Вызываются при обращении к атрибуту класса, который не существует, при назначении значения атрибуту класса и при удалении атрибута класса соответственно.
Что такое декораторы в Python?
Декораторы в Python — это функции, которые принимают другую функцию в качестве аргумента, добавляют к ней дополнительную функциональность и возвращают функцию с изменённым поведением.
Они позволяют динамически менять, расширять, дополнять логику и поведение функций, классов, методов без изменения исходного кода.
Чем отличается итератор от генератора?
Итератор и генератор — это два понятия в языке Python, которые позволяют работать с коллекциями данных.
Итератор — это объект, который обеспечивает доступ к элементам коллекции по одному. Он может работать с любым видом коллекции, а не только с последовательностью значений.
Генератор — это особый тип функции, позволяющий создавать последовательность значений по одному, вместо того чтобы возвращать всю последовательность сразу. Когда вызывается функция-генератор, она возвращает объект-итератор, который управляет потоком данных из генератора. Функция генератора продолжает выполняться до тех пор, пока не достигнет конца своей итерации, после чего она прекращает выполнение.
Какие типы данных в Python являются изменяемыми и неизменяемыми?
# Что выведет print(a)?
a = [1,2,3]
b = a
b.append(4)
[1,2,3,4]
Переменная b становится ссылкой на тот же список, что и a. Это означает, что a и b теперь указывают на один и тот же объект в памяти.
b
a
Можно ли в словарь в key записать изменяемый тип? Почему?
Использовать изменяемый тип данных в качестве ключа в словаре Python нельзя, это приведёт к ошибке.
Ключи в словарях Python должны быть неизменяемыми типами данных, такими как строки, числа или кортежи.
Лямбда функция (что это, зачем, где использовать)
В буквальном смысле, анонимная функция — это функция без имени.
Для чего используют lambda
lambda функция хорошо подходит для сортировки многомерных списков по разным параметрам, например, если нужно отсортировать список словарей по разным ключам.
В чем разница "==" и "is"?
Разница между операторами «==» и «is» в Python заключается в том, что они служат разным целям:
Оператор «==» проверяет равенство значений. Он оценивает, совпадают ли значения двух объектов, и возвращает True, если это так, и False в противном случае. 12
Оператор «is» проверяет идентичность. Он определяет, указывают ли две переменные на один и тот же объект в памяти, и возвращает True, если оба объекта указывают на одно и то же место в памяти, и False в противном случае. 12
Таким образом, «==» сравнивает значения, а «is» проверяет, указывают ли объекты на одну и ту же область памяти.
Self(что это, для чего нужен, как и где использовать)
Self в Python — это ссылка на текущий экземпляр класса. Через self можно получить доступ к атрибутам и методам класса внутри него.
Для чего нужен self:
Доступ к атрибутам и методам экземпляра. С помощью переменной self методы экземпляра могут легко получить доступ к различным атрибутам и другим методам одного и того же объекта.
Изменение состояния экземпляра. Переменная self также способна изменять состояние объекта.
Читаемость и понятность кода. Использование self подчёркивает, что метод применяется к конкретному экземпляру класса, что делает код более ясным и понятным.
Как и где использовать self: self является явным параметром для того, чтобы подчеркнуть, что метод применяется к конкретному экземпляру класса. Это делается при определении метода, когда ему первым аргументом передаётся self. Также переменная self может использоваться для доступа к полю переменной внутри определения класса.
В чем разница между func и func()?
Разница между func и func() в Python заключается в том, что первое представляет собой ссылку на функцию, а второе — вызов (выполнение) этой функции.
func — это представляющий функцию объект, который можно назначить переменной или передать другой функции. Когда используется только имя функции без круглых скобок, это означает, что к самой функции обращаются как к объекту, но не вызывают её.
func() — это вызов функции. Когда после имени функции добавляются круглые скобки, это означает, что её код фактически вызывается (выполняется).
Таким образом, использование func просто ссылается на объект функции, не вызывая её код, а с использованием func(), функция вызывается и выполняет свой код
Как рассчитывается сложность алгоритма? на примере list, tuple
Сложность алгоритма рассчитывается с использованием верхней (наихудшей) оценки, которая выражается с использованием нотации O.
Выделяют следующие основные категории алгоритмической сложности в O-нотации:
Постоянное время: O(1). Время выполнения не зависит от количества элементов во входном наборе данных. Пример: операции присваивания, сложения, взятия элемента списка по индексу и др..
Линейное время: O(N). Время выполнения пропорционально количеству элементов в коллекции. Пример: найти имя в телефонной книге простым перелистыванием, почистить ковёр пылесосом и т.д..
Пример расчёта сложности алгоритма на примере списка: если len(alist) — это N, тогда цикл for i in range(len(alist)) будет иметь сложность O(N), так как цикл выполняется N раз.
Ещё один пример: итоговая сложность двух вложенных действий равна произведению их сложностей. Например, если некоторая функция f(...) имеет класс сложности O(N2), а её выполняют в цикле N раз, то сложность этого кода будет равна: O(N) × O(N2) = O(N×N2) = O(N3).
Что такое super() и зачем нужен?
super() в Python — это встроенная функция, которая позволяет дочернему классу ссылаться на свой родительский класс. Она даёт возможность вызывать методы, определённые в суперклассе, из подкласса, что позволяет расширять и настраивать функциональность, унаследованную от родительского класса.
Зачем нужен super():
Позволяет не явно ссылаться на базовые классы по имени. Это удобно для доступа к переопределённым методам и предотвращения дублирования кода.
Гарантирует, что все конструкторы суперклассов вызываются в правильном порядке. Это предотвращает проблемы с инициализацией и позволяет каждому классу в иерархии наследования вносить свой вклад в конечное состояние объекта.
Что такое итерация?
Итерация в Python — это процесс обхода элементов итерируемого объекта. То есть это процедура взятия элементов чего-то по очереди. В более общем смысле — последовательность инструкций, которая повторяется определённое количество раз или до выполнения указанного условия.
Итерируемый объект в Python — это любой объект, от которого можно получить итератор. Такими объектами являются, например, списки, кортежи, строки и словари. Итерируемыми объектами могут быть и пользовательские объекты, если в их классе реализован специальный метод iter().
Итератор в Python — это объект, который реализует метод next(), возвращающий следующий элемент итерируемого объекта при каждом вызове, и бросающий исключение StopIteration, когда элементы закончились. Итератор получают с помощью функции iter().
Пример итерации:
numbers = [1, 2, 3, 4, 5]
for num in numbers:
print(num)
Выведи топ пять компаний у кого самая большая трата средств на работников
Что такое spill в Spark?
Spill в Spark — это термин для обозначения процесса перемещения данных из памяти на диск, а затем снова обратно в память.
Он происходит, когда данных слишком много, и они не помещаются в выделенный для задачи раздел памяти. Фреймворк вынужден выполнять дорогостоящие операции чтения и записи на диск, чтобы освободить локальную оперативную память и избежать ошибки нехватки памяти (OutOfMemory, OOM), которая может привести к сбою приложения.
Некоторые причины spill-эффекта в Spark:
Большое значение параметра spark.sql.files.maxPartitionBytes. По умолчанию это количество байтов для упаковки в один раздел при чтении файлов формата Parquet, JSON и ORC. Если установить раздел, считываемый Spark намного больше, например, 1 ГБ, активное поглощение может вызвать spill-эффект.
Операция explode() на небольшом массиве данных с соединениями и декартовыми соединениями (CrossJoin) двух таблиц, результат которого может превысить размер раздела.
Агрегации по искажённым данным, которые неравномерно распределены по узлам кластера, также потенциально могут создать очень большой раздел и вызвать перенос данных из памяти на диск и обратно.
Параллелизм в Apache Spark
Параллелизм в Apache Spark — это способность выполнять несколько задач одновременно на кластере. Spark использует модель параллелизма на уровне операций, что означает, что каждая операция в коде может быть выполнена параллельно на разных узлах кластера.
Уровень параллелизма определяет, сколько задач будет выполнено одновременно на кластере. Это может быть настроено через параметры конфигурации Spark. Важно подобрать подходящее значение в зависимости от характеристик кластера и характера обрабатываемых данных.
По умолчанию значение уровня параллелизма (spark.default.parallelism) равно общему количеству ядер на кластере.
Также в Spark есть параллелизм второго порядка, который позволяет выполнять несколько действий параллельно. Например, когда исполнители завершат первое действие, они немедленно начнут работу над вторым и не будут бездействовать, пока оба не будут завершены.
Расскажи порядок разрешения методов? python
Порядок разрешения методов (Method Resolution Order, MRO) в Python определяет последовательность, в которой Python ищет методы и атрибуты в иерархии классов. Это особенно важно при работе с множественным наследованием, когда класс может наследовать атрибуты и методы от нескольких родительских классов.
Алгоритм C3-линеаризации определяет MRO путём комбинирования:
Самого класса. Всегда начинаем с самого класса, в котором вызван метод
Списка родительских классов в порядке их перечисления. После текущего класса проверяем базовые классы в том порядке, в каком они указаны при наследовании.
MRO родительских классов в том же порядке. Если один и тот же базовый класс наследуется через несколько путей, он проверяется только один раз и в правильном порядке (все остальные разы он будет пропущен).
Порядок разрешения методов следует линейной последовательности. Это означает, что Python ищет метод от дочернего класса к родительскому, следуя порядку, указанному в определении класса.
Проверить порядок обхода методов и полей класса в Python можно, используя атрибут mro или функцию mro().
Как работает память в питоне?
Память в Python работает следующим образом:
При запуске программы операционная система создаёт новый процесс и выделяет под него ресурсы. В эту память загружается интерпретатор Python вместе со всеми необходимыми ему для работы данными, включая код программы.
Программа не сама выполняет сохранение и освобождение памяти. Интерпретатор лишь запрашивает это у диспетчера памяти.
Диспетчер памяти делегирует работу, связанную с хранением данных, аллокаторам — распределителям памяти. Непосредственно с оперативной памятью взаимодействует распределитель сырой памяти. Поверх него работают аллокаторы, реализующие стратегии управления памятью, специфичные для отдельных типов объектов.
Виртуальная память Python представляет иерархическую структуру:
Арена — фрагмент памяти, расположенный в пределах непрерывного блока оперативной памяти объёмом 256 Кб. Объекты размером более 256 Кб направляются в стандартный аллокатор C.
Пул — блок памяти внутри арены, занимающий 4 Кб, что соответствует одной странице виртуальной памяти.
Блок — элемент пула размером от 16 до 512 байт. В пределах пула все блоки имеют одинаковый размер.
Для освобождения памяти используются два механизма: счётчик ссылок и сборщик мусора. Счётчик ссылок увеличивается на единицу, когда создаётся что-то, что обращается к объекту, например, сохраняется объект в новой переменной. И наоборот, счётчик уменьшается на единицу, когда перестаётся ссылаться на объект. Если содержимое всех переменных — ссылок на объект изменится, счётчик обнулится. В этот момент Python освободит ячейку памяти, и по её адресу можно будет хранить новый объект.
Как работает сборщик мусора в python?
В большинстве случаев сборщик мусора работает автоматически, очищая неиспользуемые объекты без какого-либо вмешательства. Однако в некоторых случаях можно запустить сборку мусора вручную с помощью функции gc.collect()
Какие виды кэширования существуют в Spark и чем они отличаются?
В Spark существуют следующие виды кэширования и их отличия:
Cache(). Хранит данные в оперативной памяти каждого отдельного узла, если для этого есть место, в противном случае — на диске.
Persist(уровень). Может сохранять данные в памяти, на диске или из кэша в сериализованном или несериализованном формате в соответствии со стратегией кэширования, указанной уровнем.
Также существуют следующие уровни хранения для кэширования данных:
DISK_ONLY. Данные сохраняются на диске в сериализованном формате.
MEMORY_ONLY. Данные сохраняются в оперативной памяти в десериализованном формате.
MEMORY_AND_DISK. Данные сохраняются в оперативной памяти, и если памяти недостаточно, вытесненные блоки будут сохранены на диске.
OFF_HEAP. Данные сохраняются в памяти вне кучи.
Что такое persist в Spark и какие storage levels существуют?
Persistence в Spark позволяет контролировать уровень хранения данных (в памяти, на диске или комбинации). Это полезно для больших наборов данных, которые не помещаются целиком в память, или для обеспечения отказоустойчивости.
Существуют следующие уровни хранения в Spark:
MEMORY_ONLY. Хранит данные только в памяти. Это самый быстрый уровень хранения, но и самый изменчивый, так как данные можно удалить из памяти без пространства.
MEMORY_AND_DISK. Хранит данные в памяти, а если памяти недостаточно, сохраняет на диск.
DISK_ONLY. Хранит данные только на диске. Это самый надёжный уровень хранения, но и самый медленный.
MEMORY_ONLY_SER. Хранит данные в памяти в сериализованном виде. Экономит память, но увеличивает затраты на сериализацию/десериализацию.
MEMORY_AND_DISK_SER. Комбинация MEMORY_ONLY_SER и DISK_ONLY.
Чем отличается repartition от coalesce в Spark?
Основное отличие методов repartition и coalesce в Spark заключается в их назначении:
Repartition используется для увеличения или уменьшения количества партиций. Он выполняет shuffle данных, что может быть дорогостоящей операцией, но обеспечивает равномерное распределение данных между партициями.
Coalesce используется для уменьшения количества партиций. Он выполняет это без shuffle данных, что делает его менее затратным по сравнению с repartition. Однако coalesce эффективен только для уменьшения количества партиций.
Таким образом, repartition создаёт разделы одинакового размера, выполняя полное перемешивание данных, в то время как coalesce объединяет существующие разделы, чтобы избежать полного перемешивания.
Что делает YARN и зачем он нужен?
YARN в Apache Spark — это фреймворк управления ресурсами. Другими словами, это почти операционная система на кластерном уровне.
Что делает YARN: контролирует управление ресурсами, планирование и безопасность запущенных на нём Spark-приложений в любом режиме (кластера или клиента). При запуске Spark в YARN исполнитель Spark работает как контейнер YARN.
Зачем YARN нужен: позволяет использовать один пул ресурсов кластера для всех фреймворков, которые на нём запускаются, и настраивать его. Это даёт возможность разным типам приложений сосуществовать в одном кластере.
Кроме того, YARN централизует логи приложений, агрегируя их в HDFS или похожую систему хранения. Это упрощает отладку и позволяет проводить исторический анализ для обнаружения узких мест в производительности.
Сколько гигабайт памяти выделяется на каждую задачу в Spark?
По умолчанию каждому исполнителю (worker) в приложении Spark выделяется 1 ГБ оперативной памяти.
Какие механизмы выполнения операций соединения в спарке вы знаете?
5 механизмов выполнения операций соединения в Apache Spark SQL
Broadcast Hash Join. Один из входных наборов данных транслируется всем исполнителям. Для транслируемого набора данных создаётся хеш-таблица, после чего каждый раздел не транслируемого входного набора данных присоединяется независимо к другому набору данных, доступному как локальная хеш-таблица.
Shuffle Hash Join. Наборы данных выравниваются по выбранной схеме разделения. Если нужно, выполняется шаффлинг для соответствия схеме разделения. Затем выполняется join в каждой партиции с использованием hash join.
Sort Merge Join. Аналогично shuffle hash join, но требует сортировки данных перед join.
Cartesian Join. Используется только для cross join. Создаёт все возможные пары записей из обоих наборов данных.
Broadcast Nested Loop Join. Является механизмом join по умолчанию, когда нельзя выбрать другие механизмы.
Что такое broadcast join в Spark и как его настроить?
Broadcast join в Spark — это метод, который оптимизирует соединение больших и малых датафреймов. Он позволяет избежать перетасовки данных и сократить накладные расходы на передачу по сети.
Настроить broadcast join в Spark можно с помощью подсказок (хинтов). Они позволяют пользователю предложить Spark применять нужные подходы для создания плана выполнения SQL-запроса.
Чтобы указать, что надо сделать broadcast какой-то небольшой таблицы или датасета (до ~1Gb), можно в SQL-запросе указывать хинт /+ BROADCAST(t)/, где t — алиас таблицы или датасета.
Важно использовать broadcast join только для небольших фреймов данных, так как трансляция больших фреймов может вызвать проблемы с памятью из-за репликации данных на всех рабочих узлах.
Что такое Adaptive query execution?
Adaptive Query Execution (AQE) — это механизм оптимизации запросов в Apache Spark в реальном времени. Он динамически оптимизирует планы выполнения запросов на основе статистики и характеристик данных во время выполнения.
AQE позволяет более эффективно использовать ресурсы и снижать время выполнения. Например, он может разделить большие задачи на более мелкие, что уменьшает узкие места.
AQE также позволяет отложить решение о стратегии объединения до времени выполнения, выбрав наиболее подходящий метод на основе фактических размеров данных.
AQE используется в трёх сценариях, где традиционная оптимизация может не работать:
Перекос данных. Spark может определить перекос данных во время выполнения и скорректировать выполнение задач.
Неизвестные шаблоны объединений. В запросе с неизвестными шаблонами объединений AQE может отложить решение о стратегии объединения до времени выполнения, выбрав наиболее подходящий метод.
Изменение характеристик данных. Размер и распределение наборов данных могут меняться, делая статически определённые планы запросов устаревшими.
Есть например большая таблица в 30 строк, есть PK и куча колонок(30,40 или 50 или n).
Есть алгоритм, называется поиск островов.
Есть ПО которое формирует csv файлы, его загружал в Clickhouse. Программа закрывалась несколько раз, а она не должна. Каждый день в 9 часов, выкладывался отчет со строками, сколько минут простаивала прога. Принцип алгоритма в том, что у вас есть последовательно идущие числа. Островом являются числа 1-3, 5-7, 10-13. Нужно найти минимальное и максимальное значение острова row_number применяется
Zuletzt geändertvor 2 Tagen