redis_sinterSet intersection — O(N*M) in the worst case, expensive with large input sets
SINTER computes the intersection of multiple sets and returns only members present in all sets. Redis optimises by iterating the smallest set and checking membership in the others, but it is still O(N*M) worst case where N is the size of the smallest set and M is the number of input sets. On large, frequently overlapping sets called at request frequency this causes significant CPU and memory overhead.
Common Causes
- —Mutual friend or connection lookups computed live per request
- —Permission intersection checks across multiple roles or groups
- —Content filtering by intersecting multiple tag or category sets
- —Recommendation filtering across multiple user preference sets
How to Fix
- 1.Cache the intersection result with SINTERSTORE and a TTL
- 2.Move SINTER to a background job that refreshes the result periodically
- 3.Compute intersections in application memory when input sets are small enough to fetch cheaply
- 4.Restructure data to pre-compute intersections at write time
Example
typescript
// BAD — mutual friends computed on every profile visit
app.get('/api/users/:id/mutual', async (req, res) => {
const mutual = await redis.sinter(
`friends:${req.user.id}`,
`friends:${req.params.id}`
);
res.json({ count: mutual.length, sample: mutual.slice(0, 5) });
});
// GOOD — cache the intersection
async function getMutualFriends(redis: Redis, userA: string, userB: string) {
const cacheKey = `mutual:${[userA, userB].sort().join(':')}`;
const cached = await redis.smembers(cacheKey);
if (cached.length > 0) return cached;
const mutual = await redis.sinterstore(cacheKey, `friends:${userA}`, `friends:${userB}`);
await redis.expire(cacheKey, 300); // 5 minute TTL
return redis.smembers(cacheKey);
}
// GOOD — application-level intersection on small bounded sets
const [setA, setB] = await Promise.all([
redis.smembers(`tags:${itemA}`),
redis.smembers(`tags:${itemB}`),
]);
const commonTags = setA.filter(tag => setB.includes(tag));Sort keys consistently for cache hits
When caching an intersection of two sets, sort the key identifiers before joining them:
[userA, userB].sort().join(':'). This ensures mutual:alice:bob and mutual:bob:alice hit the same cache key.