Pull to refresh

Comments 15

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

Потомственная хабрагадалка
К примеру у нас есть таблица posts
и по этой таблице строится N сущностей, «в порядке добавления», «не прочитанное», «популярное», «отклоненное», «Новое», «отмодеренное»…

Получается что у нас есть N коллекций (возможно) одних и тех самых данных и при изменении/удалении/добавлении данных из таблицы posts нам необходимо вызвать инвалидатор у каждой из этих сущностей?
Причем если это апдейт — то вызовов будет N*2

Как то не очень эффективно, как мне кажеться. Возможно я вас просто не правильно понял.

Видимо, неправильно, потому как не надо. Напишите свой пример в терминах sql и я напишу как он будет инвалидироваться.
1. select * from posts order by time_added
2. select post.* left join post_readed where user_id=1 and post_readed.post_id=posts.id from posts where post_readed.id is null
3. select posts.* inner join post_rating where post_rating.post_id=posts.id from posts order by post_rating.rating desc
4. select * from posts where status=«rejected»
5. select * from posts where status=«new»
6. select * from posts where status=«accepted»

и тут происходит апдейт:
update posts set title=«new title», status=«accepted», time_added=time() where id=105

Прощу прощение если где-то опечатался в названиях таблиц или полей. Суть я думаю будет ясна.

Спасибо.
Для краткости буду обозначать запросы q1,...,q6.

Очевидно, q1 должен инвалидироваться при любом изменении в таблице, ему будет соответствовать инвалидатор с пустой схемой (схема — список полей). q2 и q3 содержат join-ы, как их обрабатывать я в статье не упоминал. Можно огрод городить, но на данный момент я просто игнорирую условия на приджойненные таблицы, т.е. такие запросы могут инвалидироваться чаще, чем необходимо. q4,q5,q6 — запросы с простым условием равенства поля, как раз то, с чем описанная система справляется лучше всего.

Вот набор инвалидаторов, который формируется после выполнения q1-q6:
""-- (пустая схема)
    q1, q3
user_id = ?:
    1: q2
status = ?:
    "rejected": q4
    "new": q5
    "accepted": q6

Теперь происходит обновление строки, при обновлении важны все колонки, а не только те, которые меняются, причём необходимо знать и старое и новое состояния объекта/ряда. Приходиться перед update-ом и delete-ом выбирать ряд, за всё приходится платить (впрочем ORM может делать это и так, чтобы удалять/обновлять зависимые объекты, например).

Итак мы выбираем объект
{id: 105, user_id: 2, title: "old title", status: "new", time_added: "..."}
и обновляем его до
{id: 105, user_id: 2, title: "new title", status: "accepted", time_added: "..."}

Подставляем старый объект в схемы, получаем "", user_id=2, status="new", подставляем новый, получаем "", user_id=2, status="accepted". Собирая всё вместе, видим что надо сбросить q1,q3,q5 и q6.
Извиняюсь что не написал это в предыдущем посте.
Также интересует в каком виде вы храните данные в кеше? В виде массива с данными чтото типа {id: 42, a: 1, b: 10}?
И как быть с большими наборами данных, а также с постраничным кешерованием (я подразумеваю что хранится данные должны блоками с определенным количеством элементов, и если оно будет хранится блоками то и инвалидировать нужно все эти блоки)?
Ибо выходит что бы поднять, например, 20 первых записей нужно подымать кеш и десиарелизировать 100000 элементов, а то и больше…

Спасибо.
Данные выдаются из кеша в том же виде, что выдаёт ORM в том случае если кеш не используется, таким образом кеширование прозрачно для пользователя ORM-а, ему собственно всё равно откуда пришли данные из кеши или из базы. Обычно это модели или списки моделей, но могут быть и словари/хеши/просто списки.

Кешируется то, что запрашивается, т.е. если вы пользователь ORM-а запрашивает всё сразу, то и в кеш ляжет 100000 записей, если постранично, то будет ложиться отдельными блоками с одними и теми же инвалидаторами, ну и в случае чего сброшены будут все вместе.
Гугл что-то не показывает толковых ссылок на тему, объясняющую что такое, собственно «инвалидатор».
инвалидация — процесс удаления кеша, когда он «протухает». можно, как и написано, удалять его по истечении какого-то времени, либо при каких-то событиях.

для меня, правда, остается открытыми два вопроса:
— есть главная страница, как на хабре: посты и кол-во комментариев. можно повесить инвалидацию на добавление новых комментов, но тогда если коммент был добавлен _не_ для поста на главной, кеш сгорит впустую: на главной ничего не изменилось.

— если страница тяжелая, то каждый первый человек после инвалидации будет натыкаться на страницу без кеша. можно ведь как-нибудь при инвалидации перед сжиганием кеша делать новый и подменять, чтобы всегда и все попадали на кеш?
1. С количеством комментариев возможно много вариантов:
— счётчик денормализуется и храниться в посте, минус — новые посты будут часто менятся и стираться из кеша.
— счётчик будет получаться для каждого поста на странице отдельным запросом
select count(*from comment where post_id = <post_id>;
такие запросы будут инвалидироваться только тогда когда надо, сами посты будут инвалидироваться независимо, т.е. значительно реже, чем в первом случае. Минус много запросов, но если они, как правило, будут браться из кеша, то это нормально.
— вариация на тему, считать комментарии сразу для всех постов на странице:
select post_id, count(*from comment 
where post_id in (<post_id1><post_id2>, ...) group by 1;
Минусы — такие запросы будут чаще инвалидироваться, чем в предыдущем варианте, более сложная реализация и промахи при изменении списка постов на странице.

2. Если сбросить весь кеш, то первый человек получит лаг, ничего не поделаешь. Но кеш обычно весь не сбрасывается, тяжёлая страница делает много запросов, которые инвалидируются по разным условиям.
Грубо говоря, инвалидатор — что-то, что инвалидирует, или помогает инвалидировать.

Я инвалидаторами называю структуры {условие на модель} -> {список ключей, которые нужно сбросить при выполнении условия}, описанные в статье.
самая быстрая инвалидация кэша =)

net/ipv4/route.c:823

/*
* Perturbation of rt_genid by a small quantity [1..256]
* Using 8 bits of shuffling ensure we can call rt_cache_invalidate()
* many times (2^24) without giving recent rt_genid.
* Jenkins hash is strong enough that litle changes of rt_genid are OK.
*/
static void rt_cache_invalidate(struct net *net)
{
unsigned char shuffle;

get_random_bytes(&shuffle, sizeof(shuffle));
atomic_add(shuffle + 1U, &net->ipv4.rt_genid);
}
Пока не совсем понятно что к чему, жду продолжения и примеров реализации! Интересно.
Вы меня извините, но я не могу понять:
1. Абстрагируемся от конкретной реализации движка СУБД и ее языка.
2. При этом, можем утверждать, что при «category_id=? and published=?» «не требуется последовательная проверка условий».

Мне кажется, тема кеширования настолько широка и «индивидуальна», что абстрагироваться от конкретной реализации можно только в очень общих понятиях кеширования. А у вас, скорее, частная.
Sign up to leave a comment.

Articles