Связь «многие ко многим» с одной большой таблицей

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

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

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

CREATE TABLE map_cell (
map_id INT  NOT NULL ,
cell_index INT  NOT NULL ,

...    
PRIMARY KEY (map_id, cell_index)
)

5000 строк на карту — это немало, но запросы должны оставаться эффективными для миллионов строк, поскольку основные индексы соединения могут быть кластеризованы. Если он становится слишком громоздким, его можно разделить по границам map_id. Несмотря на большое количество строк на карту, эта таблица вполне масштабируема.

Проблема возникает с хранением данных, описывающих, какие ячейки соседствуют друг с другом. Отношение соседней ячейки — это отношение «многие ко многим» к одной и той же таблице. Есть также очень большое количество таких отношений на карту. Нормализованная таблица, вероятно, будет выглядеть примерно так:

CREATE TABLE map_cell_neighbors (
id INT  NOT NULL AUTO INCREMENT ,
map_id INT  NOT NULL ,
cell_index INT  NOT NULL ,
neighbor_index INT ,
...
INDEX IX_neighbors (map_id, cell_index)
)

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

CREATE TABLE map_cell_neighbors (
id INT  NOT NULL AUTO INCREMENT ,
map_id INT  NOT NULL ,
neighbor1 INT  NOT NULL ,
neighbor2 INT  NOT NULL ,
...
INDEX IX_neighbor1 (map_id, neighbor1),
INDEX IX_neighbor2 (map_id, neighbor2)
)

Я не уверен, какой из них будет считаться более «нормализованным», поскольку вариант 1 включает в себя повторяющиеся записи (включая дублирование любых свойств, которые имеет отношение), а вариант 2 представляет собой довольно странный дизайн базы данных, который просто не кажется нормализованным. Ни один из вариантов не очень экономичен в пространстве. Для 10 карт вариант 1 использовал 300 000 строк, занимающих 12 МБ файлового пространства. Вариант 2: 150 000 строк занимают 8 МБ файлового пространства. В обеих таблицах индексы занимают больше места, чем данные, учитывая, что данные должны занимать около 20 байт на строку, но на самом деле они занимают 40-50 байт на диске.

Третий вариант вообще не будет нормализован, но будет невероятно эффективным с точки зрения пространства и строк. Это включает в себя размещение поля VARBINARY в map_cell и сохранение бинарного списка соседей в самой таблице ячеек. Это займет 24-36 байтов на ячейку, а не 40-50 байтов на отношение. Это также уменьшит общее количество строк, а запросы к таблице ячеек будут выполняться очень быстро из-за кластеризованного первичного ключа. Однако выполнить соединение с этими данными было бы невозможно. Любые рекурсивные запросы должны выполняться шаг за шагом. Кроме того, это просто очень уродливый дизайн базы данных.

К сожалению, мне нужно, чтобы мое приложение хорошо масштабировалось и не сталкивалось с узкими местами SQL всего с 50 картами. Если я не могу придумать что-то еще, последний вариант может быть единственным, который действительно работает. Прежде чем запрограммировать такую ​​гнусную идею в код, я хотел убедиться, что четко просматриваю все варианты. Может быть другой шаблон проектирования, о котором я не думаю, или, может быть, проблемы, которые я предвижу, не так серьезны, как кажутся. В любом случае, я хотел получить мнение других людей, прежде чем заходить слишком далеко.


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


person jaminv    schedule 20.08.2013    source источник
comment
Ваша основная цель — эффективность использования пространства или эффективность исполнения? И какие запросы вы ожидаете делать к этим данным? (примеры были бы хороши)   -  person RBarryYoung    schedule 20.08.2013
comment
Моя цель состоит в том, чтобы держать оба в пределах приемлемого диапазона. Эффективность выполнения важна, но я не хочу чрезмерно оптимизировать, когда в любом случае все еще есть накладные расходы AJAX. Эффективность использования пространства не так уж важна, если мы говорим не о гигабайтах, а о пространстве сервера. Моя главная забота — не допустить, чтобы большие таблицы сокращали время SQL-запросов. Самый сложный запрос будет рекурсивным, начиная с одной ячейки и разветвляясь наружу, а затем принимая решения на основе свойств этих ячеек. Это будет сделано с помощью сочетания кода SQL и кода приложения.   -  person jaminv    schedule 20.08.2013
comment
1) В вашей схеме отсутствуют внешние ключи 2) перечисление соседей не требуется, достаточно будет простой таблицы соединений {neighbour1‹--›neibour2} (с двумя внешними ключами) 3 ) является ли граф неориентированным? (я полагаю, что это так) 4) петли всегда будут проблемой; по крайней мере в SQL.   -  person wildplasser    schedule 23.08.2013
comment
1) Внешние ключи определенно помогут. 2) map_cell_neighbors и есть такая таблица соединений, за вычетом внешних ключей. В основном индексы должны быть внешними ключами. Меня беспокоит скорость, когда она (быстро) достигает миллионов строк. 3) Да. Это диаграмма Вороного, которая была смягчена для нормализации размера ячейки. 4) Я не уверен, что смогу избежать необходимости делать каждую итерацию отдельным запросом и принимать какие-то решения в коде приложения. Возможно, я мог бы использовать подзапросы, чтобы докопаться до фиксированной глубины, но я хорошо осведомлен о проблемах с циклами SQL. Я бы предпочел делать такие вещи в коде приложения.   -  person jaminv    schedule 23.08.2013


