Initial commit: Complete Software Architecture course with 11 modules
- Added comprehensive course structure with 11 modules - Included theoretical content, practical examples, and tasks - Multi-language code examples (Java, Rust, Node.js, Python) - Detailed analysis of architectural approaches and trade-offs - Complete caching strategies documentation - Architecture examples from real-world systems (Twitter, Netflix, Uber) - Structured navigation and learning path - Ready for educational use
This commit is contained in:
commit
15e744da49
107 files changed
+61762
No files matched your search
@@ -0,0 +1,310 @@
|
||||
# Кэширование
|
||||
|
||||
## Навигация
|
||||
- [🏠 Главная страница курса](../../../README.md)
|
||||
- [📚 Модуль 10](../README.md)
|
||||
|
||||
---
|
||||
|
||||
## Стратегии кэширования
|
||||
|
||||
### 1. Cache-Aside Pattern
|
||||
**Технический пример - Advanced Multi-Level Caching System:**
|
||||
|
||||
```java
|
||||
// Advanced Cache Management Service
|
||||
@Service
|
||||
public class AdvancedCacheService {
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@Autowired
|
||||
private CacheMetricsService cacheMetricsService;
|
||||
|
||||
// Multi-level caching
|
||||
private final Map<String, Object> localCache = new ConcurrentHashMap<>();
|
||||
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
|
||||
|
||||
public <T> T get(String cacheKey, Class<T> type, Supplier<T> dataLoader) {
|
||||
// Level 1: Local cache
|
||||
T value = getFromLocalCache(cacheKey, type);
|
||||
if (value != null) {
|
||||
cacheMetricsService.recordCacheHit(cacheKey, CacheLevel.LOCAL);
|
||||
return value;
|
||||
}
|
||||
|
||||
// Level 2: Redis cache
|
||||
value = getFromRedisCache(cacheKey, type);
|
||||
if (value != null) {
|
||||
putToLocalCache(cacheKey, value, Duration.ofMinutes(5));
|
||||
cacheMetricsService.recordCacheHit(cacheKey, CacheLevel.REDIS);
|
||||
return value;
|
||||
}
|
||||
|
||||
// Level 3: Data source
|
||||
cacheMetricsService.recordCacheMiss(cacheKey);
|
||||
value = dataLoader.get();
|
||||
|
||||
if (value != null) {
|
||||
putToLocalCache(cacheKey, value, Duration.ofMinutes(5));
|
||||
putToRedisCache(cacheKey, value, Duration.ofMinutes(30));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public <T> CompletableFuture<T> getAsync(String cacheKey, Class<T> type, Supplier<T> dataLoader) {
|
||||
return CompletableFuture.supplyAsync(() -> get(cacheKey, type, dataLoader), scheduler);
|
||||
}
|
||||
|
||||
public <T> List<T> getBatch(List<String> cacheKeys, Class<T> type, Function<List<String>, List<T>> dataLoader) {
|
||||
List<T> results = new ArrayList<>();
|
||||
List<String> missingKeys = new ArrayList<>();
|
||||
|
||||
// Check local cache first
|
||||
for (String key : cacheKeys) {
|
||||
T value = getFromLocalCache(key, type);
|
||||
if (value != null) {
|
||||
results.add(value);
|
||||
cacheMetricsService.recordCacheHit(key, CacheLevel.LOCAL);
|
||||
} else {
|
||||
missingKeys.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
if (missingKeys.isEmpty()) {
|
||||
return results;
|
||||
}
|
||||
|
||||
// Check Redis cache
|
||||
List<T> redisValues = getFromRedisCacheBatch(missingKeys, type);
|
||||
List<String> stillMissingKeys = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < missingKeys.size(); i++) {
|
||||
String key = missingKeys.get(i);
|
||||
T value = redisValues.get(i);
|
||||
|
||||
if (value != null) {
|
||||
results.add(value);
|
||||
putToLocalCache(key, value, Duration.ofMinutes(5));
|
||||
cacheMetricsService.recordCacheHit(key, CacheLevel.REDIS);
|
||||
} else {
|
||||
stillMissingKeys.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Load missing data
|
||||
if (!stillMissingKeys.isEmpty()) {
|
||||
List<T> loadedData = dataLoader.apply(stillMissingKeys);
|
||||
|
||||
for (int i = 0; i < stillMissingKeys.size(); i++) {
|
||||
String key = stillMissingKeys.get(i);
|
||||
T value = loadedData.get(i);
|
||||
|
||||
if (value != null) {
|
||||
results.add(value);
|
||||
putToLocalCache(key, value, Duration.ofMinutes(5));
|
||||
putToRedisCache(key, value, Duration.ofMinutes(30));
|
||||
}
|
||||
|
||||
cacheMetricsService.recordCacheMiss(key);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public void put(String cacheKey, Object value) {
|
||||
putToLocalCache(cacheKey, value, Duration.ofMinutes(5));
|
||||
putToRedisCache(cacheKey, value, Duration.ofMinutes(30));
|
||||
cacheMetricsService.recordCachePut(cacheKey);
|
||||
}
|
||||
|
||||
public void evict(String cacheKey) {
|
||||
localCache.remove(cacheKey);
|
||||
redisTemplate.delete(cacheKey);
|
||||
cacheMetricsService.recordCacheEvict(cacheKey);
|
||||
}
|
||||
|
||||
public void evictPattern(String pattern) {
|
||||
localCache.entrySet().removeIf(entry -> entry.getKey().matches(pattern));
|
||||
|
||||
Set<String> keys = redisTemplate.keys(pattern);
|
||||
if (keys != null && !keys.isEmpty()) {
|
||||
redisTemplate.delete(keys);
|
||||
}
|
||||
|
||||
cacheMetricsService.recordCacheEvictPattern(pattern);
|
||||
}
|
||||
|
||||
// Cache warming
|
||||
public void warmCache(List<String> keys, Supplier<List<Object>> dataLoader) {
|
||||
log.info("Warming cache with {} keys", keys.size());
|
||||
|
||||
List<Object> data = dataLoader.get();
|
||||
|
||||
for (int i = 0; i < keys.size() && i < data.size(); i++) {
|
||||
String key = keys.get(i);
|
||||
Object value = data.get(i);
|
||||
|
||||
if (value != null) {
|
||||
putToLocalCache(key, value, Duration.ofMinutes(5));
|
||||
putToRedisCache(key, value, Duration.ofMinutes(30));
|
||||
}
|
||||
}
|
||||
|
||||
log.info("Cache warming completed");
|
||||
}
|
||||
|
||||
private <T> T getFromLocalCache(String key, Class<T> type) {
|
||||
Object value = localCache.get(key);
|
||||
return type.cast(value);
|
||||
}
|
||||
|
||||
private <T> T getFromRedisCache(String key, Class<T> type) {
|
||||
try {
|
||||
Object value = redisTemplate.opsForValue().get(key);
|
||||
return type.cast(value);
|
||||
} catch (Exception e) {
|
||||
log.warn("Error getting from Redis cache: {}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private <T> List<T> getFromRedisCacheBatch(List<String> keys, Class<T> type) {
|
||||
try {
|
||||
List<Object> values = redisTemplate.opsForValue().multiGet(keys);
|
||||
return values.stream()
|
||||
.map(type::cast)
|
||||
.collect(Collectors.toList());
|
||||
} catch (Exception e) {
|
||||
log.warn("Error getting batch from Redis cache: {}", e.getMessage());
|
||||
return keys.stream().map(k -> (T) null).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
private void putToLocalCache(String key, Object value, Duration ttl) {
|
||||
localCache.put(key, value);
|
||||
|
||||
scheduler.schedule(() -> localCache.remove(key), ttl.toMillis(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private void putToRedisCache(String key, Object value, Duration ttl) {
|
||||
try {
|
||||
redisTemplate.opsForValue().set(key, value, ttl);
|
||||
} catch (Exception e) {
|
||||
log.warn("Error putting to Redis cache: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Write-Through Pattern
|
||||
```java
|
||||
// Write-Through реализация
|
||||
@Service
|
||||
public class WriteThroughUserService {
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, User> redisTemplate;
|
||||
|
||||
public void createUser(User user) {
|
||||
// Сохраняем в БД
|
||||
user = userRepository.save(user);
|
||||
|
||||
// Сохраняем в кэш
|
||||
String cacheKey = "user:" + user.getId();
|
||||
redisTemplate.opsForValue().set(cacheKey, user, Duration.ofMinutes(30));
|
||||
}
|
||||
|
||||
public void updateUser(User user) {
|
||||
// Обновляем БД
|
||||
user = userRepository.save(user);
|
||||
|
||||
// Обновляем кэш
|
||||
String cacheKey = "user:" + user.getId();
|
||||
redisTemplate.opsForValue().set(cacheKey, user, Duration.ofMinutes(30));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Write-Behind Pattern
|
||||
```java
|
||||
// Write-Behind реализация
|
||||
@Service
|
||||
public class WriteBehindUserService {
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, User> redisTemplate;
|
||||
|
||||
@Async
|
||||
public void createUser(User user) {
|
||||
// Сохраняем в кэш сразу
|
||||
String cacheKey = "user:" + user.getId();
|
||||
redisTemplate.opsForValue().set(cacheKey, user, Duration.ofMinutes(30));
|
||||
|
||||
// Сохраняем в БД асинхронно
|
||||
CompletableFuture.runAsync(() -> {
|
||||
userRepository.save(user);
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Многоуровневое кэширование
|
||||
|
||||
### 1. L1 Cache (Application Level)
|
||||
```java
|
||||
// L1 кэш (в памяти приложения)
|
||||
@Service
|
||||
public class L1CacheUserService {
|
||||
private final Map<Long, User> localCache = new ConcurrentHashMap<>();
|
||||
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
public User getUser(Long id) {
|
||||
return localCache.computeIfAbsent(id, key -> {
|
||||
User user = userRepository.findById(key);
|
||||
if (user != null) {
|
||||
// Автоматическое удаление через 5 минут
|
||||
scheduler.schedule(() -> localCache.remove(key), 5, TimeUnit.MINUTES);
|
||||
}
|
||||
return user;
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. L2 Cache (Distributed Cache)
|
||||
```java
|
||||
// L2 кэш (распределенный)
|
||||
@Service
|
||||
public class L2CacheUserService {
|
||||
@Autowired
|
||||
private RedisTemplate<String, User> redisTemplate;
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
public User getUser(Long id) {
|
||||
String cacheKey = "user:" + id;
|
||||
|
||||
User user = redisTemplate.opsForValue().get(cacheKey);
|
||||
if (user == null) {
|
||||
user = userRepository.findById(id);
|
||||
if (user != null) {
|
||||
redisTemplate.opsForValue().set(cacheKey, user, Duration.ofMinutes(30));
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in new issue
Block a user