В общем есть в нём одна незадокументированная фишка
(процесс дедубликации не моментальный, но не 2 года же)
Именно это и задокументировано прямым текстом: "в неизвестный момент времени, на который вы не можете ориентироваться". Не надо додумывать и считать, что документация вам врёт, потому что вам кажется, что в этой фразе почему-то подразумевается "но не 2 года же".
(справедливости ради, документация иногда действительно врёт, но не в данном случае)
А вам стоит разобраться, почему у вас не происходит дедубликация (если вам это, конечно, нужно):
действительно ли данные на одном шарде
не лежат ли данные в разных партициях (так тоже не будет дедубликации)
настройки типа insert_deduplicate=0 + optimize_skip_merged_partitions=1 могут привести к неудалению дублей даже в одной партиции
насколько большие парты с дублями (есть лимиты на слияние слишком больших)
как именно вызываете optimize (с final или нет) и каких таблиц и партиций, на всех ли шардах...
не падает ли мердж
Может еще что-то забыл и проблема в чем-то еще. Но у нас почти всё на ReplicatedReplacingMergeTree, так что на 99% уверен, что проблема не в КХ (даже не смотря на не самую удачную версию у вас - 1% на это оставляю).
PS. В современных версиях есть настройки, задающие максимальное время до мерджа, что потенциально может избавить от optimize по cron/airflow, но мы до этого еще не обновились, так что не могу сказать, насколько оно норм и какие там грабли.
Примерный uniq дает такой же результат, что и точный uniqExact, но занимает меньше памяти. Но тут данных мало, если найдется файлик пожирнее, разница будет больше.
Не так уж и много памяти сейчас. Вот есть у нас несколько юзеров БД, каждый запустил запрос на количество уникальных по нескольким колонкам. А среди них еще и строковые могут быть. И на практике это реально кучу памяти отъедает. Плюс это еще и медленно. Поэтому приблизительные алгоритмы рулят, особенно которые дают точный результат на маленьком числе групп, а неточность возникает только после тысяч.
Есть разница между тем, что запрос отожрал всю память и если повезет, то не упал и выполнился за время пока ты кофе пошел сделать, и тем, что запрос ничего не отожрал и выполнился за пару секунд (но может быть выдал 123456700 вместо 123456789, какой ужас).
В первом же абзаце статьи: "Главный критерий - нахождение адреса, если написано с ошибками или не дописан он в полной мере." Чуть опечатался и всё, разбиение на слова и точная проверка ломаются сразу. Ну и выше вам показывали про дом с квартирой. Тоже самое с улицей и населенным пунктом может быть.
ИВАНОВ ИВАН ИВАНОВИЧ и ИВАН ИВАНОВ считаются совпадением т.к все уникальные элементы более коротого набора
Мне кажется, что ИВАНОВ ИВАН ИВАНОВИЧ и ИВАНОВ ИВАН ИВАНОВИЧ всё-таки совпадение получше, то есть логично меру для ИВАН ИВАНОВ и ИВАНОВ ИВАН ИВАНОВИЧ видеть хуже.
Возможно, вы не совсем поняли про "последовательности байт" (собственно я и не пояснял особо). Там ищется не максимально длинное совпадение. Там строки разбиваются на все возможные последовательности N байт (для фиксированного N), например: 'ИВАН', 'ВАН ', 'АН И', 'Н ИВ' и т.д. И вот число совпадающих этих наборов сравнивается. Это достаточно хорошо работает с перестановками слов, опечатками и.т.п. (поэтому я и предложил метод автору). Но пункт про нормализацию строк в той или иной степени может быть полезен в каких-то случаях и здесь.
P.S. Я ни разу не специалист в этой теме, не знаю, как лучше будет работать у автора статьи и как лучше будет работать у вас. Я лишь запостил еще один вариант. Кажется, что у каждого метода есть недостатки, выстреливающие в зависимости от ситуации. Собственно не было бы нескольких разных методов если бы это было не так.
В clickhouse для этого есть всякие готовые ngramDistance*(). Неплохо работает для приведенных примеров (даже явно получше, чем в статье для Нарт). Оно смотрит процент совпадающих последовательностей байт в строках - можно это реализовать на удобном вам языке.
Hidden text
with
['Эски сары', 'Нарты']
as queries,
['Эски сары кёл', 'Хасаутская', 'Нартов', 'Новый Карачай', 'Мара-Аягъы', 'Кавказская']
as target_vector
select
arrayJoin(queries) as query,
arrayJoin(target_vector) as target,
ngramDistance(query, target) as similarity1,
ngramDistanceCaseInsensitiveUTF8(query, target) as similarity2
order by query, similarity2, similarity1
format PrettyCompactMonoBlock
;
+-query-----+-target--------+-similarity1-+-similarity2-+
1. | Нарты | Нартов | 0.375 | 0.42857143 |
2. | Нарты | Эски сары кёл | 0.85714287 | 1 |
3. | Нарты | Мара-Аягъы | 0.9130435 | 1 |
4. | Нарты | Новый Карачай | 0.9310345 | 1 |
5. | Нарты | Хасаутская | 1 | 1 |
6. | Нарты | Кавказская | 1 | 1 |
7. | Эски сары | Эски сары кёл | 0.2 | 0.22222222 |
8. | Эски сары | Хасаутская | 0.7419355 | 1 |
9. | Эски сары | Нартов | 0.82608694 | 1 |
10. | Эски сары | Кавказская | 0.87096775 | 1 |
11. | Эски сары | Мара-Аягъы | 0.93333334 | 1 |
12. | Эски сары | Новый Карачай | 0.9444444 | 1 |
+-----------+---------------+-------------+-------------+
А вообще, используйте WebP. Он лучше JPG и его уже везде принимают, кроме пары технически отсталых сайтов.
Из статьи:
Канал.jpg (Хабр не поддерживает формат WebP)
что-то мне подсказывает, что "парочка отсталых сайтов" в реальном мире - это 90+%. И по итогу будет двойное пережатие исходник -> webp -> jpeg, что врядли хорошо отразится на хорошем качестве.
Пишу это только из-за "В марте 2024 года мы снова проведём наше исследование", чтобы следующая статья была оформлена лучше.
Не надо указывать значения со 100500 значащими цифрами, если погрешность измерений не позволяет столько разрешить в действительности. То есть тут центы указывать - это только захламлять диаграммы, ухудшая их читаемость, при этом не добавляя никакой полезной информации. Самое простое до целых долларов округлить. Я бы вообще до десятков долларов округлил. Значения никак не пострадают, читабельность значительно улучшится.
snowflake id или sonyflake id почти всегда лучше, ибо 8 байт и монотонность. Возможно, хуже там где "Отсутствие предсказуемости" - это серьезный фактор. Ну или у вас ну ооочень много юников.
Агрегация данных, вставляемых разными пачками решается через комбинатор State или SimpleState В вашем примере в матвью вместо count(*) можно сделать sumSimpleState(1), и селектить потом через sumMerge.
"Данные были вставлены в одном запросе и это имеет значение." Это имеет значение, если выставлена настройка insert_deduplicate = 1. Для более эффективной вставки ее выставляют в 0, тогда и в одной вставке данные не будут схлопываться сразу.
И у вас в примерах во всех таблицах в ключе сортировки стоит поменять порядок: ORDER BY (app, time). Для полного счастья что-то вроде такого: PARTITION BY toYYYYMM(date) ORDER BY (date, app, time) (при желании можно не делать отдельную колонку с датой, но так удобнее в запросах, а сожмется она в ноль) После этого может оказаться, что и без матвью скорости хватает.
Подмена понятий туда-сюда по ходу статьи. Сначала с пафосом заявляем: «ответ найден!», потом вводим сомнительные критерии, плюс определяем некоторые термины в математической модели задачи, в конце заменяем смысл введенных в математической модели терминов на бытовой аналог этих терминов и утверждаем, что задача решена.
Подробнее:
1. Пункт 4 про термин «Эффективный спуск» — не учитываем, из какого вагона нам надо будет выйти при следующей пересадке. Это как бы очень очень важно, но просто упоминается в «PS», якобы, не имеет особого значения. Но если мне надо в другой конец поезда, то пока я иду по платформе с одного конца до другого, придет аж следующий поезд. Таким образом, если я раньше оказываюсь на платформе, то или сажусь в предыдущий поезд и перехожу по вагонам на следующих остановках, или сразу оказываюсь в нужном вагоне, или в каком-то из промежуточных состояний, но в любом случае в лучшей ситуации, чем если бы не ускорился на эскалаторе. Кроме случаев, когда место пересадки совпадает с местом, куда приходит наш эскалатор.
2. В пункте 5 называем «целесообразным» спуск с вероятностью не менее 0.5. ОК, не вопрос, ввели понятие. Но внезапно в конце «целесообразный» становится целесообразным в житейском смысле. Что, это как вообще? То есть если я в 40% случаев попадаю в предыдущий поезд и приезжаю на 2-3 минуты раньше — это нецелесообразно?
Если честно, остальное между матмоделью и выводами промотал, но учитывая странную матмодель и подмену понятий в выводах, нет особого смысла рассматривать само решение, даже если оно идеально.
Не баг, но еще по поводу скачивания файлов. Есть в планах вернуть фичу из 12-й, когда она начинает скачивать файл еще до того, как указываешь место, куда сохранять? Очень удобная фича. Так невольно тыкаешь куда поближе, лишь бы быстрее файл начал скачиваться, а в 12-й спокойненько выбираешь конечное местоположение файла
В тему экспресс-панели. В 12-й всегда добавляю сайты следующим образом:
открываю нужный сайт, тыкаю новую вкладку (открывается экспресс-панель), тыкаем плюсик, видим наш сайт первым в списке, кликаем на него, всё. В вивальди же в списке сайтов нет открытых сейчас сайтов (только старые какие-то), приходится открывать его вкладку, копипастить урл и потом копипастить его в поле экспресс-панели. Неудобненько.
И да, спасибо за браузер.
Именно это и задокументировано прямым текстом: "в неизвестный момент времени, на который вы не можете ориентироваться". Не надо додумывать и считать, что документация вам врёт, потому что вам кажется, что в этой фразе почему-то подразумевается "но не 2 года же".
(справедливости ради, документация иногда действительно врёт, но не в данном случае)
А вам стоит разобраться, почему у вас не происходит дедубликация (если вам это, конечно, нужно):
действительно ли данные на одном шарде
не лежат ли данные в разных партициях (так тоже не будет дедубликации)
настройки типа insert_deduplicate=0 + optimize_skip_merged_partitions=1 могут привести к неудалению дублей даже в одной партиции
насколько большие парты с дублями (есть лимиты на слияние слишком больших)
как именно вызываете optimize (с final или нет) и каких таблиц и партиций, на всех ли шардах...
не падает ли мердж
Может еще что-то забыл и проблема в чем-то еще. Но у нас почти всё на ReplicatedReplacingMergeTree, так что на 99% уверен, что проблема не в КХ (даже не смотря на не самую удачную версию у вас - 1% на это оставляю).
PS. В современных версиях есть настройки, задающие максимальное время до мерджа, что потенциально может избавить от optimize по cron/airflow, но мы до этого еще не обновились, так что не могу сказать, насколько оно норм и какие там грабли.
Можете сами придумать регулярку, которая слова вычленяет на ваш взгляд правильно, и заменить тут простую \w+ на нужную: https://fiddle.clickhouse.com/1a424911-2450-4668-82bc-c27a24b4161b
со \w+ и lowercase получается:
Примерный uniq дает такой же результат, что и точный uniqExact, но занимает меньше памяти. Но тут данных мало, если найдется файлик пожирнее, разница будет больше.
Не так уж и много памяти сейчас. Вот есть у нас несколько юзеров БД, каждый запустил запрос на количество уникальных по нескольким колонкам. А среди них еще и строковые могут быть. И на практике это реально кучу памяти отъедает. Плюс это еще и медленно. Поэтому приблизительные алгоритмы рулят, особенно которые дают точный результат на маленьком числе групп, а неточность возникает только после тысяч.
Есть разница между тем, что запрос отожрал всю память и если повезет, то не упал и выполнился за время пока ты кофе пошел сделать, и тем, что запрос ничего не отожрал и выполнился за пару секунд (но может быть выдал 123456700 вместо 123456789, какой ужас).
В первом же абзаце статьи: "Главный критерий - нахождение адреса, если написано с ошибками или не дописан он в полной мере." Чуть опечатался и всё, разбиение на слова и точная проверка ломаются сразу. Ну и выше вам показывали про дом с квартирой. Тоже самое с улицей и населенным пунктом может быть.
Мне кажется, что ИВАНОВ ИВАН ИВАНОВИЧ и ИВАНОВ ИВАН ИВАНОВИЧ всё-таки совпадение получше, то есть логично меру для ИВАН ИВАНОВ и ИВАНОВ ИВАН ИВАНОВИЧ видеть хуже.
Возможно, вы не совсем поняли про "последовательности байт" (собственно я и не пояснял особо). Там ищется не максимально длинное совпадение. Там строки разбиваются на все возможные последовательности N байт (для фиксированного N), например: 'ИВАН', 'ВАН ', 'АН И', 'Н ИВ' и т.д. И вот число совпадающих этих наборов сравнивается. Это достаточно хорошо работает с перестановками слов, опечатками и.т.п. (поэтому я и предложил метод автору). Но пункт про нормализацию строк в той или иной степени может быть полезен в каких-то случаях и здесь.
P.S. Я ни разу не специалист в этой теме, не знаю, как лучше будет работать у автора статьи и как лучше будет работать у вас. Я лишь запостил еще один вариант. Кажется, что у каждого метода есть недостатки, выстреливающие в зависимости от ситуации. Собственно не было бы нескольких разных методов если бы это было не так.
В clickhouse для этого есть всякие готовые ngramDistance*(). Неплохо работает для приведенных примеров (даже явно получше, чем в статье для
Нарт
). Оно смотрит процент совпадающих последовательностей байт в строках - можно это реализовать на удобном вам языке.Hidden text
https://fiddle.clickhouse.com/466ee362-dbff-4161-9553-ae6abcd565ec
Из статьи:
что-то мне подсказывает, что "парочка отсталых сайтов" в реальном мире - это 90+%. И по итогу будет двойное пережатие исходник -> webp -> jpeg, что врядли хорошо отразится на хорошем качестве.
Пишу это только из-за "В марте 2024 года мы снова проведём наше исследование", чтобы следующая статья была оформлена лучше.
Не надо указывать значения со 100500 значащими цифрами, если погрешность измерений не позволяет столько разрешить в действительности. То есть тут центы указывать - это только захламлять диаграммы, ухудшая их читаемость, при этом не добавляя никакой полезной информации. Самое простое до целых долларов округлить. Я бы вообще до десятков долларов округлил. Значения никак не пострадают, читабельность значительно улучшится.
"спокойно" - если у вас в 4 раза больше денег на сервера
snowflake id или sonyflake id почти всегда лучше, ибо 8 байт и монотонность. Возможно, хуже там где "Отсутствие предсказуемости" - это серьезный фактор. Ну или у вас ну ооочень много юников.
А что если убрать из рассмотрения исходно простые числа?
Странно, что в тест не включили просто сжатие ZSTD без кодеков.
От себя могу добавить:
в первую очередь подбираем ORDER BY таблиц, потом уже тестируем кодеки, если нужно
тестируйте на своих реальных данных для каждой конкретной таблицы
для целых чисел (включая Date, DateTime, Decimal и Enum) часто хорош вариант (T64, LZ4)
для строк не ошибкой будет всегда выбирать между двумя вариантами: ZSTD или LowCardinality
LowCardinality может быть выгоден и гораздо больше чем для 10000 уникальных значений, особенно если строки длинные
для коротких строк можно по умолчанию оставлять LZ4
LZ4HC очень медленный на вставку
Агрегация данных, вставляемых разными пачками решается через комбинатор
State
илиSimpleState
В вашем примере в матвью вместо
count(*)
можно сделатьsumSimpleState(1)
, и селектить потом черезsumMerge
."Данные были вставлены в одном запросе и это имеет значение."
Это имеет значение, если выставлена настройка
insert_deduplicate = 1
. Для более эффективной вставки ее выставляют в 0, тогда и в одной вставке данные не будут схлопываться сразу.И у вас в примерах во всех таблицах в ключе сортировки стоит поменять порядок:
ORDER BY (app, time)
. Для полного счастья что-то вроде такого:PARTITION BY toYYYYMM(date)
ORDER BY (date, app, time)
(при желании можно не делать отдельную колонку с датой, но так удобнее в запросах, а сожмется она в ноль)
После этого может оказаться, что и без матвью скорости хватает.
Подробнее:
1. Пункт 4 про термин «Эффективный спуск» — не учитываем, из какого вагона нам надо будет выйти при следующей пересадке. Это как бы очень очень важно, но просто упоминается в «PS», якобы, не имеет особого значения. Но если мне надо в другой конец поезда, то пока я иду по платформе с одного конца до другого, придет аж следующий поезд. Таким образом, если я раньше оказываюсь на платформе, то или сажусь в предыдущий поезд и перехожу по вагонам на следующих остановках, или сразу оказываюсь в нужном вагоне, или в каком-то из промежуточных состояний, но в любом случае в лучшей ситуации, чем если бы не ускорился на эскалаторе. Кроме случаев, когда место пересадки совпадает с местом, куда приходит наш эскалатор.
2. В пункте 5 называем «целесообразным» спуск с вероятностью не менее 0.5. ОК, не вопрос, ввели понятие. Но внезапно в конце «целесообразный» становится целесообразным в житейском смысле. Что, это как вообще? То есть если я в 40% случаев попадаю в предыдущий поезд и приезжаю на 2-3 минуты раньше — это нецелесообразно?
Если честно, остальное между матмоделью и выводами промотал, но учитывая странную матмодель и подмену понятий в выводах, нет особого смысла рассматривать само решение, даже если оно идеально.
открываю нужный сайт, тыкаю новую вкладку (открывается экспресс-панель), тыкаем плюсик, видим наш сайт первым в списке, кликаем на него, всё. В вивальди же в списке сайтов нет открытых сейчас сайтов (только старые какие-то), приходится открывать его вкладку, копипастить урл и потом копипастить его в поле экспресс-панели. Неудобненько.
И да, спасибо за браузер.