Caching

01.07.2026 | Muallif: Jaxongir a.k.a | Kategoriya: Spring Boot | 32 daqiqa o'qish

Caching bo‘limi quyidagilarni qamrab oladi: @EnableCaching, @Cacheable, @CacheEvict, @CachePut, CacheManager, Caffeine, Redis, cache key strategy, TTL, cache-aside, va write-through.


1. Caching nima?

Caching - tez-tez kerak bo‘ladigan ma’lumotni sekin joydan emas, tez joydan olish.

Masalan:

Client -> API -> Database

Agar har safar databasega borsak:

  • latency oshadi;

  • databasega yuk tushadi;

  • bir xil query qayta-qayta ishlaydi.

Cache bilan:

Client -> API -> Cache
              ↓
           Database

Agar data cache’da bo‘lsa, databasega bormaymiz.


2. Oddiy misol

Tasavvur qiling, productni ID bo‘yicha olyapmiz:

@Service
public class ProductService {

    public Product getById(Long id) {
        return productRepository.findById(id)
                .orElseThrow();
    }
}

Agar id = 10 product ko‘p chaqirilsa, har safar DBga query ketadi.

Cache qo‘shsak:

@Cacheable(value = "products", key = "#id")
public Product getById(Long id) {
    return productRepository.findById(id)
            .orElseThrow();
}

Endi birinchi chaqiruvda DBga boradi, keyingi chaqiruvlarda cache’dan oladi.


3. Spring Cache Abstraction

Spring cachingni bitta umumiy abstraction orqali beradi.

Siz kodda shuni yozasiz:

@Cacheable("products")

Lekin ichkarida cache provider boshqacha bo‘lishi mumkin:

Provider

Qayerda ishlaydi?

ConcurrentMap

Oddiy local memory

Caffeine

Tez local cache

Redis

Distributed cache

Ehcache

Local/JVM cache

Hazelcast

Distributed in-memory cache

Siz annotationlarni deyarli o‘zgartirmaysiz, faqat cache provider almashadi.


4. Cachingni yoqish

Spring Boot’da caching ishlashi uchun:

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

Dependency odatda:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

5. @Cacheable

@Cacheable - method natijasini cachega saqlaydi.

@Cacheable(value = "users", key = "#id")
public UserDto getUser(Long id) {
    System.out.println("DBga bordi");
    return userRepository.findById(id)
            .map(userMapper::toDto)
            .orElseThrow();
}

Birinchi chaqiruv:

getUser(1)
DBga bordi
cachega saqlandi

Ikkinchi chaqiruv:

getUser(1)
cache’dan qaytdi
method ichiga kirmadi

Muhim joy:

@Cacheable cache hit bo‘lsa, method tanasini umuman ishlatmaydi.


6. value va key

@Cacheable(value = "users", key = "#id")

Bu yerda:

Qism

Ma’nosi

value = "users"

Cache nomi

key = "#id"

Cache key

Masalan:

cache name: users
key: 1
value: UserDto(id=1, name="Ali")

7. Cache key qanday tanlanadi?

Agar methodda bitta parametr bo‘lsa:

@Cacheable("users")
public UserDto getUser(Long id) {
}

Spring default key sifatida idni ishlatadi.

Lekin aniq yozish yaxshi:

@Cacheable(value = "users", key = "#id")
public UserDto getUser(Long id) {
}

Agar bir nechta parametr bo‘lsa:

@Cacheable(value = "users", key = "#status + ':' + #page")
public Page<UserDto> getUsers(UserStatus status, int page) {
}

Yaxshiroq:

@Cacheable(
        value = "users",
        key = "T(String).format('%s:%s:%s', #status, #page, #size)"
)
public Page<UserDto> getUsers(UserStatus status, int page, int size) {
}

Lekin murakkab keylar uchun alohida KeyGenerator yozish yaxshiroq.


8. @CachePut

@CachePut methodni har doim ishlatadi va natijani cachega yozadi.

@CachePut(value = "users", key = "#id")
public UserDto updateUser(Long id, UpdateUserRequest request) {
    User user = userRepository.findById(id).orElseThrow();

    user.setName(request.name());

    return userMapper.toDto(userRepository.save(user));
}

Farqi:

Annotation

