Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как работает Redis
Redis (REmote DIctionary Server) — это in-memory хранилище данных структурированного формата, работающее как сервер ключ-значение. Это один из самых быстрых способов кэширования и работы с высоконагруженными системами, используемый в миллионах приложений по всему миру.
Архитектура и основной принцип
Redis работает по простой схеме:
- Хранение в памяти — все данные находятся в RAM, что обеспечивает наносекундные операции чтения/записи
- Однопоточность — каждый запрос выполняется атомарно, исключая race conditions
- Сохранение на диск — периодическое сохранение для восстановления после перезагрузки
- Сетевой протокол — клиент подключается через TCP на порт 6379
Клиент (Java приложение)
↓
TCP (RESP протокол)
↓
Redis сервер (однопоточный)
↓
В памяти (RAM)
↓
На диск (RDB/AOF)
Типы данных в Redis
Redis поддерживает 5 основных типов:
1. String (строки)
// Базовые операции
redisTemplate.opsForValue().set("user:123:name", "John");
String name = redisTemplate.opsForValue().get("user:123:name");
// Атомарные операции
redisTemplate.opsForValue().increment("counter"); // +1
redisTemplate.opsForValue().append("text", " added");
2. List (списки, очереди)
// Очередь задач
redisTemplate.opsForList().rightPush("tasks", "task1", "task2");
String task = redisTemplate.opsForList().leftPop("tasks");
// Блокирующая операция
String item = redisTemplate.opsForList().leftPop("queue", 10, TimeUnit.SECONDS);
3. Set (множества, уникальные значения)
// Пересечение, объединение
redisTemplate.opsForSet().add("users:online", "user1", "user2");
Set<String> online = redisTemplate.opsForSet().members("users:online");
Set<String> common = redisTemplate.opsForSet().intersect("set1", "set2");
4. Sorted Set (отсортированные множества, лидерборды)
// Рейтинговая таблица
redisTemplate.opsForZSet().add("leaderboard", "player1", 1000);
redisTemplate.opsForZSet().add("leaderboard", "player2", 1500);
// Топ 10 игроков
Set<String> top10 = redisTemplate.opsForZSet()
.reverseRange("leaderboard", 0, 9);
5. Hash (хеши, объекты)
// Кэширование объекта
Map<String, String> user = new HashMap<>();
user.put("name", "John");
user.put("email", "john@example.com");
redisTemplate.opsForHash().putAll("user:123", user);
String email = redisTemplate.opsForHash().get("user:123", "email");
Механизм работы в памяти
Redis использует хеш-таблицы с оптимизацией для больших наборов данных:
// Redis внутри работает так:
// Ключ → Хеш значения → Указатель на объект в памяти
// Поиск ключа O(1) время!
String value = redis.get("key"); // Наносеконды
Когда памяти недостаточно, Redis может использовать эвикшн политики (LRU, LFU):
maxmemory-policy:
- noeviction: Отказываем новым данным
- allkeys-lru: Удаляем самые старые ключи
- allkeys-lfu: Удаляем редко используемые ключи
- volatile-lru: Удаляем с TTL, затем LRU
Сохранение данных: RDB и AOF
RDB (Redis Database)
# Снимок памяти в один момент времени
SAVE # Синхронно (блокирует)
BGSAVE # В фоновом потоке
# Результат: dump.rdb файл
Плюсы: быстрая загрузка, компактный файл
Минусы: теряются данные между снимками
AOF (Append-Only File)
# Логирование всех команд
COMMAND → AOF файл → Применяем при загрузке
# Различные стратегии fsync
appendfsync always # Синхронно (медленно, безопасно)
appendfsync everysec # Каждую секунду (баланс)
appendfsync no # ОС решает (быстро, рискованно)
Плюсы: полная история, точное восстановление
Минусы: больший файл, медленнее загружается
Использование с Java (Spring)
@Configuration
public class RedisConfig {
@Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
@Bean
public RedisTemplate<String, Object> redisTemplate(
LettuceConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
return template;
}
}
@Service
public class CacheService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public void cacheUser(User user) {
redisTemplate.opsForValue().set(
"user:" + user.getId(),
user,
Duration.ofHours(1)
);
}
public User getUser(Long id) {
return (User) redisTemplate.opsForValue()
.get("user:" + id);
}
}
Или с аннотациями
@Service
public class UserService {
@Cacheable(value = "users", key = "#id")
public User findById(Long id) {
// Первый вызов — вычисляем
return userRepository.findById(id);
// Последующие вызовы — из Redis
}
@CacheEvict(value = "users", key = "#id")
public void updateUser(User user) {
userRepository.save(user);
}
}
Сложные сценарии: Transactions и Pub/Sub
Транзакции
redisTemplate.execute((RedisCallback<Object>) connection -> {
connection.multi();
connection.set("key1".getBytes(), "value1".getBytes());
connection.set("key2".getBytes(), "value2".getBytes());
connection.exec(); // Атомарно
return null;
});
Pub/Sub (очень осторожно!)
redisTemplate.convertAndSend("channel:events", "event data");
redisTemplate.getConnectionFactory()
.getConnection()
.subscribe(message -> {
System.out.println("Received: " + message);
}, "channel:events".getBytes());
Производительность и лучшие практики
- Используй батчинг — отправляй несколько команд сразу
- Выбери правильную типизацию — Hash вместо String для объектов
- Управляй TTL — удаляй старые кэши автоматически
- Мониторь память — используй
INFO memory - Кластеризуй при масштабировании — Redis Cluster
- Репликация — Master-Slave для высокой доступности
Redis — это не просто кэш, а мощная база данных в памяти, способная хранить структурированные данные и обеспечивать супербыструю обработку высоконагруженных приложений.