2013-04-26 3 views
2

Мы пытаемся создать приложение для уведомлений для наших пользователей с помощью mongo. мы создали 1 mongodb на 10GB RAM, 150GB SAS HDD 15K RPM, 4 Core 2.9GHZ xeon intel XEN VM.Производительность MongoDb медленная даже с использованием индекса

DB схемы: -

{ 
    "_id" : ObjectId("5178c458e4b0e2f3cee77d47"), 
    "userId" : NumberLong(1574631), 
    "type" : 2, 
    "text" : "a user connected to B", 
    "status" : 0, 
    "createdDate" : ISODate("2013-04-25T05:51:19.995Z"), 
    "modifiedDate" : ISODate("2013-04-25T05:51:19.995Z"), 
    "metadata" : "{\"INVITEE_NAME\":\"2344\",\"INVITEE\":1232143,\"INVITE_SENDER\":1574476,\"INVITE_SENDER_NAME\":\"123213\"}", 
    "opType" : 1, 
    "actorId" : NumberLong(1574630), 
    "actorName" : "2344" 
} 

DB stats :- 
db.stats() 
{ 
    "db" : "UserNotificationDev2", 
    "collections" : 3, 
    "objects" : 78597973, 
    "avgObjSize" : 489.00035699393925, 
    "dataSize" : 38434436856, 
    "storageSize" : 41501835008, 
    "numExtents" : 42, 
    "indexes" : 2, 
    "indexSize" : 4272393328, 
    "fileSize" : 49301946368, 
    "nsSizeMB" : 16, 
    "dataFileVersion" : { 
     "major" : 4, 
     "minor" : 5 
    }, 
    "ok" : 1 
} 

индекс: - идентификатор пользователя и _ID

мы пытаемся выбрать последние 21 уведомлений для одного пользователя.

db.userNotification.find({ "userId" : 53 }).limit(21).sort({ "_id" : -1 }); 

, но этот запрос занимает слишком много времени. Пт Апр 26 05: 39: 55.563 [conn156] запрос UserNotificationDev2.userNotification query: {query: {userId: 53}, orderby: {_id: -1}} cursorid: 225321382318166794 ntoreturn: 21 ntoskip: 0 nscanned: 266025 keyUpdates: 0 Количество: 2 замка (микро) r: 4224498 нетто: 21 reslen: 10295 2581ms

даже счет занимает много времени.

Fri Apr 26 05:47:46.005 [conn159] command UserNotificationDev2.$cmd command: { count: "userNotification", query: { userId: 53 } } ntoreturn:1 keyUpdates:0 numYields: 11 locks(micros) r:9753890 reslen:48 5022ms 

Мы делаем некоторые ошибки в запросе?

Пожалуйста, помогите !!!

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

+2

Вы можете запустить 'explain()' в своей находке, в частности 'db.userNotification.find ({" userId ": 53})' – cirrus

+2

Можете ли вы запустить getIndexes() тоже? – cirrus

ответ

0

Один из вариантов заключается в том, чтобы попытаться очертить, тогда вы можете равномерно распределять уведомления между осколками, поэтому, когда вам нужно выбрать, вы сканируете меньший поднабор данных. Нужно решить, какими будут ваши осколки. Для меня это выглядит как OperationType или userName, но я не знаю ваших данных достаточно хорошо. Другое дело, почему вы сортируете by _id?

+0

Поскольку мне нужны последние вставленные уведомления пользователя – user2323026

+1

sharding - это не ответ, когда у них нет оптимальных индексов. –

0

Я только что попытался воспроизвести вашу проблему. Создано 140.000.000 вставок в userNotifications. Без индекса на userId Я получил ответы от 3-4 секунд. После того, как я создал индекс на userId, время уменьшилось до почти мгновенных ответов.

db.userNotifications.getIndexes() 

[ { "v": 1, "ключ": { "_id": 1 }, "н.з.": "test.userNotifications", "название" : "ID " }, { "v": 1, "ключ": { "идентификатор пользователя": 1 }, "н.з.": "test.userNotifications", "имя": "UserID_1" } ]

Другое дело: когда ваш выбор происходит, система постоянно записывает коллекцию userNotification для mongo? Mongo блокирует всю коллекцию, если это произойдет. Если это так, то
Я бы разделил чтение и запись между мастером и ведомым (см. Репликацию), а также сделать некоторые осколки. Btw. Какой язык вы используете для своего приложения?

+2

вы не можете «разбить» чтение и запись между первичным и вторичным, потому что второстепенное должно делать точно так же, как и многие записи, как первичные. –

2

Вы запрашиваете userId, но не индексируете его в любом месте. Мое предложение - создать индекс на { "userId" : 1, "_id" : -1 }.Это создаст дерево индексов, которое начинается с userId, а затем _id, что почти точно соответствует вашему запросу. Это самый простой/самый гибкий способ ускорить ваш запрос.

Еще один, более эффективный с точки зрения памяти подход - это сохранить ваш userId и timestamp как строку в _id, например _id : "USER_ID:DATETIME. Пример:

{_id : "12345:20120501123000"} 
{_id : "15897:20120501124000"} 
{_id : "15897:20120501125000"} 

Уведомление _id - это строка, а не MongoId. Тогда ваш запрос выше становится регулярным выражением:

db.userNotification.find({ "_id" : /^53:/ }).limit(21).sort({ "_id" : -1 }); 

Как и следовало ожидать, это будет возвращать все уведомления для USERID 53 в порядке убывания. Эффективная часть памяти выполнена в два раза:

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

Re: count. Графу требуется время, потому что он просматривает всю коллекцию.

Re: Ваша схема. Я предполагаю, что для вашего набора данных это лучший способ использовать вашу память. Когда объекты становятся большими, а ваши запросы сканируют по нескольким объектам, их нужно будет полностью загрузить в память (у меня был убийца OOM, который убил мой экземпляр mongod, когда я отсортировал с 2000 объектами 2 МБ на машине с объемом 2 ГБ). С большими объектами ваше использование ОЗУ будет сильно колебаться (не говоря уже о том, что они ограничены до точки). С вашей текущей схемой mongo будет намного легче загружать только те данные, которые вы запрашиваете, что приводит к меньшему обмену и более последовательному использованию памяти.

+1

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

+0

@ АсяКамский - Спасибо за информацию. Насколько мне известно, в Adil

+1

они использовали индексы еще до 2,4, но не эффективно. Представьте себе разницу в производительности между прохождением и чтением записей в b-дереве индекса между определенными диапазонами, а также подсчет количества из них. –

0

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

Вам нужен составной индекс для userId, _id. Это будет поддерживать запросы, которые запрашивают только userId, но они также используются запросами userId, которые сортируют/ограничивают _id.

Когда вы добавляете индекс {userId: 1, _id: -1}, не забудьте указать индекс только для userId, поскольку он станет избыточным.

Что касается count(), убедитесь, что вы используете 2.4.3 (последняя версия), были значительные улучшения в том, как count() использует индексы, которые привели к значительно лучшей производительности.