Method ishlaydimi?

Cachega yozadimi?

@Cacheable

Cache miss bo‘lsa

Ha

@CachePut

Har doim

Ha

@CachePut odatda update methodlarda ishlatiladi.


9. @CacheEvict

@CacheEvict - cache’dan ma’lumotni o‘chiradi.

Masalan user delete qilinsa:

@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) {
    userRepository.deleteById(id);
}

Endi users::1 cache’dan o‘chadi.

Update qilganda ham ishlatish mumkin:

@CacheEvict(value = "users", key = "#id")
public UserDto updateUser(Long id, UpdateUserRequest request) {
    User user = userRepository.findById(id).orElseThrow();
    user.setName(request.name());
    userRepository.save(user);

    return userMapper.toDto(user);
}

Bu holatda keyingi getUser(id) DBdan yangisini olib, cachega qayta yozadi.


10. allEntries = true

Ba’zan bitta key emas, butun cache tozalanadi.

@CacheEvict(value = "products", allEntries = true)
public ProductDto createProduct(CreateProductRequest request) {
    Product product = productRepository.save(mapper.toEntity(request));
    return mapper.toDto(product);
}

Bu list cachelar uchun foydali bo‘lishi mumkin.

Masalan:

@Cacheable(value = "productLists", key = "#category")
public List<ProductDto> getByCategory(String category) {
}

Agar yangi product qo‘shilsa, category listlar eskirib qolishi mumkin. Shunda allEntries = true ishlatish mumkin.

Lekin ehtiyot bo‘ling:

allEntries = true ko‘p ishlatilsa cache foydasi kamayadi.


11. condition va unless

condition

Method cache ishlatishdan oldin tekshiriladi.

@Cacheable(
        value = "users",
        key = "#id",
        condition = "#id > 0"
)
public UserDto getUser(Long id) {
}

Agar id <= 0 bo‘lsa, cache ishlamaydi.

unless

Method ishlagandan keyin natijaga qarab cache qilmaslik.

@Cacheable(
        value = "users",
        key = "#id",
        unless = "#result == null"
)
public UserDto getUser(Long id) {
}

Agar result null bo‘lsa, cachega yozilmaydi.

Yana bir misol:

@Cacheable(
        value = "users",
        key = "#id",
        unless = "#result.status == 'BLOCKED'"
)
public UserDto getUser(Long id) {
}

12. @Caching

Bir nechta cache operatsiyani birga ishlatish uchun:

@Caching(
        put = {
                @CachePut(value = "users", key = "#result.id")
        },
        evict = {
                @CacheEvict(value = "userLists", allEntries = true)
        }
)
public UserDto updateUser(Long id, UpdateUserRequest request) {
    User user = userRepository.findById(id).orElseThrow();
    user.setName(request.name());

    return userMapper.toDto(userRepository.save(user));
}

Bu yerda:

  • bitta user cache yangilanadi;

  • user list cache tozalanadi.


13. CacheManager nima?

CacheManager - cachelarni boshqaruvchi Spring abstraction.

Application
   ↓
CacheManager
   ↓
Cache provider: Caffeine / Redis / Ehcache / ...

Masalan:

@Autowired
private CacheManager cacheManager;

Manual ishlatish:

Cache cache = cacheManager.getCache("users");

if (cache != null) {
    cache.evict(1L);
}

Lekin odatda annotationlar yetarli.


14. Local cache vs distributed cache

Local cache

Har bir application instance o‘z memorysida cache saqlaydi.

App instance 1 -> cache A
App instance 2 -> cache B
App instance 3 -> cache C

Misol: Caffeine.

Afzalliklari:

  • juda tez;

  • network yo‘q;

  • setup oson.

Kamchiligi:

  • har instance cache alohida;

  • data inconsistent bo‘lishi mumkin;

  • restart bo‘lsa cache yo‘qoladi.


Distributed cache

Bitta umumiy cache server ishlatiladi.

App instance 1 ┐
App instance 2 ├── Redis
App instance 3 ┘

Misol: Redis.

Afzalliklari:

  • barcha instance bitta cache ko‘radi;

  • horizontal scalingda yaxshi;

  • TTL va eviction kuchli;

  • session/cache/share data uchun qulay.

