1. 비즈니스 시나리오
다중 스레드 동시성의 경우 데이터베이스와 Redis 간의 데이터 일관성을 보장하기 위해 두 개의 데이터베이스 수정 요청이 있다고 가정합니다.
수정 요청 구현에는 계단식이 필요합니다. Redis의 데이터 수정 후 수정.
요청 1: A는 데이터베이스 데이터를 수정합니다. B는 Redis 데이터를 수정합니다.
요청 2: C는 데이터베이스 데이터를 수정합니다. D는 Redis 데이터를 수정합니다.
동시 상황에서는 A —>C —> ;> B
(스레드에서 동시에 실행되는 여러 세트의 원자 연산의 실행 순서가 겹칠 수 있음을 이해하십시오.)
1. 이때 문제
A가 데이터베이스 데이터를 수정하여 최종적으로 Redis에 저장했습니다. C도 A 데이터 이후에 데이터베이스를 수정했습니다.
이때, Redis의 데이터와 데이터베이스 데이터 사이에 불일치가 발생하여 후속 쿼리 과정에서 Redis를 오랜 시간 동안 먼저 확인하게 되어 쿼리된 데이터가 실제 데이터가 아닌 심각한 문제가 발생하게 됩니다. 데이터베이스에서.
2. 솔루션
Redis를 사용할 때 가장 인기 있는 솔루션 중 하나는 지연 이중 삭제 전략입니다.
참고: 이중 삭제 전략의 결과로 Redis에 저장된 데이터가 삭제되고 후속 쿼리가 데이터베이스에 쿼리되므로 자주 수정되는 데이터 테이블은 Redis 사용에 적합하지 않다는 점을 알아야 합니다. 따라서 Redis는 변경 사항보다 훨씬 더 많은 것을 읽는 데이터 캐시를 사용합니다.
지연된 이중 삭제 계획의 실행 단계
두 번째 Redis 삭제 전에 데이터베이스 업데이트 작업을 완료해야 합니다. 세 번째 단계가 없다면 두 번의 Redis 삭제 작업이 완료된 후에도 데이터베이스의 데이터가 업데이트되지 않았을 가능성이 높다고 상상해보십시오. 이때 데이터에 액세스하라는 요청이 있으면 문제가 발생합니다. 처음에 언급한 질문이 나옵니다. 4. 캐시를 두 번 삭제해야 하는 이유는 무엇인가요? 두 번째 삭제 작업이 없고 이때 데이터에 액세스하라는 요청이 있는 경우 이전에 수정되지 않은 Redis 데이터일 수 있습니다. 삭제 작업이 수행된 후 Redis는 비어 있게 됩니다. 요청이 들어오면 데이터베이스에 액세스하게 됩니다. 이때 데이터베이스의 데이터는 이미 업데이트된 데이터이므로 데이터 일관성이 보장됩니다. 2. 코드 연습1. Redis 및 SpringBoot AOP 종속성 소개1> 캐시 삭제
2> 데이터베이스 업데이트
3> 지연 500밀리초(특정 비즈니스에 따라 지연 실행 시간 설정)
4> 지연 이유는 무엇입니까? 500밀리초?
<!-- redis使用 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2. 사용자 정의 AOP 주석 및 측면 작성ClearAndReloadCache 지연된 이중 삭제 주석/**
*延时双删
**/
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.METHOD)
public @interface ClearAndReloadCache {
String name() default "";
}
ClearAndReloadCacheAspect 지연된 측면 이중 삭제@Aspect @Component public class ClearAndReloadCacheAspect { @Autowired private StringRedisTemplate stringRedisTemplate; /** * 切入点 *切入点,基于注解实现的切入点 加上该注解的都是Aop切面的切入点 * */ @Pointcut("@annotation(com.pdh.cache.ClearAndReloadCache)") public void pointCut(){ } /** * 环绕通知 * 环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。 * 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型 * @param proceedingJoinPoint */ @Around("pointCut()") public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint){ System.out.println("----------- 环绕通知 -----------"); System.out.println("环绕通知的目标方法名:" + proceedingJoinPoint.getSignature().getName()); Signature signature1 = proceedingJoinPoint.getSignature(); MethodSignature methodSignature = (MethodSignature)signature1; Method targetMethod = methodSignature.getMethod();//方法对象 ClearAndReloadCache annotation = targetMethod.getAnnotation(ClearAndReloadCache.class);//反射得到自定义注解的方法对象 String name = annotation.name();//获取自定义注解的方法对象的参数即name Set<String> keys = stringRedisTemplate.keys("*" + name + "*");//模糊定义key stringRedisTemplate.delete(keys);//模糊删除redis的key值 //执行加入双删注解的改动数据库的业务 即controller中的方法业务 Object proceed = null; try { proceed = proceedingJoinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } //开一个线程 延迟1秒(此处是1秒举例,可以改成自己的业务) // 在线程中延迟删除 同时将业务代码的结果返回 这样不影响业务代码的执行 new Thread(() -> { try { Thread.sleep(1000); Set<String> keys1 = stringRedisTemplate.keys("*" + name + "*");//模糊删除 stringRedisTemplate.delete(keys1); System.out.println("-----------1秒钟后,在线程中延迟删除完毕 -----------"); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); return proceed;//返回业务代码的值 } }3. yml
server: port: 8082 spring: # redis setting redis: host: localhost port: 6379 # cache setting cache: redis: time-to-live: 60000 # 60s datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/test username: root password: 1234 # mp setting mybatis-plus: mapper-locations: classpath*:com/pdh/mapper/*.xml global-config: db-config: table-prefix: configuration: # log of sql log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # hump map-underscore-to-camel-case: true4, user_db.sql script테스트 데이터 생성에 사용됨
DROP TABLE IF EXISTS `user_db`;
CREATE TABLE `user_db` (
`id` int(4) NOT NULL AUTO_INCREMENT,
`username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user_db
-- ----------------------------
INSERT INTO `user_db` VALUES (1, '张三');
INSERT INTO `user_db` VALUES (2, '李四');
INSERT INTO `user_db` VALUES (3, '王二');
INSERT INTO `user_db` VALUES (4, '麻子');
INSERT INTO `user_db` VALUES (5, '王三');
INSERT INTO `user_db` VALUES (6, '李三');
5, UserController/** * 用户控制层 */ @RequestMapping("/user") @RestController public class UserController { @Autowired private UserService userService; @GetMapping("/get/{id}") @Cache(name = "get method") //@Cacheable(cacheNames = {"get"}) public Result get(@PathVariable("id") Integer id){ return userService.get(id); } @PostMapping("/updateData") @ClearAndReloadCache(name = "get method") public Result updateData(@RequestBody User user){ return userService.update(user); } @PostMapping("/insert") public Result insert(@RequestBody User user){ return userService.insert(user); } @DeleteMapping("/delete/{id}") public Result delete(@PathVariable("id") Integer id){ return userService.delete(id); } }6, UserService
/**
* service层
*/
@Service
public class UserService {
@Resource
private UserMapper userMapper;
public Result get(Integer id){
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getId,id);
User user = userMapper.selectOne(wrapper);
return Result.success(user);
}
public Result insert(User user){
int line = userMapper.insert(user);
if(line > 0)
return Result.success(line);
return Result.fail(888,"操作数据库失败");
}
public Result delete(Integer id) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getId, id);
int line = userMapper.delete(wrapper);
if (line > 0)
return Result.success(line);
return Result.fail(888, "操作数据库失败");
}
public Result update(User user){
int i = userMapper.updateById(user);
if(i > 0)
return Result.success(i);
return Result.fail(888,"操作数据库失败");
}
}
3 테스트 검증1, ID=10, 새 데이터 추가
2 Redis에 저장중단점 만들기, 시뮬레이션 첫 번째 실행 스레드 삭제 후 A가 데이터베이스 업데이트를 완료하기 전에 다른 스레드 B가 ID=10에 액세스하여 이전 데이터를 읽습니다.
위 내용은 SpringBoot AOP Redis가 지연된 이중 삭제 기능을 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

Redis는 고성능과 유연성으로 인해 SQL 데이터베이스를 뛰어 넘습니다. 1) Redis는 메모리 스토리지를 통해 매우 빠른 읽기 및 쓰기 속도를 달성합니다. 2) 복잡한 데이터 처리에 적합한 목록 및 컬렉션과 같은 다양한 데이터 구조를 지원합니다. 3) 단일 스레드 모델은 개발을 단순화하지만 높은 동시성은 병목 현상이 될 수 있습니다.

Redis는 동시성이 높은 기존 데이터베이스보다 우수하고 대기 시간 시나리오가 낮지 만 복잡한 쿼리 및 트랜잭션 처리에는 적합하지 않습니다. 1.Redis는 메모리 저장, 빠른 읽기 및 쓰기 속도, 높은 동시성 및 낮은 대기 시간 요구 사항에 적합합니다. 2. 전통적인 데이터베이스는 디스크를 기반으로하며 복잡한 쿼리 및 트랜잭션 처리를 지원하며 데이터 일관성과 지속성이 강합니다. 3. Redis는 기존 데이터베이스의 보충 또는 대체물로 적합하지만 특정 비즈니스 요구에 따라 선택해야합니다.

redisisahigh-performancein-memorydatrscructurestorestorethexcelscelsspeedandversitility

Redis는 주로 데이터베이스이지만 단순한 데이터베이스 이상입니다. 1. 데이터베이스로서 Redis는 지속성을 지원하고 고성능 요구에 적합합니다. 2. 캐시로서 Redis는 응용 프로그램 응답 속도를 향상시킵니다. 3. 메시지 중개인으로서 Redis는 실시간 커뮤니케이션에 적합한 Publish-Subscribe 모드를 지원합니다.

redisiSamultifacetedToolthatservesAsadatabase, Server 및 more.ItfunctionsAnin-memoryDatrastRuctureStore, SupportSvariousDatastructures, andCanbeusedAsacache, MessageBroker, SessionStorage, 및 FordiptributedLocking을 지원합니다.

redisisanopen-source, in-memorydatructurestorestoreusedasadatabase, cache 및 messagebroker, excell

Redis는 데이터베이스, 캐시 및 메시지 중개인으로 사용되는 오픈 소스 메모리 데이터 구조 스토리지로 빠른 응답 및 높은 동시성이 필요한 시나리오에 적합합니다. 1.Redis는 메모리를 사용하여 데이터를 저장하고 마이크로 초 읽기 및 쓰기 속도를 제공합니다. 2. 문자열, 목록, 컬렉션 등과 같은 다양한 데이터 구조를 지원합니다. 3. Redis는 RDB 및 AOF 메커니즘을 통한 데이터 지속성을 인식합니다. 4. 단일 스레드 모델 및 멀티플렉싱 기술을 사용하여 요청을 효율적으로 처리하십시오. 5. 성능 최적화 전략에는 LRU 알고리즘 및 클러스터 모드가 포함됩니다.

Redis의 기능에는 주로 캐시, 세션 관리 및 기타 기능이 포함됩니다. 1) 캐시 함수는 메모리를 통해 데이터를 저장하여 읽기 속도를 향상시키고 전자 상거래 웹 사이트와 같은 고주파 액세스 시나리오에 적합합니다. 2) 세션 관리 기능은 분산 시스템에서 세션 데이터를 공유하고 만료 시간 메커니즘을 통해 자동으로 정리합니다. 3) 실시간 메시지 푸시 및 다중 스레드 시스템 및 기타 시나리오에 적합한 Publish-Subscribe 모드, 분산 잠금 및 카운터와 같은 기타 기능.


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

Eclipse용 SAP NetWeaver 서버 어댑터
Eclipse를 SAP NetWeaver 애플리케이션 서버와 통합합니다.

mPDF
mPDF는 UTF-8로 인코딩된 HTML에서 PDF 파일을 생성할 수 있는 PHP 라이브러리입니다. 원저자인 Ian Back은 자신의 웹 사이트에서 "즉시" PDF 파일을 출력하고 다양한 언어를 처리하기 위해 mPDF를 작성했습니다. HTML2FPDF와 같은 원본 스크립트보다 유니코드 글꼴을 사용할 때 속도가 느리고 더 큰 파일을 생성하지만 CSS 스타일 등을 지원하고 많은 개선 사항이 있습니다. RTL(아랍어, 히브리어), CJK(중국어, 일본어, 한국어)를 포함한 거의 모든 언어를 지원합니다. 중첩된 블록 수준 요소(예: P, DIV)를 지원합니다.

에디트플러스 중국어 크랙 버전
작은 크기, 구문 강조, 코드 프롬프트 기능을 지원하지 않음

SublimeText3 영어 버전
권장 사항: Win 버전, 코드 프롬프트 지원!

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경