16. 16
Доменный объект
public class MyObject {
private long id;
private String title;
...
public long getId() {
return id;
}
public String getTitle() {
return title;
}
public void setTitle( String newTitle ) {
title = newTitle;
}
}
17. public class TitleCommand extends Command {
private String newTitle;
private String oldTitle;
public TitleCommand( MyObject o, String newTitle ) {
super( o );
this.newTitle = newTitle;
}
@Override
public void apply() {
oldTitle = o.getTitle();
o.setTitle( newTitle );
}
@Override
public void undo() {
o.setTitle( oldTitle );
}
}
17 Класс для изменения названия объекта
18. public class MyObjectService {
@Autowired
private MongoCommandsDao commandsDao;
@Autowired
private MongoMyObjectDao objectDao;
public void updateTitle( MyObject o, String title ) {
Command c = new TitleCommand( o, title );
c.apply();
commandsDao.save( c );
objectDao.save( o );
}
}
18 Сервис изменения заголовка объекта
19. public class MongoMyObjectDao {
@Autowired
private MongoOperations mongo;
public void save( MyObject o ) {
mongo.save( o );
}
}
19 Простейшая реализация репозитория
20. 20
Обновление объекта целиком
• изменение всех деревьев индексов
• блокировка всей БД на время записи
документа
• размещение объекта целиком на диске
• добавление документа в oplog целиком
– а значит еще раз запись на диск
– передача объекта по сети для репликации
21. 21
public class TitleCommand extends Command {
private String newTitle;
private String oldTitle;
public TitleCommand( MyObject o, String newTitle ) {
super( o );
this.newTitle = newTitle;
}
@Override
public Update getUpdate() {
return Update.update( "title", newTitle );
}
@Override
public void apply() {
oldTitle = o.getTitle();
o.setTitle( newTitle );
}
@Override
public void undo() {
o.setTitle( oldTitle );
}
}
22. public class MyObjectService {
@Autowired
private MongoCommandsDao commandsDao;
@Autowired
private MongoMyObjectDao objectDao;
public void updateTitle( MyObject o, String title ) {
Command c = new TitleCommand( o, title );
c.apply();
commandsDao.save( c );
objectDao.updateFromCommand( c );
}
}
22 Рефакторинг сервиса под новую идею
23. public class MongoMyObjectDao {
@Autowired
private MongoOperations mongo;
public void updateFromCommand( Command c ) {
Query query = new Query(
Criteria.where( "_id" )
.is( c.getObject().getId() )
);
mongo.updateFirst(
query,
c.getUpdate(),
MyObject.class
);
}
}
23 Рефакторинг репозитория под новую идею
24. 24
Изменение поля объекта
• изменение только одного дерева индекса
(если поле было проиндексировано)
• запись небольшого объема данных на диск
• передача лишь изменяемой части
документа по сети
25. 25
Результаты для 10К документов
.save() .update()
-----------------------
ms Task name
-----------------------
47768 create
10653 read
40214 save
-----------------------
ms Task name
-----------------------
44667 create
11199 read
02472 update
26. Рецепты на данных
выбор между вложенными и
самостоятельными документами
27. 27
Коллекция отдельных документов
{
"eventId" : 1,
"author" : "Alice",
"text" : "I like JavaDay 2014”
"date" : "2014-10-17"
}
{
"eventId" : 1,
"author" : "Bob",
"text" : "I enjoy JavaDay 2014”
"date" : "2014-10-18"
}
28. 28
Коллекция отдельных документов
• Плюсы:
– если нужно вывести один документ, то поиск
займет O(log n) времени
– время записи документов O(1), так как он
полностью размещается в конце файла данных
29. 29
Список вложенных документов
{
"eventId" : 1,
"comments" : [ {
"author" : "Alice",
"text" : "I like JavaDay 2014",
"date" : "2014-10-17”
}, {
"author" : "Bob",
"text" : "I enjoy JavaDay 2014",
"date" : "2014-10-18”
} ]
}
30. 30
Список вложенных документов
• Плюсы:
– маленькое индексное дерево
– меньше чтений с диска, если нужно вывести сразу
все
• Минусы:
– можно достигнуть лимита документа в 16МиБ и
все перестанет работать. Сразу.
– …
31. 31
Добавление элемента в список
• появление фрагментированного участка
• запись объекта целиком в конец
• перестроение индекса
32. Растём вертикально
• обновляйте поля, а не документы, когда это
возможно
• храните документы в массивах, если это
допустимо и они не будут значительно
расти
37. 37
Шардирование
• Плюсы:
– растем линейно на чтение*
– растем линейно на запись*
– всегда актуальные данные
• Минусы:
– нужен правильный индекс и схема
43. 43
Очевидное решение
{ user : 1, friend : 2 }
{ user : 1, friend : 3 }
{ user : 1, friend : 4 }
{ user : 2, friend : 1 }
{ user : 2, friend : 4 }
{ user : 3, friend : 1 }
Мои друзья:
db.friends.find( { user : 1 } );
Мои фолловеры:
db.friends.find( { friend : 1 } );
44. 44
Очевидное решение
{ user : 1, friend : 2 }
{ user : 1, friend : 3 }
{ user : 1, friend : 4 }
{ user : 2, friend : 1 }
{ user : 2, friend : 4 }
{ user : 3, friend : 1 }
Мои друзья:
db.friends.find( { user : 1 } );
Мои фолловеры:
db.friends.find( { friend : 1 } );
shard 1
shard 2
shard 3
45. 45
scatter / gather problem
latency
shard 3
{ user : 3, friend : 1 }
shard 2
{ user : 2, friend : 1 }
{ user : 2, friend : 4 }
shard 1
{ user : 1, friend : 2 }
{ user : 1, friend : 3 }
{ user : 1, friend : 4 }
mongos
• запросить данные
• объединить
• отсортировать
1ms
3ms
0ms
total query
46. 46
Производительное решение
{ user : 1, friend : 2 }
{ user : 1, friend : 3 }
{ user : 1, friend : 4 }
{ user : 2, friend : 1 }
{ user : 2, friend : 4 }
{ user : 3, friend : 1 }
{ user : 1, follower : 2 }
{ user : 1, follower : 3 }
{ user : 2, follower : 1 }
{ user : 3, follower : 1 }
{ user : 4, follower : 1 }
{ user : 4, follower : 2 }
Followers
Friends
47. 47
Хороший ключ
• высокое разнообразие значений
• равномерное распределение по всему
допустимому диапазону
• попадание читающих запросов на один
сервер
48. Растём горизонтально
• для масштабирования – шардирование
• хороший ключ для шардов не монотонен и
разнообразен
• дублирование может значительно повысить
производительность