Хочу хранить в Монге прайсы.
Значимые колонки:
- ID прайслиста (около 3000 разных прайсов, будет медленно расти. Примерно на 20-30 в месяц)
- ID производителя (около 4000 разных производителей)
- артикул (строка)
- цена
- количество
- наименование
Важные замечания:
В базе будет порядка 100 миллионов строк.
Прайсы могут быть от 100 строк до нескольких миллионов.
Товар - это уникальная комбинация ID бренда + артикул. Уникальных товаров будет около 15-20 миллионов. Т.о. один и тот же товар может повторяться в разных прайсах.
Количество товаров одного бренда может быть от 1000 до несколько миллионов.
Типовой запрос в базу:
Дай список предложений (строк из прайсов) по такому-то списоку товаров.
Т.е. на вход подается N товаров, на выходе все строки, где эти товары есть, т.е. в каких прайс листах по каким ценам и какое количество.
Теперь про шардинг.
Основные требования: главное, чтобы быстро читалось. Скорость записи на втором месте.
Теперь мои мысли.
Т.к. в запрос могут приходить товары 1-2 брендов, то хотелось бы, чтобы запрос пошел только на один шард. Это наводит на мысль шардировать по ID бренда. Но при этом возникает сложность с размером чанков. Как я выше написал, есть бренды с несколькими миллионами позиций (артикулов). Чанк не может содержать меньше, чем все документы с одним конкретным ID производителя. Если не задирать размер чанка в монге, то монга начинает ругаться, что не может перемещать чанки, размер которых больше максимального размера чанка (а оно так и будет).
Если размер чанка задрать, то один станут большими и данные по шардам будут распределены неравномерно.
Если использовать Compound Shard Key, например, ID производителя + артикул, то запрос по N товарам одного бренда вполне может пойти во все шарды, чего хотелось бы избежать.
Собственно вопрос - что посоветуете сделать в моей ситуации?
товар == артикул?
Т.к. скорость записи не так важна, то можно рассмотреть такой вариант - "схлопнуть" все данные по товару, т.е. один артикул содержит список прайсов+производителей где он участвует. Шардить по артиклу.
+ экономия памяти - кол-во документов в 5 раз меньше, артикул (и наименование) общие.
Должен получится один из самых быстрых вариантов на чтение, т.к. по по одному значению вытаскивается вся информация по товару, не бегая по индексу и шардам.
Да, с чтением будет хорошо. Как в таком случае оптимальней прайсы обновлять? Ну чтобы уж не совсем медленно было :) Т.е. приходит новый прайс. Всю старую информацию (все предложения) нужно удалить и залить новый.
Я тут помозговал. Но ведь проблема с тем, что когда приходит запрос (как я с исходном вопросе описывал) с пачкой товаров одного бренда, то при шардировании только по товару этот запрос будет в высокой вероятностью направлен на кучу шардов. А хотелось бы количество затрагиваемых шардов уменьшить. Это снова подталкивает к идее шардировать по бренду со всему вытекающими последствиями.
> хотелось бы количество затрагиваемых шардов уменьшить
для этого можно попробовать, как вы написали, compound index (бренд, артикул), тогда одно-брендовые чанки будут ближе к друг другу.
А если нужно уж совсем точно, то можете просто для каждого прайса сделать свою коллекцию.
Данных навскикду 15-30Гб, и шардинг не нужен. Если будет чтения не хватать, то можно обычную реплику для балансинга чтения, либо самодельный шардинг (как это сделали в Яндекс диске) - разные прайсы на разные сервера.
Я бы на вашем месте уже что-нибудь попробовал и замерил скорость, какая у вас нагрузка планируется, может вам шардинг и не нужен?
> хотелось бы количество затрагиваемых шардов уменьшить
по идее mongos не должен ваш запрос ($in) разрывать на части и отправлять точечно по шардам, поэтому при любом индексе запрос полетит на все шарды. Точечно будет если вы вручную сделаете findOne на каждый товар, но так (скорее всего) будет медленее.
Ещё точечно будет если вы будете делать запрос $lte + $gte, т.е. перед запросом определять диапазон товаров (или несколько диапазонов), а при получении списка выкидывать лишние, но это геморно.
Дмитрий Лабутин: Если найдете, бросте ссылку, я сходу не нашел. Это теория, монгос перепраляет запрос 2 путями: целенаправленный (один или несколько нод), либо на все ноды. Про модификацию запроса ничего не сказано, т.е. в худшем случае запрос полетит на все ноды, в лучшем - монгос переберет каждый элемент массива, составит список нод, и отправит всем копию этого запроса - т.е. каждая нода* будет работать с полным списком товара.
Быстрее будет провести эксперимент, сделать запрос и посмотреть что explain выдаст.
в лучшем - монгос переберет каждый элемент массива, составит список нод, и отправит всем копию этого запроса - т.е. каждая нода* будет работать с полным списком товара.