Mastering Redis: From Basics to High Availability in Java Backend

This article walks through what Redis is, its data types, caching usage with Spring Boot, performance advantages, eviction policies, persistence mechanisms, master‑slave replication, Sentinel high‑availability, and practical interview‑style Q&A, providing a comprehensive guide for backend developers.

Java Interview Crash Guide
Java Interview Crash Guide
Java Interview Crash Guide
Mastering Redis: From Basics to High Availability in Java Backend

What is Redis

Redis is an open‑source, high‑performance key‑value in‑memory database written in C under the BSD license. It can be used as a database, cache, or message broker and supports various data structures.

Performance: Data resides in memory, offering extremely fast read/write speeds and supporting up to 100,000 QPS.

Single‑threaded: Thread‑safe with an I/O multiplexing model.

Data types: Strings, hashes, lists, sets, sorted sets, etc.

Persistence: Supports data persistence to disk.

High availability: Master‑slave replication, Sentinel, and clustering.

Distributed lock: Can be used for locking mechanisms.

Pub/Sub: Supports publish‑subscribe messaging.

Five Data Types

Redis provides five core data structures:

String: Basic type, can store binary data up to 512 MB.

Hash: A map of fields and values, ideal for storing objects.

List: Ordered collection of strings, useful for queues and timelines (e.g., Twitter followers).

Set: Unordered collection of unique strings.

Sorted Set (Zset): Set with a double‑precision score for automatic ordering.

Redis data types diagram
Redis data types diagram

Redis Cache in Spring Boot

Two common ways to use Redis in Spring Boot:

Directly via RedisTemplate.

Through Spring Cache annotations.

Add the following dependencies to pom.xml:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

Configure application.yml:

server:
  port: 8082
spring:
  cache:
    type: redis
  redis:
    host: 127.0.0.1
    port: 6379
    database: 0
    lettuce:
      pool:
        max-active: 100

Define a serializable User entity and a custom RedisCacheConfig that sets key and value serializers:

public class User implements Serializable {
    private static final long serialVersionUID = 662692455422902539L;
    private Integer id;
    private String name;
    private Integer age;
    // getters, setters, constructors, toString omitted for brevity
}

@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisCacheConfig {
    @Bean
    public RedisTemplate<String, Serializable> redisCacheTemplate(LettuceConnectionFactory connectionFactory) {
        RedisTemplate<String, Serializable> template = new RedisTemplate<>();
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setConnectionFactory(connectionFactory);
        return template;
    }
}

Use @Cacheable, @CachePut, and @CacheEvict annotations in a service layer to manage cache entries:

@Service
public class UserServiceImpl implements UserService {
    private static final Logger logger = LogManager.getLogger(UserServiceImpl.class);
    private static final Map<Integer, User> userMap = new HashMap<>();
    static {
        userMap.put(1, new User(1, "肖战", 25));
        userMap.put(2, new User(2, "王一博", 26));
        userMap.put(3, new User(3, "杨紫", 24));
    }

    @CachePut(value = "user", key = "#user.id")
    @Override
    public User save(User user) {
        userMap.put(user.getId(), user);
        logger.info("Saving user: {}", user);
        return user;
    }

    @CacheEvict(value = "user", key = "#id")
    @Override
    public void delete(int id) {
        userMap.remove(id);
        logger.info("Deleted user with id {}", id);
    }

    @Cacheable(value = "user", key = "#id")
    @Override
    public User get(Integer id) {
        logger.info("Fetching user with id {}", id);
        return userMap.get(id);
    }
}

Typical controller endpoints:

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private RedisTemplate<String, Serializable> redisCacheTemplate;
    @Autowired
    private UserService userService;

    @RequestMapping("/test")
    public void test() {
        redisCacheTemplate.opsForValue().set("userkey", new User(1, "张三", 25));
        User user = (User) redisCacheTemplate.opsForValue().get("userkey");
        logger.info("Current object: {}", user);
    }

    @RequestMapping("/add")
    public void add() {
        User user = userService.save(new User(4, "李现", 30));
        logger.info("Added user: {}", user);
    }

    @RequestMapping("/delete")
    public void delete() {
        userService.delete(4);
    }

    @RequestMapping("/get/{id}")
    public void get(@PathVariable("id") String idStr) throws Exception {
        if (StringUtils.isBlank(idStr)) {
            throw new Exception("id is empty");
        }
        Integer id = Integer.parseInt(idStr);
        User user = userService.get(id);
        logger.info("Fetched user: {}", user);
    }
}

Enable caching in the main application class:

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableCaching
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Cache Issues