Ответы (1)


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

Если SQL Server, например, является опцией, вы можете хранить полигоны как типы геометрии, использовать встроенную пространственную индексацию и методы, совместимые с OGC, такие как «STContains», «STCrosses», «STOverlaps», «STTouches». .

Пространственные индексы SQL Server после разложения полигонов на различные слои b-дерева также используют тесселяцию для индексации соседних ячеек, которых касается данный полигон, на данном уровне индекса дерева.

Существуют и другие основные базы данных, которые также поддерживают пространственные типы, включая MySQL< /а>

person mdisibio    schedule 22.08.2013
comment
Это определенно то, что я искал. Я не знал, что такие функции существуют. Мое приложение работает на веб-сервере Linux с MySQL. Похоже, что MySQL также предлагает такие функции. Я рассмотрю детали. - person jaminv; 22.08.2013
comment
Вопрос: действительно ли очень эффективно использовать эти тесты пространственных отношений в предложениях WHERE? Мои запросы будут примерно такими: SELECT * FROM map_cell c1 LEFT JOIN map_cell c2 ON c1.map_id = c2.map_id AND Touches(c1.geom, c2.geom), чтобы вернуть всех соседей для конкретной ячейки. Сможет ли он оптимально обработать такой запрос, если предположить, что в таблице более 100 000 строк и 5 000 строк, соответствующих критерию c1.map_id=c2.map_id? - person jaminv; 22.08.2013
comment
Я знаком с SQL Server, но не с MySQL, поэтому не могу быть точным. Говоря абстрактно, использование механизма базы данных с поддержкой пространственных данных для выполнения этого запроса будет наиболее оптимизированным подходом вместо того, чтобы пытаться выполнять большие непространственные рекурсии. В частности, на вопрос о производительности отвечает более четкое определение ваших потребностей. Это одноразовый запрос? Это что-то динамическое, которое будет выполняться только для одной ячейки за раз? Если данные карты являются статическими, вы можете создать таблицу, содержащую декартово произведение всех соприкасающихся ячеек, и заполнить ее один раз (или периодически обновлять). - person mdisibio; 22.08.2013
comment
Запросы часто будут рекурсивными. Мне регулярно нужно выходить из одной или нескольких ячеек и находить все ячейки в пределах определенного диапазона (например, находить все ячейки в пределах видимости или диапазона мобильности). Схема карты исправлена. После создания свойства ячейки могут измениться, но структура диаграммы никогда не изменится. Если две клетки являются соседями, они всегда останутся такими. Однако декартово произведение составляет 15 000 отношений на карту, и я обеспокоен тем, что в какой-то момент это перегрузит SQL. Мой ОП продемонстрировал такую ​​таблицу и то, как быстро она будет расти. - person jaminv; 22.08.2013
comment
После быстрого поиска в Google не похоже, что в MySQL есть функция «Расстояние», которую делает SQL Server. Тем не менее... Я призываю вас сначала немного поработать с типом геометрии и функциями. Думайте о наборах (или в этом случае «набор» ячеек может быть количественно определен как ограничивающая рамка), и, вероятно, вы сможете устранить рекурсию. Кроме того, хотя в декартовой таблице продуктов могут быть тысячи строк, цель состоит в том, чтобы исключить любые вычисления во время выполнения и представить простой поиск, индексированный по ячейке... но выполняйте его, только если он соответствует вашим требованиям. - person mdisibio; 23.08.2013
comment
Свойства ячеек могут влиять на расстояние. Пересечение реки повлияет на движение, а большая высота повлияет на зрение. Водяные клетки полностью блокируют движение. Несмотря на это, расстояние (для моих целей) равно центру центра; центры ячеек не требуют геометрических объектов, а расстояние легко вычислить. Я мог бы сделать это, если бы хотел принимать большинство решений в коде приложения, но этому коду все равно потребуются все данные о соседях. В любом случае, это дает мне много идей. Я собираюсь просто попробовать несколько вещей и протестировать с кучей данных. Спасибо за помощь. - person jaminv; 23.08.2013