Kamchiligi:

  • network latency bor;

  • Redis down bo‘lsa ta’sir qiladi;

  • serialization kerak;

  • infra murakkabroq.


15. Caffeine local cache

Caffeine - JVM ichida ishlaydigan juda tez local cache.

Dependency:

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>

Config:

spring:
  cache:
    type: caffeine
    cache-names:
      - users
      - products
    caffeine:
      spec: maximumSize=10000,expireAfterWrite=10m

Bu nimani bildiradi?

maximumSize=10000        -> 10 mingtagacha entry
expireAfterWrite=10m     -> yozilgandan 10 minut keyin o‘chadi

16. Caffeine qachon yaxshi?

Caffeine yaxshi:

Holat

Sabab

Reference data

Masalan region, category, settings

Kam o‘zgaradigan data

Cache invalidation oson

Single instance app

Distributed cache shart emas

Juda tez response kerak

Memorydan o‘qiydi

DB yukini kamaytirish

Tez-tez o‘qiladigan datalar

Misol:

@Cacheable(value = "regions")
public List<RegionDto> getRegions() {
    return regionRepository.findAll()
            .stream()
            .map(mapper::toDto)
            .toList();
}

17. Redis cache

Redis - distributed cache uchun eng ko‘p ishlatiladigan variantlardan biri.

Dependency:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

Config:

spring:
  cache:
    type: redis
  data:
    redis:
      host: localhost
      port: 6379

TTL config:

@Configuration
public class RedisCacheConfig {

    @Bean
    public RedisCacheManagerBuilderCustomizer redisCacheCustomizer() {
        return builder -> builder
                .withCacheConfiguration(
                        "users",
                        RedisCacheConfiguration.defaultCacheConfig()
                                .entryTtl(Duration.ofMinutes(10))
                )
                .withCacheConfiguration(
                        "products",
                        RedisCacheConfiguration.defaultCacheConfig()
                                .entryTtl(Duration.ofMinutes(30))
                );
    }
}

Bu yerda:

users cache    -> 10 minut TTL
products cache -> 30 minut TTL

18. Redisda serialization

Redisga Java obyektini saqlash uchun serialization kerak.

Default serialization ba’zan noqulay bo‘lishi mumkin. JSON ishlatish yaxshiroq:

@Configuration
public class RedisConfig {

    @Bean
    public RedisCacheConfiguration redisCacheConfiguration() {
        ObjectMapper objectMapper = new ObjectMapper()
                .findAndRegisterModules();

        GenericJackson2JsonRedisSerializer serializer =
                new GenericJackson2JsonRedisSerializer(objectMapper);

        return RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(10))
                .serializeValuesWith(
                        RedisSerializationContext.SerializationPair
                                .fromSerializer(serializer)
                );
    }
}

Bu productionda ko‘p kerak bo‘ladi.


19. TTL nima?

TTL - Time To Live.

Ya’ni cache entry qancha vaqt yashaydi.

users::1 -> 10 minutdan keyin avtomatik o‘chadi

TTL nega kerak?

  • stale data uzoq qolmasligi uchun;

  • memory to‘lib ketmasligi uchun;

  • cache invalidationni yengillashtirish uchun.


20. TTL tanlash

Data turi

Tavsiya TTL

Region/category/reference data

1 soat - 24 soat

Product detail

5 - 30 minut

User profile

1 - 10 minut

Exchange rate

Manbaga bog‘liq

Permission/role

Qisqa yoki event-based invalidation

Search/list result

30 sekund - 5 minut

Qoida:

Data qanchalik tez o‘zgarsa, TTL shunchalik qisqa bo‘lishi kerak.


21. Cache key strategy

Yomon key:

@Cacheable(value = "users", key = "#name")
public List<UserDto> search(String name, String city, UserStatus status) {
}

Bu noto‘g‘ri, chunki city va status hisobga olinmayapti.

To‘g‘riroq:

@Cacheable(
        value = "userSearch",
        key = "#name + ':' + #city + ':' + #status"
)
public List<UserDto> search(String name, String city, UserStatus status) {
}

Yana yaxshiroq - request object asosida key:

@Cacheable(value = "userSearch", key = "#request.cacheKey()")
public Page<UserDto> search(UserSearchRequest request, Pageable pageable) {
}