Cache consistency: In distributed environments, cache and database may become inconsistent. If strong consistency is required, avoid using cache.

Cache avalanche: When many keys expire simultaneously, the database can be overwhelmed. Mitigation: add random jitter to expiration times, stagger key TTLs, or keep hot data without expiration.

Cache penetration: Requests for non‑existent keys hit the database repeatedly. Mitigation: validate parameters, return early, or use a Bloom filter to pre‑check existence.

Cache breakdown (thundering herd): A hot key expires and many concurrent requests hit the database. Mitigation: keep hot keys permanently cached, use mutex locks, or employ double‑checked locking.

Why Is Redis So Fast?

All data resides in memory, giving O(1) access similar to a HashMap.

Simple data structures lead to straightforward operations.

Single‑threaded design eliminates context switches and lock contention.

Uses non‑blocking I/O with an event‑driven multiplexing model.

Redis vs. Memcached

Persistence: Redis can persist data to disk; Memcached cannot.

Data types: Redis supports five complex data structures; Memcached only stores simple strings.

Implementation: Redis implements its own protocol and VM; Memcached uses a simpler protocol.

Value size: Redis values can be up to 1 GB, while Memcached limits values to 1 MB.

Eviction Policies

Redis provides six eviction strategies (see diagram):

Redis eviction policies
Redis eviction policies

Since Redis 4.0, LFU (least‑frequency‑used) policies ( volatile‑lfu and allkeys‑lfu) are also available.

Persistence

Redis offers two persistence mechanisms:

RDB (snapshot): Periodically writes the entire dataset to a binary dump.rdb file. It forks a child process to perform the write, using copy‑on‑write to avoid blocking the main thread.

AOF (append‑only file): Logs every write command to appendonly.aof. Configurable appendfsync policies ( always, everysec, no) control durability. AOF provides higher durability but larger files.

Both can be enabled simultaneously; on restart Redis prefers AOF for recovery.

Master‑Slave Replication

Replication provides read scalability and high availability. The master handles writes; slaves handle reads. The replication process:

Slave issues SLAVEOF masterIP masterPort and stores master info.

Slave periodically pings the master to establish a socket connection.

Master sends a full data dump (RDB) to the slave.

During the dump, write commands are buffered in the replication backlog.

After the dump, the master continuously streams new write commands to the slave.

Redis uses PSYNC (partial sync) to support both full and incremental synchronization. Key concepts:

runId: Unique identifier generated at each server start.

offset: Byte offset of the replication stream.

repl_backlog_size: Fixed‑size circular buffer on the master (default 1 MB).

Full sync steps:

Slave sends PSYNC ? -1.

Master replies with FULLRESYNC {runId} {offset} and starts a background save (RDB).

Master streams the RDB file; writes during this period are stored in the backlog.

Slave loads the RDB, clears its old data, and starts applying backlog updates.

Partial sync (incremental) occurs when the slave reconnects with a known runId and offset. If the backlog still contains the missing data, the master sends only the delta; otherwise a full sync is triggered.

Sentinel

Redis Sentinel provides automatic failover and service discovery. Its main functions are:

Monitoring: Periodically PING masters, slaves, and other Sentinels.

Notification: Alerts administrators or applications when a server goes down.

Automatic failover: Promotes a slave to master when the current master is objectively down.

Configuration provider: Clients query Sentinels to obtain the current master address.

Sentinel workflow:

Each Sentinel sends a PING to known instances every second.

If no PONG is received within down‑after‑milliseconds, the instance is marked as subjectively down (SDOWN).

Sentinels exchange their SDOWN opinions; if a quorum agrees, the instance becomes objectively down (ODOWN).

When ODOWN is confirmed, Sentinels run a leader election to select a new master from the slaves.

The new master is announced, and remaining slaves are reconfigured to replicate from it.

If the original master recovers and starts responding to PING, its SDOWN status is cleared.

Sentinel architecture diagram
Sentinel architecture diagram

Conclusion

This interview‑style article covered what Redis is, its core features, caching usage in Java Spring Boot, performance reasons, eviction policies, persistence options, high‑availability mechanisms such as master‑slave replication and Sentinel, and practical solutions to cache‑related problems like avalanche, penetration, and breakdown.

Author: 坚持就是胜利 Source: ue​jin.cn/post/6844904017387077640
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Javadatabasehigh availabilityRedisSpring Boot
Java Interview Crash Guide
Written by

Java Interview Crash Guide

Dedicated to sharing Java interview Q&A; follow and reply "java" to receive a free premium Java interview guide.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.