Как получить граф для больших столов? - PullRequest
4 голосов
/ 05 апреля 2019

Пример таблицы:

+----+-------+-------+-------+-------+-------+---------------+
| id | col1  | col2  | col3  | col4  | col5  | modifiedTime  |
+----+-------+-------+-------+-------+-------+---------------+
|  1 | temp1 | temp2 | temp3 | temp4 | temp5 | 1554459626708 |
+----+-------+-------+-------+-------+-------+---------------+ 

В приведенной выше таблице содержится 50 миллионов записей

  1. (col1, col2, col3, col4, col5 - это столбцы VARCHAR)
  2. (id - PK)
  3. (ifiedTime)

Каждый столбец индексируется

Например: у меня есть две вкладкина моем веб-сайте.

FirstTab - я печатаю счет выше таблицы со следующими критериями [col1 как «value1%» и col2 как «value2%»]

SeocndTab - я печатаючисло в приведенной выше таблице со следующими критериями [col3, например, «value3%»]


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

Примечание: я бы когда-нибудь изменил данные записей (строки в таблице).Вставьте новые строки.Удалите ненужные записи.

Мне нужно выполнимое решение вместо запроса всей таблицы.Пример: как кэширование старшего графа.Возможно ли что-нибудь подобное?

Ответы [ 10 ]

4 голосов
/ 09 апреля 2019

Хотя я уверен, что это возможно для MySQL, вот решение для Postgres, использующее триггеры.

Счетчик хранится в другой таблице, и при каждой вставке / обновлении / удалении имеется триггер, который проверяет, соответствует ли новая строка условию (ам), и, если это так, добавляет 1 к счетчику.Другая часть триггера проверяет, соответствует ли старая строка условию (ам), и, если это так, вычитает 1.

Вот базовый код для триггера, который считает строки с temp2 = '5':

CREATE OR REPLACE FUNCTION updateCount() RETURNS TRIGGER AS 
$func$
BEGIN
   IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN
      EXECUTE 'UPDATE someTableCount SET cnt = cnt + 1 WHERE 1 = (SELECT 1 FROM (VALUES($1.*)) x(id, temp1, temp2, temp3) WHERE x.temp2 = ''5'')'
      USING NEW;
   END IF;
   IF TG_OP = 'DELETE' OR TG_OP = 'UPDATE' THEN
      EXECUTE 'UPDATE someTableCount SET cnt = cnt - 1 WHERE 1 = (SELECT 1 FROM (VALUES($1.*)) x(id, temp1, temp2, temp3) WHERE x.temp2 = ''5'')'
      USING OLD;
   END IF;
   RETURN new;
END
$func$ LANGUAGE plpgsql;

Вот рабочий пример для dbfiddle .

Конечно, вы можете изменить код триггера, чтобы он был динамическим, где выражения и счетчики хранятся для каждого в таблице, например:

CREATE TABLE someTableCount
(
   whereExpr text,
   cnt INT
);

INSERT INTO someTableCount VALUES ('temp2 = ''5''', 0);

В триггере вы затем выполните цикл по условиям и обновите соответственно.

2 голосов
/ 09 апреля 2019

FirstTab - я печатаю счет выше таблицы со следующими критериями [col1 как «value1%» и col2 как «value2%»]

Это было бы полезно от «составного»index:

INDEX(col1, col2)

, потому что это будет "покрытие".(То есть все столбцы, необходимые в запросе, находятся в одном индексе.)

SeocndTab - я распечатываю счетчик приведенной выше таблицы по следующим критериям [col3 наподобие «value3%»]

По-видимому, у вас уже есть оптимальный (охватывающий) индекс:

INDEX(col3)

Теперь давайте рассмотрим его с другой точки зрения.Вы заметили, что поисковые системы больше не дают точного количества совпадающих строк?Вы выясняете, почему - подсчет занимает слишком много времени, независимо от того, какой метод используется .