Request:

public record UserSearchRequest(
        String name,
        String city,
        UserStatus status
) {
    public String cacheKey() {
        return "%s:%s:%s".formatted(name, city, status);
    }
}

Lekin pagination bo‘lsa, page, size, sort ham keyga kirishi kerak.


22. Pageable bilan cache key

@Cacheable(
        value = "userSearch",
        key = "#request.cacheKey() + ':' + #pageable.pageNumber + ':' + #pageable.pageSize + ':' + #pageable.sort"
)
public Page<UserDto> search(UserSearchRequest request, Pageable pageable) {
}

Aks holda:

page=0 cachega tushadi
page=1 chaqiriladi
lekin page=0 natijasi qaytib qolishi mumkin

Bu jiddiy bug.


23. Cache invalidation

Cachingdagi eng qiyin narsa - cachega yozish emas.

Eng qiyini:

Cache’dagi eski datani qachon o‘chirish?

Masalan:

@Cacheable(value = "products", key = "#id")
public ProductDto getProduct(Long id) {
}

Product update bo‘lsa:

public ProductDto updateProduct(Long id, UpdateProductRequest request) {
}

Agar cache tozalanmasa, user eski productni ko‘raveradi.

To‘g‘ri:

@CacheEvict(value = "products", key = "#id")
public ProductDto updateProduct(Long id, UpdateProductRequest request) {
    Product product = productRepository.findById(id).orElseThrow();
    product.setName(request.name());

    return mapper.toDto(productRepository.save(product));
}

24. List cache invalidation

Detail cache oson:

products::10

Lekin list cache qiyin:

productList::category=PHONE&page=0
productList::category=PHONE&page=1
productList::category=LAPTOP&page=0

Agar product update bo‘lsa, qaysi listlarni tozalash kerak?

Oddiy yechim:

@Caching(evict = {
        @CacheEvict(value = "products", key = "#id"),
        @CacheEvict(value = "productLists", allEntries = true)
})
public ProductDto updateProduct(Long id, UpdateProductRequest request) {
}

Bu xavfsiz, lekin qo‘pol yechim.

Katta sistemalarda:

  • qisqa TTL;

  • event-based invalidation;

  • cache versioning;

  • Redis key pattern bilan delete;

  • list cache qilmaslik;

ishlatiladi.


25. Cache-aside pattern

Eng ko‘p ishlatiladigan pattern.

1. App cache’dan o‘qiydi
2. Cache miss bo‘lsa DBdan o‘qiydi
3. Natijani cachega yozadi
4. Keyingi safar cache’dan qaytaradi

@Cacheable aynan shunga o‘xshaydi.

@Cacheable(value = "products", key = "#id")
public ProductDto getProduct(Long id) {
    return productRepository.findById(id)
            .map(mapper::toDto)
            .orElseThrow();
}

Oqim:

getProduct(10)
  cache miss
  DB query
  cache put

getProduct(10)
  cache hit
  DBga bormaydi

26. Write-through pattern

Write-through - data yozilganda DB bilan birga cache ham yangilanadi.

@CachePut(value = "products", key = "#result.id")
public ProductDto updateProduct(Long id, UpdateProductRequest request) {
    Product product = productRepository.findById(id).orElseThrow();
    product.setName(request.name());

    Product saved = productRepository.save(product);
    return mapper.toDto(saved);
}

Oqim:

Update request
  DB update
  Cache update

Afzalligi:

  • update bo‘lgandan keyin cache yangi bo‘ladi.

Kamchiligi:

  • yozish operatsiyasi murakkablashadi;

  • list cachelarni baribir invalidatsiya qilish kerak;

  • cache update xatosini boshqarish kerak.


27. Cache-aside vs write-through

Pattern

O‘qish

Yozish

Qachon yaxshi

Cache-aside

Miss bo‘lsa DB

Update’da evict

Ko‘p app uchun default

Write-through

Cache doim yangiroq

DB + cache update

Detail cache uchun yaxshi

Write-behind

Avval cache, keyin DB

Async DB write

Juda ehtiyotkorlik kerak

Ko‘p Spring Boot loyihalarda eng oddiy va xavfsiz variant:

Read: @Cacheable
Update/Delete: @CacheEvict

