Большие соединительные таблицы и масштабирование - PullRequest
1 голос
/ 25 августа 2011

Проблема

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

Фон

Я имею дело с базой данных, которая хранит геномную информацию. Ряд маркеров (~ 3 миллиона), соответствующих локусам, в которых имеются вариации ДНК, связаны с индивидуумами, чей генотип определен в этих локусах. Каждый маркер имеет ряд возможных генотипов, из которых каждый должен иметь один.

Текущая реализация

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

Ниже приведена уменьшенная версия этих таблиц:

                                        Table "public.genotypes"
      Column      |            Type             |                       Modifiers                        
------------------+-----------------------------+--------------------------------------------------------
 id               | integer                     | not null default nextval('genotypes_id_seq'::regclass)
 ref_variation_id | integer                     | 
 value            | character varying(255)      |  
Indexes:
    "genotypes_pkey" PRIMARY KEY, btree (id)
    "index_genotypes_on_ref_variation_id" btree (ref_variation_id)


Table "public.genotypes_individuals"
    Column     |  Type   | Modifiers 
---------------+---------+-----------
 genotype_id   | integer | 
 individual_id | integer | 
Indexes:
    "index_genotypes_individuals_on_genotype_id_and_individual_id" UNIQUE, btree (genotype_id, individual_id)
    "index_genotypes_individuals_on_genotype_id" btree (genotype_id)

                                       Table "public.individuals"
    Column     |            Type             |                        Modifiers                         
---------------+-----------------------------+----------------------------------------------------------
 id            | integer                     | not null default nextval('individuals_id_seq'::regclass)
 hap_id        | character varying(255)      | 
 population_id | integer                     | 
 sex           | character varying(255)      | 
Indexes:
    "individuals_pkey" PRIMARY KEY, btree (id)
    "index_individuals_on_hap_id" UNIQUE, btree (hap_id)

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

  • Простой поиск всех генотипов человека

    SELECT * FROM "genotypes" INNER JOIN "genotypes_individuals" ON "genotypes" .id = "genotypes_individuals" .genotype_id WHERE ("genotypes_individuals" .individual_id = 2946)

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

    SELECT * FROM "genotypes" INNER JOIN "genotypes_individuals" ON "genotypes" .id = "genotypes_individuals" .genotype_id WHERE ("genotypes_individuals" .individual_id = 2946) AND ("genotypes" .ref_variation_id 37, IN3, 37 (IN 37) ..))

  • Нам также иногда нужно идти другим путем.

    ВЫБРАТЬ * ИЗ "индивидуумов" ВНУТРЕННЕЕ СОЕДИНЕНИЕ "genotypes_individuals" ВКЛ "индивидуумов" .id = "genotypes_individuals" .individual_id ГДЕ ("genotypes_individuals" .genotype_id = 53430)

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

Я понимаю, что базы данных предназначены для эффективной обработки больших таблиц, но мы уже сталкиваемся с узкими местами из-за дискового ввода-вывода. Отдельный запрос все еще несущественен, но тысячи из них быстро складываются. Мы можем несколько облегчить эту проблему, распределив БД по нескольким дискам. Тем не менее, я хотел посмотреть, есть ли другие альтернативы там. Мне было интересно, можно ли каким-то образом отделить записи в таблице соединений по индивидуальному_идиду, что, возможно, не повлияло бы на поиск от отдельных лиц к генотипам, добавив дополнительные строки индивидуальных генотипов в таблицу соединений. Или индексы уже делают это?

Ответы [ 2 ]

0 голосов
/ 30 августа 2011

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

Ваш поиск всех генотипов человека

SELECT * 
FROM "genotypes" 
INNER JOIN "genotypes_individuals" 
        ON "genotypes".id = "genotypes_individuals".genotype_id 
WHERE ("genotypes_individuals".individual_id = 2946 )

становится

SELECT * 
FROM genotypes_individuals
WHERE (individual_id = 2946)

Иногда это происходит быстрее.Иногда это не так.

Переключение на естественные ключи в наша производственная система увеличила среднюю производительность в 10 раз. Некоторые запросы выполнялись в 100 раз быстрее с естественными ключами, потому что естественные ключи исключали множество соединений.Некоторые запросы также выполнялись медленнее.Но среднее ускорение было впечатляющим в любом случае.

0 голосов
/ 25 августа 2011

Вы смотрели на разбиение таблицы ?

...