Так как «col1» не дает мне ни подсказки ни о вашем приложении, ни о какой-либо идеечто подсчитывается, я могу выбросить только некоторые общие рекомендации:

  • Не указывать счет.
  • Предварительно вычислить счет, сохранить его где-нибудь и выдать «устаревшие» значения.Это может быть удобно, если учитывается только несколько разных «значений».Это, вероятно, непрактично для произвольных строк.
  • Скажите "about nnnn" в выводе.
  • Воспроизведите несколько приемов, чтобы решить, целесообразно ли вычислять точное значение или просто сказать "about".
  • Скажите «больше 1000».
  • и т. Д.

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

Вы выразили беспокойство по поводу "скорости вставки".Это , обычно , не проблема, и преимущество правильного индекса для SELECTs перевешивает незначительный удар по производительности для INSERTs.

.
1 голос
/ 12 апреля 2019

Как общее практическое правило, когда речь заходит об оптимизации (и да, для одного узла SQL-сервера на 50 миллионов записей на таблицу требуется один!), Вот список нескольких возможных методов оптимизации, некоторые из которых довольно просты в реализации, другие могут быть нужны более серьезные модификации:

  • оптимизируйте тип и размеры полей MYSQL , например. используйте INT вместо VARCHAR, если данные могут быть представлены числами, используйте SMALL INT вместо BIG INT и т. д. Если вам действительно нужно иметь VARCHAR, тогда используйте как можно меньшую длину каждого поля,

  • посмотрите на ваш набор данных; есть ли какие-либо повторяющиеся значения ? Допустим, если какое-либо из ваших полей содержит только 5 уникальных значений в строках по 50 млн., Затем сохраните эти значения в отдельной таблице и просто свяжите PK с этой таблицей образцов,

  • MYSQL-разделение , базовое понимание показано на этой ссылке , поэтому общая идея заключается в том, чтобы реализовать какую-то схему разделения, например, CRONJOB каждый день создает новый раздел ночью, когда загрузка сервера минимальна, или когда вы достигаете еще 50 тыс. INSERT или около того (кстати, для операций UPDATE / DELETE на разных разделах потребуются дополнительные усилия),

  • кеширование - это еще один очень простой и эффективный подход, поскольку запрос (почти) одних и тех же данных (я предполагаю, что значения value1%, value2%, value3% всегда одинаковы?) Снова и снова снова. Поэтому выполняйте SELECT COUNT () один раз, а затем используйте счет разностного индекса, чтобы получить фактическое количество выбранных строк,

  • база данных в памяти может использоваться вместе с традиционными базами данных SQL для получения часто необходимых данных: может быть достаточно простого стиля пары ключ-значение: Redis, Memcached, VoltDB, MemSQL - лишь некоторые из них. Кроме того, MYSQL также знает движок в памяти ,

  • использовать другие типы БД , например, NoSQL DB, например MongoDB, если ваш набор данных / система может использовать другую концепцию.

1 голос
/ 11 апреля 2019

Звучит так, будто вы пытаетесь использовать молоток, когда нужна отвертка. Если вы не хотите запускать пакетные вычисления, я бы предложил использовать потоковую среду, такую ​​как Flink или Samza, для добавления и вычитания из вашего количества при добавлении или удалении записей. Именно для этого и создаются эти фреймворки.

Если вы привержены использованию SQL, вы можете настроить задание, которое будет выполнять нужные операции подсчета в каждый заданный интервал времени и сохранять значения во второй таблице. Таким образом, вам не нужно выполнять повторные подсчеты в одних и тех же строках.

0 голосов
/ 11 апреля 2019

Каждая база данных имеет свои особенности в том, как «улучшать» свои СУБД.Я не могу говорить о MySQL или SQL Server, но для PostgreSQL вы должны рассмотреть возможность создания индексов, которые вы ищете, как индексы на основе GIN (Обобщенный инвертированный индекс).

CREATE INDEX name ON table USING gin(col1);
CREATE INDEX name ON table USING gin(col2);
CREATE INDEX name ON table USING gin(col3);

Более подробную информацию можно найти здесь .