28. @Transactional va cache

Bu juda muhim.

@Transactional
@CacheEvict(value = "products", key = "#id")
public void updateProduct(Long id) {
    Product product = productRepository.findById(id).orElseThrow();
    product.setName("New name");

    throw new RuntimeException("DB rollback");
}

Savol:

Cache qachon o‘chadi? Transaction rollback bo‘lsa nima bo‘ladi?

Spring Cache annotationlari transaction bilan avtomatik ideal integratsiyada deb o‘ylash xavfli. Ba’zi holatlarda cache DB commitdan oldin o‘zgarishi mumkin.

Agar aniq commitdan keyin cache tozalash kerak bo‘lsa, event ishlatish yaxshi:

@Transactional
public void updateProduct(Long id, UpdateProductRequest request) {
    Product product = productRepository.findById(id).orElseThrow();
    product.setName(request.name());

    eventPublisher.publishEvent(new ProductUpdatedEvent(id));
}
@Component
public class ProductCacheInvalidator {

    @CacheEvict(value = "products", key = "#event.productId")
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void onProductUpdated(ProductUpdatedEvent event) {
        // cache evicted after commit
    }
}

Bu usul DB commit bo‘lgandan keyin cache tozalanishini kafolatlashga yaqinroq.


29. Self-invocation cachingda ham muammo

AOP mavzusidagi muammo cachingda ham bor.

Yomon:

@Service
public class ProductService {

    public ProductDto getProductWrapper(Long id) {
        return getProduct(id);
    }

    @Cacheable(value = "products", key = "#id")
    public ProductDto getProduct(Long id) {
        return productRepository.findById(id)
                .map(mapper::toDto)
                .orElseThrow();
    }
}

getProductWrapper() ichidan getProduct() chaqirilsa, cache ishlamasligi mumkin.

Sabab:

this.getProduct(id)

proxy orqali o‘tmaydi.

Yechim:

  • methodni boshqa servicega chiqarish;

  • tashqaridan proxy orqali chaqirish;

  • self-injection ishlatish, lekin dizayn jihatdan uncha toza emas.


30. Private methodda caching ishlamaydi

Yomon:

@Cacheable("products")
private ProductDto getProduct(Long id) {
}

Spring Cache ham AOP/proxyga tayanadi. Private method intercept qilinmaydi.

To‘g‘ri:

@Cacheable("products")
public ProductDto getProduct(Long id) {
}

31. Cache stampede muammosi

Tasavvur qiling cache expire bo‘ldi.

1000 ta request bir vaqtda getProduct(10) chaqirdi
cache miss
hammasi DBga ketdi

Bu cache stampede yoki thundering herd deyiladi.

Yechimlar:

  • sync = true;

  • lock;

  • TTLga jitter qo‘shish;

  • background refresh;

  • Redis distributed lock;

  • hot keylarni alohida boshqarish.

Spring’da:

@Cacheable(value = "products", key = "#id", sync = true)
public ProductDto getProduct(Long id) {
    return productRepository.findById(id)
            .map(mapper::toDto)
            .orElseThrow();
}

sync = true bir key bo‘yicha bir vaqtda faqat bitta thread DBga borishiga yordam beradi. Lekin hamma providerlarda bir xil ishlamasligi mumkin.


32. Negative caching

Agar data topilmasa, har safar DBga borish ham yomon.

Masalan:

GET /products/999999

Bunday product yo‘q. Lekin bot yoki client qayta-qayta so‘rasa, DBga yuk tushadi.

Negative caching - "yo‘q" natijani ham qisqa muddat cache qilish.

Masalan result Optional.empty() yoki maxsus DTO bilan.

Lekin ehtiyot bo‘ling:

Product hozir yo‘q
5 sekunddan keyin yaratilishi mumkin

Shuning uchun negative cache TTL qisqa bo‘lishi kerak.


33. Nimalarni cache qilish kerak?

Cache qilish yaxshi:

Data

Sabab

Reference data

Kam o‘zgaradi

Product detail

Ko‘p o‘qiladi

Category list

Kam o‘zgaradi

Permissions

Tez-tez tekshiriladi

Settings/config

Ko‘p ishlatiladi

Expensive calculation

