Выделить подписку в отдельную сущность: она должна держать список подписанных пользователей на данное событие, при этом сделать синхронный кэш в памяти (ну или просто кэш) - т.е. вот пользователь подписывается на событие икс в категории игрек - записываем это в профиль юзера и добавляем юзера в список подписок категории "игрек.событие" . Как только событие происходит - нам не надо проходить по всем профилям пользователей, а достаточно просто пройтись по всем подписанным пользователям. Либо, можно оптимизировать работу с БД и переложить на её плечи проверку "подписан ли пользователь икс на подписку игрек". Как именно делать оптимальнее - зависит от многих факторов: самый простой вариант - это в памяти приложения, редис же имеет смысл для кластеризации и распределения нагрузок - но это может несколько увеличить время обработки запроса.