-HTH

0 голосов
/ 10 апреля 2019

Если в вашей системе не происходит массовых обновлений вставки / массовых обновлений, можете ли вы попробовать вертикальное разбиение таблицы?Вертикальным разделением вы можете отделить блок данных col1, col2 от других данных таблицы, и, таким образом, ваше пространство поиска уменьшится.

Кроме того, индексация по каждому столбцу не кажется лучшим подходом кидти с.Указатель везде, где это абсолютно необходимо.В этом случае я бы сказал Index (col1, col2) и Index (col3).

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

0 голосов
/ 09 апреля 2019

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

  1. Сколько времени вы тратите на подмножество проблемы, т. Е. Количество [col1 как «значение1%» и col2 как «значение2%»] и 2-е [col3 как «значение3%]
  2. Хитрость заключается в том, чтобы один раз отсканировать источник данных и уменьшить его, создав индекс. Итак, сначала создайте индекс для col1, col2, col3, id. Назначение col3 и id состоит в том, чтобы база данных сканировала только индекс. И я бы получил оба счета в одном и том же SQL
select sum
       (
           case 
               when col1 like 'value1%' and col2 like 'value2%' then 1
               else 0
           end
       ) cnt_condition_1,
       sum
       (
           case 
               when col3 like 'value3%' then 1
               else 0
           end
       ) cnt_condition_2
from table
where (col1 like 'value1%' and col2 like 'value2%') or
      (col3 like 'value3%')
```
So the 50M row table is probably very wide right now.  This should trim it down - on a reasonable server I would expect above to return in a few seconds.  If it does not and each condition returns < 10% of the table, second option will be to create multiple indexes for each scenario and do count for each so that index is used in each case.
0 голосов
/ 08 апреля 2019

это будет работать:

select count(*) from (
select * from tablename where col1 like 'value1%' and col2 like 'value2%' and col3 
like'value3%')
where REGEXP_LIKE(col1,'^value1(.*)$') and REGEXP_LIKE(col2,'^value2(.*)$') and 
REGEXP_LIKE(col1,'^value2(.*)$');

старайтесь не применять индекс ко всем столбцам, так как это замедляет обработку запроса SQL и делает его только в обязательных столбцах.

0 голосов
/ 08 апреля 2019

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

СУБД Column хранит данные в виде столбцов, что означает, что каждый столбец индексируется независимо от других. Это позволяет значительно быстрее агрегировать, я переключился с Postgres на MonetDB (СУБД с открытым исходным кодом) и суммирование одного поля из таблицы строк в 6 миллионов, упавшей с ~ 60 с до 50 мс. Я выбрал MonetDB, так как он поддерживает запросы SQL и соединения odbc, что было плюсом для моего варианта использования, но вы столкнетесь с аналогичными улучшениями производительности с другими СУБД Column.

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

0 голосов
/ 08 апреля 2019

В Postgres вы можете получить приблизительное количество строк из внутренней статистики, которой управляет планировщик запросов:

SELECT reltuples AS approximate_row_count FROM pg_class WHERE relname = 'mytable';

Здесь у вас есть более подробная информация: https://wiki.postgresql.org/wiki/Count_estimate

Сначала вы можете создать материализованное представление. Примерно так:

CREATE MATERIALIZED VIEW mytable AS SELECT * FROM the_table WHERE col1 like "value1%" and col2 like "value2%";`

Вы также можете материализовать запросы напрямую. Если у вас 10 вкладок, то вам нужно материализовать 10 представлений:

CREATE MATERIALIZED VIEW count_tab1 AS SELECT count(*) FROM the_table WHERE col1 like "value1%" and col2 like "value2%";`
CREATE MATERIALIZED VIEW count_tab2 AS SELECT count(*) FROM the_table WHERE col2 like "value2%" and col3 like "value3%";`
...

После каждой вставки вы должны обновлять представления (асинхронно):

REFRESH MATERIALIZED VIEW count_tab1
REFRESH MATERIALIZED VIEW count_tab2
...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...