Hisoblash qimmat

External API response

Sekin va limitli bo‘lishi mumkin

Cache qilish xavfli:

Data

Sabab

Tez o‘zgaradigan balance

Stale data xavfli

Payment status

Xatoga narxi baland

Personal sensitive data

Security risk

Juda katta objectlar

Memory bosimi

Noto‘g‘ri invalidatsiya qilinadigan listlar

Eski data qaytadi

Transaction ichidagi vaqtinchalik data

Consistency muammo


34. Cache va security

User-specific data cache qilinsa, keyga user ID kiritilishi shart.

Yomon:

@Cacheable(value = "profile", key = "'current'")
public UserProfileDto getCurrentProfile() {
}

Bu hamma userga bitta cache qaytarishi mumkin.

To‘g‘ri:

@Cacheable(value = "profile", key = "#userId")
public UserProfileDto getProfile(Long userId) {
}

Role/permission cache qilganda ham:

@Cacheable(value = "permissions", key = "#userId")
public Set<String> getPermissions(Long userId) {
}

Agar user role o‘zgarsa:

@CacheEvict(value = "permissions", key = "#userId")
public void changeUserRole(Long userId, Long roleId) {
}

35. Cache monitoring

Productionda cache qo‘shib qo‘yish yetarli emas. Monitoring kerak.

Ko‘rish kerak bo‘lgan metriclar:

Metric

Ma’nosi

Hit rate

Cache’dan qaytgan so‘rovlar foizi

Miss rate

DBga ketgan so‘rovlar foizi

Eviction count

Cache’dan chiqarilgan entrylar

Cache size

Hozirgi entrylar soni

Latency

Cache access va DB access vaqti

Redis memory

Redis RAM ishlatilishi

Redis ops/sec

Redis request soni

Agar hit rate past bo‘lsa, caching foydasi kam.


36. Spring Boot Actuator bilan cache metric

Agar Actuator va Micrometer ishlatilsa, cache metriclar chiqishi mumkin.

Dependency:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Endpoint:

/actuator/metrics

Cache metriclar:

cache.gets
cache.puts
cache.evictions
cache.size

Providerga qarab metriclar farq qilishi mumkin.


37. Real project pattern: Product cache

Service

@Service
public class ProductService {

    private final ProductRepository productRepository;
    private final ProductMapper mapper;

    public ProductService(
            ProductRepository productRepository,
            ProductMapper mapper
    ) {
        this.productRepository = productRepository;
        this.mapper = mapper;
    }

    @Cacheable(value = "products", key = "#id")
    @Transactional(readOnly = true)
    public ProductDto getById(Long id) {
        return productRepository.findById(id)
                .map(mapper::toDto)
                .orElseThrow(() -> new ProductNotFoundException(id));
    }

    @Caching(evict = {
            @CacheEvict(value = "products", key = "#id"),
            @CacheEvict(value = "productLists", allEntries = true)
    })
    @Transactional
    public ProductDto update(Long id, UpdateProductRequest request) {
        Product product = productRepository.findById(id)
                .orElseThrow(() -> new ProductNotFoundException(id));

        product.setName(request.name());
        product.setPrice(request.price());

        return mapper.toDto(product);
    }

    @CacheEvict(value = "productLists", allEntries = true)
    @Transactional
    public ProductDto create(CreateProductRequest request) {
        Product product = mapper.toEntity(request);
        Product saved = productRepository.save(product);

        return mapper.toDto(saved);
    }
}

Bu pattern:

getById -> cache read
update -> detail cache evict + list cache evict
create -> list cache evict

38. Real project pattern: Reference data cache

Reference datalar uchun caching juda yaxshi.

@Service
public class RegionService {

    private final RegionRepository regionRepository;

    @Cacheable(value = "regions", key = "'all'")
    @Transactional(readOnly = true)
    public List<RegionDto> getAll() {
        return regionRepository.findAll()
                .stream()
                .map(RegionDto::from)
                .toList();
    }

    @CacheEvict(value = "regions", key = "'all'")
    @Transactional
    public RegionDto create(CreateRegionRequest request) {
        Region region = regionRepository.save(new Region(request.name()));
        return RegionDto.from(region);
    }
}

Bu yerda key oddiy:

regions::all

