(内容为自用,无序)
1.常用的Tcp网络你了解多少?
常用的TCP网络协议包括HTTP(80端口)、HTTPS(443端口)、FTP(21和20端口)、SMTP(25端口)、POP3(110端口)、IMAP(143端口)、SSH(22端口)和DNS(53端口)。
2.说说http和https的区别?
HTTP和HTTPS的主要区别在于安全性和数据传输的加密程度:
-
安全性:
-
HTTP:数据传输是明文的,不提供任何加密保护,容易被窃听和篡改。
-
HTTPS:通过SSL/TLS协议对通信数据进行加密,确保传输过程中的机密性和完整性,更安全。
-
-
加密方式:
-
HTTP:没有加密,数据以纯文本形式传输。
-
HTTPS:使用SSL(Secure Sockets Layer)或TLS(Transport Layer Security)协议加密数据,保证信息在传输过程中不被窃取或篡改。
-
-
默认端口:
-
HTTP:默认使用端口80。
-
HTTPS:默认使用端口443。
-
-
信任与SEO:
-
HTTPS:被视为更加安全和可信的协议,对于网站安全性和搜索引擎优化(SEO)有正面影响。
-
总之,HTTPS相比于HTTP提供了更高的安全性和数据保护,适合处理敏感信息和涉及用户账号登录的网站。
3.tcp的三次握手和四次握手你了解吗?
TCP的三次握手用于建立连接,首先客户端发送SYN报文给服务器,服务器回复确认收到的SYN,并发送自己的SYN和确认信息,最后客户端发送确认收到服务器的SYN和确认信息。四次握手则用于终止连接,客户端发送FIN报文告知服务器即将关闭连接,服务器回复确认收到的FIN,然后发送自己的FIN给客户端,最后客户端回复确认收到服务器的FIN。这些过程确保了数据的可靠传输和连接的安全关闭。
4.数据库的三大范式你了解多少?
-
第一范式(1NF):
-
确保每列具有原子性,即每个字段都不可再分。表中的每个单元格只包含一个值,不允许多个值或者是复杂的数据结构。
-
-
第二范式(2NF):
-
在满足第一范式的基础上,要求非主键属性必须完全依赖于候选键(即实体的唯一标识),而不是依赖于候选键的一部分。换句话说,表中的每一列数据都和主键相关,而不是部分相关。
-
-
第三范式(3NF):
-
在满足第二范式的基础上,要求非主键列之间不存在传递依赖。换句话说,任何非主键列不能依赖于其他非主键列。
-
5.说说数据库事务的四个原则?
-
原子性(Atomicity):
-
事务是一个不可分割的工作单位,要么全部执行成功,要么全部失败回滚,不存在部分执行的情况。
-
-
一致性(Consistency):
-
在事务开始前和结束后,数据库的完整性约束没有被破坏。即事务在执行前后,数据库从一个一致性状态变换到另一个一致性状态。
-
-
隔离性(Isolation):
-
多个事务并发执行时,每个事务对其他事务的操作是隔离的,互不干扰。每个事务应该感觉就像在系统中执行时,没有其他事务在同时运行。
-
-
持久性(Durability):
-
一旦事务提交,其结果应该永久保存在数据库中,即使发生系统故障也不应该丢失。已提交的事务对系统的影响是持久的。
-
6.说说队列和栅的区别?
队列(Queue)和栈(Stack)是两种常见的数据结构,它们在数据的存储和访问方式上有显著区别。队列采用先 进先出(FIFO)的原则,新元素从队尾添加,从队头移除,适合于任务调度和消息传递等需要按顺序处理的场 景。而栈则是后进先出(LIFO),最后放入栈的元素最先被取出,常用于表达式求值和函数调用等逆序处理的需求。这两种数据结构操作方式不同,根据数据访问的顺序需求选择合适的结构能提高程序效率和逻辑清晰度。
7.你都知道哪些排序方法,简要说说?
-
冒泡排序(Bubble Sort):通过相邻元素的比较和交换来进行排序,每次将最大(或最小)的元素冒泡到最后(或最前)。
-
选择排序(Selection Sort):每次从未排序的数据中选择最小(或最大)的元素,放到已排序序列的末尾,直到所有元素排序完毕。
-
插入排序(Insertion Sort):通过构建有序序列,对未排序数据在已排序序列中从后向前扫描,找到相应位置并插入。
-
希尔排序(Shell Sort):是插入排序的一种改进,通过将原始列表分割成若干个较小的子列表,对各个子列表进行插入排序,最后再对整个列表进行插入排序。
-
归并排序(Merge Sort):采用分治法,将列表分成较小的两个子列表,递归地对子列表进行排序,然后将排好序的子列表合并成一个有序列表。
-
快速排序(Quick Sort):也是采用分治法,通过一趟排序将待排序记录分割成独立的两部分,其中一部分的所有记录都比另一部分的所有记录小,然后对这两部分分别进行快速排序。
-
堆排序(Heap Sort):利用堆这种数据结构而设计的一种排序算法,堆是一个近似完全二叉树的结构,并同时满足堆的性质。
-
计数排序(Counting Sort):非比较排序算法,通过确定每个元素在输入序列中的位置来排序,适用于元素范围较小且分布均匀的情况。
8.数据库的隔离级别?最好深入谈谈。
数据库的隔离级别是指多个并发事务在数据库中执行时,各自所能看到的数据状态的不同程度。隔离级别的设定影响了事务并发执行的效果、数据的一致性以及系统的性能。常见的隔离级别有四种:读未提交(Read Uncommitted)、读提交(Read Committed)、可重复读(Repeatable Read)、串行化(Serializable)。
-
读未提交(Read Uncommitted):
-
最低的隔离级别。
-
允许事务读取其他未提交的事务修改的数据。
-
可能导致脏读(Dirty Read),即读取到未提交的数据,可能造成不一致的结果。
-
-
读提交(Read Committed):
-
数据库默认的隔离级别(大多数数据库)。
-
事务只能读取到已经提交的数据,不能读取到未提交的数据。
-
可能导致不可重复读(Non-repeatable Read),即在同一个事务中,两次读取同一数据,但结果不一致。
-
-
可重复读(Repeatable Read):
-
保证了在事务执行期间多次读取同一数据时,读取到的数据始终一致,即使其他事务已经修改了该数据。
-
避免了不可重复读问题。
-
可能导致幻读(Phantom Read),即在同一个事务中,两次查询同一个范围的数据,但结果集不一致,可能由于其他事务插入新数据导致。
-
-
串行化(Serializable):
-
最高的隔离级别。
-
通过强制事务串行执行来避免并发问题,保证了最高的数据一致性。
-
可能会降低系统的并发性能,因为它会限制同时执行的事务数量。
-
9.说说redis的几个数据类型?
string 字符串(一个字符串类型最大存储容量为512M) list 可以重复的集合 set 不可以重复的集合 hash 类似于 Map zset(sorted set) 带分数的 set
10.说一说redis的使用场景?
Redis被广泛应用于数据缓存、会话存储、消息队列、实时数据处理和分布式锁等场景。作为内存数据库,Redis提供了高速的数据读写能力,适合存储频繁访问的数据如网页内容、用户会话信息等,显著提升系统响应速度。它还可用作简易的消息队列或发布/订阅系统,支持异步任务处理和实时数据分析。Redis的数据结构丰富,如字符串、哈希、列表等,适用于各种数据处理需求,如计数器、排行榜和统计数据的存储。此外,Redis的原子操作能力使其成为分布式环境下实现锁机制的理想选择,确保多个进程或服务器间的数据互斥访问。
11.cookie和session的区别?
Cookie 是 web 服务器发送给浏览器的一块信息,浏览器会在本地一个文件中 给每个 web 服务器存储 cookie。以后浏览器再给特定的 web 服务器发送请求时,同 时会发送所有为该服务器存储的 cookie Session 是存储在 web 服务器端的一块信息。session 对象存储特定用户会话 所需的属性及配置信息。当用户在应用程序的 Web 页之间跳转时,存储在 Session 对 象中的变量将不会丢失,而是在整个用户会话中一直存在下去 Cookie 和 session 的不同点 无论客户端做怎样的设置,session 都能够正常工作。当客户端禁用 cookie 时将无 法使用 cookie 在存储的数据量方面:session 能够存储任意的 java 对象,cookie 只能存储 String 类型的对象
12.mybatis中#{}和${}的区别?
#{}是预编译处理也就是占位符,${}是字符串替换; Mybatis 在处理#{}时,会将 sql 中的#{}替换为?号,调用 PreparedStatement 的 set 方法来赋值; Mybatis 在处理${}时,就是把${}替换成变量的值; 使用#{}可以有效的防止 SQL 注入,提高系统安全性
13什么是SQL注入,在使用mybatis时如何防止SQL注入?
SQL注入是一种利用不安全的输入验证或SQL查询构造的漏洞,使攻击者能够执行恶意SQL语句的攻击方式。在使用MyBatis时,为防止SQL注入,关键在于始终使用#{}
来替代${}
来传递参数。#{}
会将参数值安全地作为预编译参数传递给数据库,而${}
则直接替换SQL片段,可能导致注入风险。此外,严格验证和过滤用户输入、避免动态拼接SQL语句、以及限制数据库用户权限也是有效防范SQL注入的措施之一。
14.你用get请求比较多还是post请求比较多?为什么?
我更倾向于使用GET请求来获取数据,因为GET请求具有以下优势:
-
GET请求适合用于数据检索和无副作用的操作,如获取页面内容或查询资源。
-
GET请求的参数通过URL传递,便于调试和直接在浏览器中测试。
-
GET请求可以被缓存,有利于提升性能和减少服务器压力。
然而,对于需要传输敏感信息或进行修改操作的情况,我会选择使用POST请求:
-
POST请求适合提交数据和执行非幂等操作,如提交表单、修改资源状态等。
-
POST请求的数据传输在HTTP请求体中,相对安全不易被截取。
-
POST请求支持传输大量数据和多种数据类型,比如文件上传。
15.聊一下面向对象的开闭原则
面向对象编程中的开闭原则(Open-Closed Principle,OCP)是指软件实体(类、模块、函数等)应该对扩展开放,对修改关闭的原则。具体来说:
-
开放对扩展(Open for Extension): 表示可以通过添加新的代码来扩展模块的功能。新功能应该通过扩展现有代码来实现,而不是修改现有代码。
-
关闭对修改(Closed for Modification): 表示现有的实现代码不应该被修改,以保证稳定性。任何变化都应该通过扩展来实现,而不是直接修改已有的实现。
实现开闭原则的关键在于设计良好的抽象和接口,使得系统可以方便地扩展而不需要修改现有的代码。常见的实现方式包括使用抽象类、接口、依赖注入和设计模式(如策略模式、装饰器模式等)来实现可扩展的架构。
16.说一下进程和线程的区别?
进程和线程是操作系统中的两个重要概念:
-
进程(Process):
-
是程序在执行过程中分配和管理资源的基本单位。
-
拥有独立的内存空间和系统资源,包括代码、数据、文件描述符等。
-
进程间通信需要特殊的IPC(Inter-Process Communication)机制。
-
创建和撤销进程的开销较大。
-
-
线程(Thread):
-
是进程中的执行单元,共享相同进程的资源,如内存空间、文件等。
-
线程间通信简单,直接共享内存,因此效率较高。
-
创建和撤销线程的开销较小。
-
多个线程可以并发执行,适合在多核处理器上提高程序性能。
-
总之,进程适合进行资源分配和调度,线程则更适合实现并发和提高程序响应速度。操作系统利用这两个概念来优化资源管理和提高系统性能。
17list,map,set集合有什么不同?
-
List(列表):
-
是一种有序的数据集合,可以存储重复元素。
-
元素之间的顺序是有序的,即插入的顺序与元素在列表中的顺序是一致的。
-
可以通过索引访问元素,支持增加、删除和修改操作。
-
在Python中,对应的数据类型是
list
。
-
-
Map(映射):
-
通常指的是键值对的集合,也称为字典(Dictionary)。
-
每个元素包含一个键和对应的值,键是唯一的,但值可以重复。
-
支持通过键来快速查找、插入和删除元素。
-
在Python中,字典的数据类型是
dict
。
-
-
Set(集合):
-
是一种无序且不重复的集合。
-
集合中的元素没有顺序,不能通过索引访问。
-
主要用来判断一个元素是否在集合中,或者进行集合间的交、并、差等运算。
-
在Python中,集合的数据类型是
set
。
-
关键区别总结:
-
List 是有序的、允许重复元素的序列。
-
Map 是键值对的集合,键唯一且无序,值可以重复。
-
Set 是无序且不重复的集合,用于存储唯一值并支持集合运算。
18.内连接和外连接有什么区别和联系?
内连接(Inner Join)和外连接(Outer Join)是数据库中常用的数据关联方法。内连接通过匹配两个表中满足连接条件的行来获取数据,结果集中只包含符合条件的数据。例如,连接订单表和客户表,只返回已有订单的客户信息。外连接则包括不满足条件的行,左外连接返回左表所有行及右表匹配行(无匹配时填NULL),右外连接则反之。全外连接返回左右表所有行及对应匹配,无匹配时同样填NULL。例如,左外连接可用于查看所有客户及其订单情况,即使某些客户没有订单。连接类型的选择取决于需求,内连接用于只需匹配数据,而外连接适合需要包含所有数据的场景。
19.谈谈VUE中的双向绑定?
在Vue.js中,双向绑定是一种机制,能够确保视图层与模型层的数据保持同步更新。通过 v-model
指令,Vue实现了表单元素与数据之间的双向绑定,使得当表单数据变化时,相关的数据模型也会自动更新,反之亦然。这种机制简化了开发者操作DOM和监听事件的复杂性,提升了开发效率和代码可维护性,特别适用于需要频繁操作表单数据的场景。
20.你对VUE的路由了解多少?
Vue的路由系统由Vue Router管理,用于构建单页面应用(SPA)。它支持声明式的路由配置,通过组件化的方式定义导航和视图的关系。开发者可以轻松地定义嵌套路由、动态路由参数,并实现路由间的数据传递。路由系统还提供了导航守卫,允许开发者在路由跳转前后进行逻辑控制和权限验证。此外,Vue Router支持懒加载,可以按需加载路由组件,优化应用的加载性能。总之,Vue Router为Vue.js应用提供了强大的路由管理功能,使得开发单页面应用变得简单、高效。
21.mysql的索引用到过吗?怎么实现的?
MySQL的索引是提高数据库检索效率的重要手段。它通过B-Tree索引和哈希索引实现,B-Tree适用于范围查询和排序,而哈希索引则适用于等值查询。在MySQL中,我们可以通过CREATE INDEX命令在创建表时或ALTER TABLE命令后添加索引。合理选择和设计索引可以显著提升数据库的查询性能,但需要注意不要过度索引,以避免额外的存储和更新成本。通过监控和优化索引的使用,可以有效提高数据库的响应速度和整体性能。
22.如果不使用spring的方法,单纯的java怎么读取文件?那使用spring方法呢?
使用纯Java读取文件,首先创建File
对象表示文件路径,然后使用FileInputStream
或BufferedInputStream
创建文件输入流。通过输入流读取文件内容,可以使用字节数组或字符流方式获取数据,并确保在完成后关闭流以释放资源。这种方法适用于简单的文件读取操作,能够有效处理文本和二进制文件。
使用spring的话,Spring框架简化了文件操作,利用ResourceLoader
和Resource
接口可以轻松加载类路径或URL资源。通过getResource
方法获取资源,再使用getInputStream
读取内容。支持从文件系统加载资源,如FileSystemResource
。Spring的这些特性使文件访问更统一,适用于各种资源管理需求,如文本、配置文件等。
23.mysql事务的状态有哪些?
-
活动(ACTIVE):事务已经启动并且正在进行中。在这个状态下,事务可以执行SQL语句来读取和修改数据。
-
部分提交(PARTIALLY COMMITTED):事务执行了所有的SQL语句并且已经提交了部分的修改,但是还没有完成最终的提交操作。在这个状态下,数据库引擎已经将部分的修改操作写入到事务日志中。
-
已提交(COMMITTED):事务已经顺利完成,所有的修改操作都已经提交到数据库中,并且事务日志也已经写入磁盘。
-
已回滚(ROLLED BACK):事务由于某种原因被取消或者回滚,所有的修改操作都被撤销,数据库回到事务开始前的状态。
-
失败(FAILED):事务在执行过程中遇到了错误或者异常,无法继续执行。这种情况下,事务通常会自动回滚,但有时需要手动进行处理。
-
挂起(SUSPENDED):事务处于暂停状态,暂时不执行任何操作。这种状态通常不常见,而且通常是在特殊的数据库管理操作中出现。
24.说一说jvm的内存分配,谈谈每个区的作用。
java 虚拟机主要分为以下几个区 (1) 方法区 有时候也成为永久代,在该区内很少发生垃圾回收,但是并不代表不发生 GC,在 这里进行的 GC 主要是对方法区里的常量池和对类型的卸载 方法区主要用来存储已被虚拟机加载的类的信息、常量、静态变量和即时编译器 编译后的代码等数据。 该区域是被线程共享的。 方法区里有一个运行时常量池,用于存放静态编译产生的字面量和符号引用。该 常量池具有动态性,也就是说常量并不一定是编译时确定,运行时生成的常量也 会存在这个常量池中。 (2)虚拟机栈 虚拟机栈也就是我们平常所称的栈内存,它为 java 方法服务,每个方法在执行的 时候都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接和方法出口 等信息。 虚拟机栈是线程私有的,它的生命周期与线程相同。 局部变量表里存储的是基本数据类型、returnAddress 类型(指向一条字节码指令 的地址)和对象引用,这个对象引用有可能是指向对象起始地址的一个指针,也 有可能是代表对象的句柄或者与对象相关联的位置。局部变量所需的内存空间在 编译器间确定 操作数栈的作用主要用来存储运算结果以及运算的操作数,它不同于局部变量表 通过索引来访问,而是压栈和出栈的方式 每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用 是为了支持方法调用过程中的动态连接.动态链接就是将常量池中的符号引用在运 行期转化为直接引用。 (3)本地方法栈 本地方法栈和虚拟机栈类似,只不过本地方法栈为 Native 方法服务。 (4)堆 java 堆是所有线程所共享的一块内存,在虚拟机启动时创建,几乎所有的对象实例 都在这里创建,因此该区域经常发生垃圾回收操作。 (5)程序计数器: 内存空间小,字节码解释器工作时通过改变这个计数值可以选取下一条需要执行的 字节码指令,分支、循环、跳转、异常处理和线程恢复等功能都需要依赖这个计数 器完成。该内存区域是唯一一个 java 虚拟机规范没有规定任何 OOM 情况的区域
25.说一说java的垃圾回收机制?
Java的垃圾回收机制是一种自动化内存管理系统,其设计目的是减少程序员手动管理内存的复杂性和错误。以下是Java垃圾回收机制的详细解释:
-
引用追踪:
- Java的垃圾回收器通过跟踪对象之间的引用关系来确定哪些对象是“活”的,即可达的(reachable),哪些是“死”的,即不可达的(unreachable)。
-
标记-清除算法:
- 最基本的垃圾回收算法是标记-清除(Mark and Sweep)。在这个过程中,垃圾回收器首先标记所有被引用的对象,然后清除所有未被标记的对象,释放它们所占用的内存空间。
-
可达性分析:
- 垃圾回收器从一个或多个称为“根”的对象开始,递归地遍历所有从根开始的引用链,标记所有可达对象。不可达的对象会被判定为垃圾并进行回收。
-
串行垃圾回收器(Serial Garbage Collector):
- 单线程执行垃圾回收,适合于单核处理器和简单应用,会在整个回收过程中暂停所有用户线程。
-
并行垃圾回收器(Parallel Garbage Collector):
- 使用多个线程进行垃圾回收,可以显著减少回收时的停顿时间,适合多核处理器和需要高吞吐量的应用。
-
并发垃圾回收器(Concurrent Mark Sweep Garbage Collector,CMS GC):
- 在程序运行的同时执行部分垃圾回收操作,减少长时间的停顿。适用于需要快速响应时间的应用场景。
-
G1垃圾回收器(Garbage-First Garbage Collector):
- 面向大堆内存和多核处理器的高性能回收器,通过分区来进行垃圾回收,可在更短的时间内达到可接受的停顿时间。
-
停顿时间:长时间的垃圾回收停顿会影响程序的响应性能,因此需要根据应用的特性选择合适的垃圾回收器来平衡吞吐量和停顿时间。
-
内存利用率:优化的垃圾回收可以有效避免内存泄漏和减少内存碎片,提高可用内存的利用率和程序的整体性能稳定性。
26.如何解决RabbitMQ的幂等性?
使用 SETNX
命令可以实现分布式系统中的幂等性操作,特别是在处理并发情况下的数据写入操作时非常有用。SETNX
命令是 Redis 中的一个原子性命令,用于设置一个键的值,仅当键不存在时才会设置成功,如果键已经存在,则不做任何操作。
生成唯一标识:首先,生成一个唯一的操作标识符或者消息ID,可以是全局唯一的,例如使用 UUID。
使用 SETNX 执行操作:将生成的唯一标识作为 Redis 键,操作的数据作为值,使用 SETNX
命令尝试设置这个键值对。
- 如果
SETNX
返回 1(表示设置成功),则说明操作尚未执行过,可以执行操作并记录该操作已经完成。 - 如果
SETNX
返回 0(表示键已存在),则说明操作已经执行过,此时可以直接跳过操作的执行步骤,因为之前已经处理过相同的操作。
记录操作完成状态:在操作执行成功后,可以将该操作的完成状态记录在 Redis 中,以便后续检查操作是否已经完成。
27.如何保证rabbitmq数据不丢失?
1.站在消费者角度
丢失场景:如果使用默认的自动应答机制,当消费者堆积大量未消费的数据的时候可能造成消费者服务器宕机。会造成数据丢失。因为默认的自动应答在消费者接收到消息的时候就返回给rabbitMQ一个ack信息。
解决方案:使用手动应答。当开启手动应答的时候,如果消费者宕机那么未应答的数据会重新入队。
2.站在RabbitMQ角度
丢失场景:当RabbitMQ宕机的时候,队列和队列中的数据都会丢失。
解决方案:对队列和队列中的数据进行持久化。持久化完成后服务器宕机后重启数据和队列依然存在
3.站在生产者角度
丢失场景:当生产者把数据推送给rabbitMQ的时候,rabbitMQ会进行持久化。在持久化未完成的时候rabbitMQ服务宕机依然会造成数据丢失。
解决方案:开启发布确认模式。生产者发送消息给RabbitMQ的同时发送一条确认消息。当rabbitMQ把消息推送到指定队列中并且持久化完成后给生产者返回ack。如果返回nack则需要生产者重新发送数据。
28.如何解决延迟队列的边界问题?
-
使用消息的 TTL 和死信队列:
- RabbitMQ 中可以通过设置消息的 TTL 来实现延迟投递。当消息过期后,可以将其发送到死信交换机(Dead-Letter Exchange,DLX),然后由 DLX 把这些消息路由到指定的队列。
- 此方法需要设置两个队列:一个用于存放待处理的延迟消息,另一个用作死信队列来接收超时的消息。
- 可以使用
x-message-ttl
和x-dead-letter-exchange
参数来配置队列和消息的 TTL 和死信交换机。
-
使用插件实现延迟队列:
- RabbitMQ 社区提供了一些插件,如
rabbitmq_delayed_message_exchange
插件,它允许你发送带有延迟的消息而无需额外的队列。 - 这种方法可以直接在消息交换时设置延迟时间,而不是通过消息 TTL 来实现延迟。
- RabbitMQ 社区提供了一些插件,如
-
处理延迟边界问题:
- 在使用消息 TTL 时,要注意消息的延迟时间与实际的处理时间之间可能存在的偏差。例如,如果消息被发送时系统时间不准确或者 RabbitMQ 服务时间与发送者不同步,可能会导致消息的延迟投递时间存在一定的误差。
- 可以通过使用 NTP(Network Time Protocol)等方式来保持系统时间的同步性,减少因时间不同步而导致的延迟边界问题。
29.Redis和Mysql数据一致性如何实现?
一、 延时双删策略
在写库前后都进行redis.del(key)操作,并且设定合理的超时时间。具体步骤是: 1)先删除缓存 2)再写数据库 3)休眠500毫秒(根据具体的业务时间来定) 4)再次删除缓存。 那么,这个500毫秒怎么确定的,具体该休眠多久呢? 需要评估自己的项目的读数据业务逻辑的耗时。这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据。 当然,这种策略还要考虑 redis 和数据库主从同步的耗时。最后的写数据的休眠时间:则在读数据业务逻辑的耗时的基础上,加上几百ms即可。比如:休眠1秒。
二、设置缓存的过期时间
从理论上来说,给缓存设置过期时间,是保证最终一致性的解决方案。所有的写操作以数据库为准,只要到达缓存过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存 结合双删策略+缓存超时设置,这样最差的情况就是在超时时间内数据存在不一致,而且又增加了写请求的耗时。
三、如何写完数据库后,再次删除缓存成功?
上述的方案有一个缺点,那就是操作完数据库后,由于种种原因删除缓存失败,这时,可能就会出现数据不一致的情况。这里,我们需要提供一个保障重试的方案。
1、方案一具体流程
(1)更新数据库数据; (2)缓存因为种种问题删除失败; (3)将需要删除的key发送至消息队列; (4)自己消费消息,获得需要删除的key; (5)继续重试删除操作,直到成功。 然而,该方案有一个缺点,对业务线代码造成大量的侵入。于是有了方案二,在方案二中,启动一个订阅程序去订阅数据库的binlog,获得需要操作的数据。在应用程序中,另起一段程序,获得这个订阅程序传来的信息,进行删除缓存操作。
2、方案二具体流程
(1)更新数据库数据; (2)数据库会将操作信息写入binlog日志当中; (3)订阅程序提取出所需要的数据以及key; (4)另起一段非业务代码,获得该信息; (5)尝试删除缓存操作,发现删除失败; (6)将这些信息发送至消息队列; (7)重新从消息队列中获得该数据,重试操作。