Chunki butun region list bitta cache entry sifatida saqlanyapti.


39. Cache bilan eng ko‘p qilinadigan xatolar

Xato

Oqibat

Key noto‘g‘ri tanlangan

Boshqa requestga noto‘g‘ri data qaytadi

Invalidation yo‘q

Eski data qaytadi

User-specific key yo‘q

Bir user boshqa user datasini ko‘rishi mumkin

Juda uzun TTL

Stale data uzoq qoladi

Juda qisqa TTL

Cache foydasi kamayadi

Katta object cache

Memory bosimi

List cache haddan tashqari ko‘p

Invalidation murakkablashadi

Transaction bilan noaniq ishlash

DB rollback, cache o‘zgarib qolishi mumkin

Self-invocation

Cache annotation ishlamaydi

Cache monitoring yo‘q

Foyda yoki zarar noma’lum


40. Interview savollar

Savol 1: Spring Cache qanday ishlaydi?

Javob:

Spring Cache annotationlar orqali method natijasini cachega saqlaydi yoki cache’dan o‘qiydi. U CacheManager abstractioniga tayanadi. Provider sifatida Caffeine, Redis, Ehcache kabi texnologiyalar ishlatilishi mumkin.


Savol 2: @Cacheable, @CachePut, @CacheEvict farqi nima?

Javob:

@Cacheable cache’da data bo‘lsa methodni ishlatmaydi, yo‘q bo‘lsa methodni ishlatib resultni cachega yozadi. @CachePut methodni har doim ishlatadi va resultni cachega yozadi. @CacheEvict cache’dagi entryni o‘chiradi.


Savol 3: Local cache va distributed cache farqi nima?

Javob:

Local cache har bir app instance ichida alohida saqlanadi, masalan Caffeine. Juda tez, lekin instance’lar orasida umumiy emas. Distributed cache esa Redis kabi alohida serverda bo‘ladi va barcha instance’lar bir xil cache’dan foydalanadi.


Savol 4: Cache invalidation nima?

Javob:

Cache invalidation - data o‘zgarganda cache’dagi eski entryni o‘chirish yoki yangilash jarayoni. Cachingdagi eng qiyin muammolardan biri, chunki noto‘g‘ri invalidation eski yoki noto‘g‘ri data qaytaradi.


Savol 5: Cache-aside pattern nima?

Javob:

Cache-aside’da application avval cache’dan o‘qiydi. Cache miss bo‘lsa DBdan o‘qiydi va natijani cachega yozadi. Spring’dagi @Cacheable shu patternga juda o‘xshaydi.


Savol 6: Cache key strategy nima uchun muhim?

Javob:

Cache key method natijasini identifikatsiya qiladi. Agar keyga barcha muhim parametrlar kiritilmasa, boshqa requestga noto‘g‘ri data qaytishi mumkin. Masalan paginationda page, size, sort ham keyga kirishi kerak.


Savol 7: Redis qachon kerak?

Javob:

Redis ko‘p instance’li applicationlarda, distributed cache kerak bo‘lganda, cache barcha serverlar orasida umumiy bo‘lishi kerak bo‘lganda ishlatiladi. Masalan microservice, Kubernetes, load balancer ortidagi bir nechta app instance.


41. Qisqa xulosa

Caching performance uchun kuchli qurol, lekin noto‘g‘ri ishlatilsa xavfli.

Mavzu

Asosiy fikr

@Cacheable

O‘qish natijasini cache qiladi

@CachePut

Update natijasini cachega yozadi

@CacheEvict

Eski cache entryni o‘chiradi

CacheManager

Cache providerlarni boshqaradi

Caffeine

Tez local cache

Redis

Distributed cache

TTL

Entry qancha yashashini belgilaydi

Key strategy

To‘g‘ri data qaytishi uchun muhim

Cache-aside

Eng ko‘p ishlatiladigan pattern

Write-through

Write paytida cache ham yangilanadi

Invalidation

Cachingdagi eng qiyin qism

Eng muhim gap:

Cache - database o‘rnini bosmaydi. Cache faqat tez-tez o‘qiladigan, nisbatan barqaror va noto‘g‘ri eskirib qolsa katta zarar bermaydigan datalar uchun ishlatiladi.