1、JVM调优的工具有哪些?

命令:

  jsp 进程状态信息

  Jstack 线程内的堆栈信息

  Jmap(使用较多) 查看堆的信息,堆的大小配置,堆内存各部分的使用情况

  jstat 垃圾回收信息

  工具:

  jconsole:用于对JVM内存,线程,类的监控

  visualVM:用于对JVM内存,线程,类的监控

2、Java内存泄漏的排查思路?

首先通过jmap命令或者设置JVM参数区获取dump文件

然后将dump文件放入VisualVM中去分析

通过查看堆的信息,定位到哪行代码出了问题,然后再去修改代码

3、CPU彪高排查方案与思路

1、使用top命令,查看哪个进程CPU使用率较高

2、使用ps H -eo pid,tid,%cpu | grep 进程id 命令查看进程中的线程状态

3、找到关键线程,然后使用jstack命令去查看进程中哪个线程出了问题,然后找到对应代码并修改

四、Mysql数据库

1、InnoDB和MyISAM的区别和使用场景

事务:InnoDB支持事务,而MyISAM不支持

外键:InnoDB支持外键,而MyISAM不支持

锁:InnoDB支持行级锁(并发量高)和表锁,MyISAM只支持表锁

索引:InnoDB不支持全文索引,MyISAM支持全文索引(全文索引是基于分词的索引,能更快速地在文本中检索一段信息内容)

使用场景:

InnoDB适合需要高并发或事务的场景,

MyISAM适合读操作远远大于写操作且不需要事务的场景 

属性InnoDBMyISAM
事务支持事务不支持事务
外键支持外键不支持外键
支持行锁(并发更高)和表锁只支持表锁(不会死锁)
索引不支持全文索引支持全文索引(查询更快)
适用场景并发量高或者需要事务读远多于写且不需要事务

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记【点击此处即可】免费获取

2、如何防止sql注入?

在编写sql语句时,使用参数传递的方式去给变量赋值而不是直接嵌入到sql语句中

在mybatis的mapper中,我们可以用#{value}的方式去传递参数防止sql注入。

3、怎样实现幂等

幂等:也就是相同条件下对一个业务的操作,不管操作多少次,结果都是一样

实现方案:唯一索引(通常是主键),乐观锁(通常是version字段),token+redis(令牌-缓存)

token:token是服务端生成的一串字符串,当客户端登录后,服务端将token作为令牌发送给客户端,之后客户端访问服务端时,只需要校验token即可,不需要再验证用户和密码,redis作为缓存保存令牌到服务端。

4、一条sql语句的执行流程

Mysql分为server层和存储引擎层

  1. 建立连接:server层的连接器验证客户端发来的用户名和密码是否正确,如果错误就返回错误提示,如果正确就校验用户的权限
  2. 查询缓存:当执行sql查询语句时,mysql会先去缓存中查询是否有记录,如果有记录就直接返回,没有的话就去数据库中查询,然后将查询记录记录到缓存中
  3. 分析器:分析器的作用是对sql语句进行词法分析和语法分析,词法分析就是对sql语句中的关键词进行分析,比如查询到select关键词就知道这是一条查询语句,此外还会分析被操作的表,条件语句的字段等,语法分析就是判断这条语法是否正确。
  4. 优化器:对sql语句进行优化
  5. 执行器:调用存储引擎的接口,执行sql语句,执行之前会判断一下有没有权限
 5、Mysql的事务特性(ACID)
  • 原子性:单个事务是不可分割的最小工作单元,事务中的所有操作,要么全都执行成功,要么全都执行失败。通过undolog日志解决
  • 一致性:数据库总是从一个一致性的状态转换到另外一个一致性的状态。如果事务成功完成,那么所有变化都将成功应用,如果失败,就会回滚到原始状态。通过undolog日志解决
  • 持久性:一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。通过redolog日志解决
  • 隔离性:事务与事务之间互不影响。通过加锁实现
6、 Mysql事务的隔离级别,脏读、不可重复度,幻读。

脏读:事务读取到了未提交的数据,比如A查询到了B修改了但未提交的数据

不可重复读:事务对同一内容的两次查询结果不一致,比如A查询到数据不存在,B插入并提交,A再次查询时发现存在

幻读:事务在插入前检查到数据不存在,插入时却发现数据已经存在无法插入,比如A发现数据不存在准备插入,但B此时插入数据并提交

不可重复读重点在于update,而幻读的重点在于insert或delete。

Mysql事务的隔离级别从低到高为:

  1. 读未提交RC:所有的事务都可以读取其他事务未提交的结果,会发生脏读,不可重复读,幻读
  2. 读已提交RU:事务开始时,只能读取到已提交的修改,会发生不可重复读,幻读
  3. 可重复读RR:事务开始时,在同一条件的查询返回的内容是一致的,会发生幻读,mysql默认隔离级别Mysql-可重复读的隔离级别在什么情况下会出现幻读_可重复读为什么会出现幻读_rsjssc的博客-CSDN博客
  4. 可串行化:所有事务依次执行,事务之间不能互相干扰,能防止脏读,不可重复读,幻读
7、 锁的类型,行锁和表锁,共享锁和独占锁
锁的类型是否会死锁并发量适用场景
表锁不会死锁并发量低

1、读多写少(上一次锁连续读)

2、写特别多(如果用行锁,会导致其他事务长时间锁等待,锁冲突频率变高)

行锁会死锁并发量高并发量高的场合
页面锁会死锁并发量中

 行锁:行锁就是加在单行上的锁,只存在于InnoDB引擎中,分为共享锁(读锁)和独占锁(写锁)

类型作用
共享锁(读锁)获得共享锁的事务被允许读一行,并阻止其他事务获得该行的独占锁
独占锁(写锁)获得独占锁的事务被允许写一行,并阻止其他事务获得该行的共享锁和独占锁

读锁和写锁的

  • 共同点: 自动加锁
  • 不同点:共享锁可能会死锁,独占锁不会 / 读锁读取数据后释放,写锁要事务结束后才释放
  • 语法:SELECT...+LOCK IN SHARE MODE / INSERT...+FOR UPDATE

 此外还有意向共享锁和意向独占锁,这是InnoDB自动添加的,事务在获取读锁和写锁前必须先获取该表的对应意向锁。

表锁:表锁是加在整个表的锁

表锁也有两种锁,共享锁和独占锁,作用与行锁的基本相同

语法:LOCK TABLE {TABLENAME} READ / LOCK TABLE {TABLENAME} WRITE

8、内关联,全关联,左关联,右关联的区别

内关联(INNER JOIN):所有的查询结果在关联的两张表中都有记录

全关联:联结了那些在相关表中没有关联的行,分为左关联和右关联

左关联(LEFT JOIN):从From子句的左边表中选择所有行,可以没有右边的表的行

左关联(RIGHT JOIN):从From子句的右边表中选择所有行,可以没有左边的表的行

9、索引,索引的分类

索引的作用相当于是图书的目录,它是对数据表中一列或多列值进行排序的一种存储结构

索引的分类:普通索引,主键索引,复合索引,唯一索引,外键索引,全文索引

普通索引:

        mysql表中最基本的索引

        语法:CREAT INDEX index_name ON TABLE table_name(column)

主键索引:

        在Mysql创建表指定主键的时候,会自动在主键上建立一个主键索引,标志着主键具有唯一性且不为空 

复合索引:

        复合索引也叫组合索引,在建立索引的时候使用表中的多个字段,就比如说使用身份证号和手机号建立索引

        语法:CREAT INDEX index_name ON TABLE table_name(column1,column2)

 唯一索引:

        唯一索引标志着该字段具有唯一性

        语法:CREAT UNIQUE INDEX index_name ON TABLE table_name(column)

如果创建表的时候加索引语法为:

CREATE TABLE tablename(
    propname1 type1,
    ……
    propnamen type..n,
    UNIQUE INDEX|KEY index_name (column [(length)] [ ASC | DESC ] ) );

   //简化为:UNIQUE INDEX index_name (column);

10、索引的优缺点和使用规则

索引的优点主要体现在:

  1. 提高了用户查询的速度,提高了性能
  2. 降低了查询中分组和排序的时间
  3. 加速表与表之间的连接
  4. 通过索引的唯一性,可以确保表中数据的唯一性

索引的缺点主要体现在:

  1. 空间:索引占用了磁盘空间
  2. 时间:当数据量较大时,索引的创建和维护也是非常耗费时间的
  3. 每次对数据进行增删改查时,索引也要进行动态维护,降低了数据的维护速度

总结,以下条件不适合添加索引:

  1. 在查询中很少使用的列
  2. 数据值很少的列
  3. 写操作远多于读操作的列(修改频率大于查询频率的列)

使用索引的规则:

  1. 对查询频率高的字段创建索引
  2. 对经常需要分组、排序和联合操作的字段创建索引
  3. 尽量使用唯一索引
  4. 多使用短索引
  5. 索引的数目不要不多(不然会降低增删改的效率)
11、索引的原理,B+树的优点

首先我们了解一下B树(Balance Tree),即平衡树的意思,下图即是一颗B树

图片

 B树的每一个节点代表一个磁盘块,每一个节点上面存储了多个键值对和指针

B树相对于平衡二叉树,每个节点存储了更多的键(Key)和数据(Data),并且每个节点拥有更多的子节点,子节点的个数就是B树的阶数,比如上图就是的B树就是三阶B树

基于这个性质,B树查询数据读取磁盘的次数会减少,查找的效率会比平衡二叉树高。

B+树是对B树的进一步优化,下图是一颗B+树

 在B+树中,非叶子节点不会存储数据(Data),只会存储键值(Key),而所有的数据都存储在叶子节点上,并且所有数据都是按照顺序排列的

在数据库中,页(节点)的大小是固定的,InnoDB默认为16kb,页如果不存储数据,就可以存储更多的键值,树的阶数会提升,

这样一来我们查询数据访问磁盘的次数会进一步减少,查询的效率更高,根节点默认存储在内存,所以我们访问一次三阶B+树索引的数据只需要2次磁盘IO

因为B+树索引中所有的数据都存储在叶子节点上,并且按顺序排列,所以对数据的范围查找,分组,排序都会更加简单

MyISAM中的B+树索引实现与innodb中的略有不同。在MyISAM中,B+树索引的叶子节点并不存储数据,而是存储数据的文件地址。

B+树相对于B树的特点:

  1. 只有叶子节点才会存放数据,非叶子节点只存放索引
  2. B+树中每个节点都通过双向链表连接,叶子节点中的数据通过单向链表连接
  3. 非叶子节点中的索引会出现在子节点中,而且是子节点中索引的最大(最小)值
  4. 非叶子节点的索引树=子节点数

B+树相对于B树的优点:

  1.  普通查询,范围查询,分组查询,排序的速度更快
  2. 插入和删除的速度更快
12、聚集索引和非聚集索引

以主键为B+树索引的键值构建的B+树索引就是聚集索引

以主键以外的字段作为B+树索引的键值构建的B+树索引就是非聚集索引

非聚集索引与聚集索引的区别在于非聚集索引的叶子节点不存储表中的数据,而是存储该列对应的主键,想要查找数据我们还需要根据主键再去聚集索引中进行查找,这个再根据聚集索引查找数据的过程,我们称为回表。   

13、联合索引的原理

联合索引是对多个列进行索引

联合索引也是通过B+树实现的,特点如下:

  • B+树的键值数量不是1,而是2或2以上
  • B+树在对第一个索引排序的基础上,再去对第二个索引排序,再去对第三个,依次排序
  • 联合索引遵循最左匹配原则

最左匹配原则:在联合索引中查询时,从左边的字段开始匹配,若条件中的字段顺序与联合索引中字段顺序一致,就可以走索引查询,否则不走

14、索引什么时候会失效
  1. 使用LIKE模糊查询时%或者_放在最前面(会变为全表查询)
  2. 使用or语句or的两边没有同时使用索引
  3. 联合索引查询时没有遵守最左匹配原则
  4. 索引列数据类型隐式转化(varchar不加单引号的话可能会自动转换为int型)
  5. 对索引列进行计算或使用函数(改变了列值,在索引中找不到)
  6. order by使用错误(排序与索引中的排序不一致)
  7. 全盘扫描速度比索引快
  8. Where 子句中使用参数
15、高性能数据库优化实战经验:

1.打破范式设计,冗余少量字段方便查询,需要注意源表和冗余表保证同一事务写。
2.关联关系在业务层面约束,不依赖数据库外键
3.字段拓展性,如模板信息这种结构不清晰的字段使用json类型,json检索的问题我的想法是少量key使用虚拟列并建立索引,多条件检索直接异构es
4.冷热分离,源表拆分成多张表,可以把频繁变更的字段放在主表,使用率较低的放在副表,判断依据可以是创建时间、业务域
5.服务拆分在分片字段选择上尽量考虑使用本地事务,让同业务的不同sql命中同一个分表,以避免使用分布式事务
6.尽量使用单表维度sql,原因:join性能差,后期分库分表更方便,前瞻性设计要考虑使用哪种ID主键策略

16、sql语句中group by的使用

        group by 根据字面意思就是对数据进行分组,所谓分组就是将数据集划分为若干个不同的小区域,针对每个小区域单独数据处理

        注意:group by语句中select 查询的字段要么就是分组依据,要么在聚合函数中

        select 类别,sun(分数) from 成绩 group by 类别 order by sum(分数) desc

        

 篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记【点击此处即可】免费获取

17、sql书写顺序
 
  1. SELECT <字段名>

  2. FROM <表名>

  3. JOIN <表名>

  4. ON <连接条件>

  5. WHERE <筛选条件>

  6. GROUP BY <字段名>

  7. HAVING <筛选条件>

  8. UNION

  9. ORDER BY <字段名>

  10. LIMIT <限制行数>;

AI写代码

18、关联语句join与合并语句union

        Join是关联语句,可以把两个表通过连接条件合并一个数据集

        inner join:内连接,只有在两个表中都符合连接条件的记录才会被收集

        left join:左连接的结果包括左表中的所有记录和右表中满足连接条件的记录

        right join:右连接的结果包括右表中的所有记录和左表中满足连接条件的记录

        full join:全连接的结果是左右表的并集

        cross join:左右表做笛卡尔积

        union (all):union能将多个select的记录进行合并,这多个记录必须列数一致,列名一致,列数据类型对应一致,如果是union all则返回的记录集包括重复记录

五、Redis

1、redis缓存穿透,缓存击穿以及缓存雪崩问题和对应的解决方案

缓存穿透:当客户端访问一个不存在的数据,这个数据在缓存和数据库中都不能命中,如果有大量的这种穿过缓存直接访问数据库的请求,就会给数据库带来很大压力。

解决思路:

  • 缓存空对象:当发现数据库中也没有该数据时,我们把这个数据存在redis中,但值设置为空,这样下次访问时就会直接从redis中获取空对象。实现简单,维护方便,但内存消耗更大
  • 布隆过滤器:布隆过滤器实际上是一串很长的二进制数组,它通过哈希思想去判断key是否存在,如果不存在则拒绝请求,存在则放行右redis和数据库处理。内存占用少,但实现复杂,有误判可能(存在也可能被判为不存在)

实例解决方案(缓存空对象)

1、判断缓存中是否有数据,如果有直接返回,没有则去数据库查数据

2、判断从数据库查出的数据是否为空,如果为空,则将空对象存在redis中

 缓存击穿:当一个被高并发访问并且重建业务比较复杂的热点key失效时,会有大量的请求直接访问到数据库,给数据库带来巨大冲击。

解决思路:

  • 分布式互斥锁:当一个线程获得锁然后执行重建缓存的逻辑时,其他线程只能等待缓存重建完成后再去获取缓存(使用redis的setnx方法来表示获取锁 ,set if not exist)。有死锁和线程池阻塞的风险,降低吞吐量,但能有效保持数据一致
  • 设置永不过期:不直接设置key的过期时间,而是将过期时间放在value中,当有线程去查询key并发现已经到过期时间后,就异步地去更新缓存。解决了热点key的一系列危机,但在缓存更新时不能保证数据一致性

实例解决方案(分布式互斥锁)

缓存雪崩:当缓存中大量的key同时失效或redis服务崩溃时,会有大量请求直接访问到数据库,带来巨大压力

解决思路:

  • 给不同的key的TTL增加随机值
  • 设置key永不过期,加分布式锁
  • 利用redis集群提高服务的可用性(比如使用redis哨兵)
  • 给缓存业务添加降级限流策略
  • 添加多级缓存

 实例解决方案(设置key永不过期)

与分布式互斥锁相比,其实就是在缓存命中之后加一层缓存是否过期的校验,过期后就执行获取锁更新缓存的操作,没过期就直接返回

值得注意的点:

  1. 该方案中可以封装一个实体类,里面存有逻辑缓存过期时间和真实value,redis中的value就引用该对象实例
  2. 判断缓存是否过期通过拿逻辑缓存过期时间与LocalDateTime做对比
  3. 更新缓存时,获得锁后,应该开启独立线程去更新缓存,然后直接返回旧的缓存数据,目的是减少响应时间,因为设置key永不过期本身就不能保证数据完全同步
  4. 开启独立线程使用的是Executor,一套线程池管理框架,可以处理多线程操作,具体实现如下
     
       
    1. private static final ExecutorService CACHE_REBUILD_EXECUTOR =

    2. Executors.newFixedThreadPool(10);

    3. if (isLock){

    4. CACHE_REBUILD_EXECUTOR.submit( ()->{

    5. try{

    6. //重建缓存

    7. this.saveShop2Redis(id,20L);

    8. }catch (Exception e){

    9. throw new RuntimeException(e);

    10. }finally {

    11. unlock(lockKey);

    12. }

    13. });

    14. }

    AI写代码

 2、redis的五种数据类型

3、redis的最佳实践

 参考文章:

3.1、redis键值设计

①优雅的key结构 业务名称:数据名称:id 例:login:user:1

优点:可读性强,避免key冲突,方便管理,节省内存空间

3.2、bigkey和bigkey的危害

bigkey以key的成员数量和key的大小来综合评估,比如key本身数据量过大,key的成员数量过多

bigkey的危害有:

  • 网络阻塞:读取bigkey时,少量的QPS就可能宽带使用率拉满,造成网络阻塞,导致物理机和redis执行速度变慢
  • 数据倾斜:bigkey的内存使用率大,导致数据分片的内存分配不均衡
  • redis阻塞:对bigkey的数据进行一些运算操作时,耗时会比较久,就有可能导致redis进程阻塞
  • CPU压力:对bigkey进行序列化和反序列化将导致CPU的使用率飙升,给予CPU巨大压力

3.3 总结

对于key:

  • 固定格式:业务名称:数据名称:id
  • 足够简短:尽量不超过44字节,因为key是string类型,底层编码包含int、embstr和raw三种。embstr在小于44字节时使用,采用连续内存空间,内存占用更小
  • 不要包含特殊字符

对于value:

  • 合理地 拆分数据,拒绝bigkey
  • 选择合适的数据结构
  • hash的entry数量尽量不要大于500,因为hash的entry数量大于500时,会使用哈希表结构而不是ziplist,增大内存占用
  • 设置合理地过期时间

3.4 redis的更新策略

更新缓存还是删除缓存?

一般选择删除缓存,有以下好处:

 没有无效更新操作,线程安全问题小

先操作数据库还是先操作缓存

一般选择先操作数据库,理由如下:

在两个线程并发来访问时,假设线程1先来,他先把缓存删了,此时线程2过来,他查询缓存数据并不存在,此时他写入缓存,当他写入缓存后,线程1再执行更新动作时,实际上写入的就是旧的数据,新的数据被旧数据覆盖了。

而先操作数据库就不会有这种问题,安全系数更高

4、redis为什么快?

1、基于内存实现

Redis是基于内存存储的,读取数据时不需要进行磁盘IO,要比数据存储在磁盘的数据库快

2、高效的数据结构

redis中采用了许多高效的数据结构,比如

哈希表:可以保证查找操作空间复杂度是O(1)

跳表:保证查找/添加/插入/删除操作都能够在O(LogN)的复杂度内完成。

3、单线程操作,避免了不必要的线程上下文切换和竞争锁消耗,而且也不用去考虑线程安全问题,提高性能

4、高性能的多路复用IO模型

redis网络模型使用IO多路复用结合事件处理器

IO多路复用让redis在单线程的情况下也能处理多个连接请求,并且由于IO多路复用是非阻塞的,让redis能够高效网络通信

事件处理器通过事件监听和通知机制,避免了redis一直轮询关注事件进度,避免CPU浪费

并且在redis6.0中,为了提升性能,在命令回复和命令转换中使用了多线程

5、虚拟内存机制

Redis直接自己构建了VM机制 ,虚拟内存机制就是暂时把不经常访问的数据(冷数据)从内存交换到磁盘中,从而腾出宝贵的内存空间用于其它需要访问的数据(热数据)。

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记【点击此处即可】免费获取

RedisIO多路复用模型:

IO多路复用模型通过调用select/poll/epoll等函数,开启一个监听线程(selector)去监听多个socket客户端,此时监听线程会阻塞,其他非监听线程仍可以继续工作,一旦某个socket客户端有IO操作就绪,selector会解除阻塞并通知redis,Redis就会建立连接,接收数据,然后selector继续阻塞等待客户端就绪。

一条命令在Redis是如何完成执行的?

Redis Server一旦和某一客户端建立连接后,就会在事件驱动框架中注册可读事件,对应客户端的命令请求。整个命令处理的过程可分为如下阶段:

  • 命令解析,对应processInputBufferAndReplicate

  • 命令执行,对应processCommand

  • 结果返回,对应addReply

5、Redis为什么是单线程的?

首先要知道,Redis的性能瓶颈在内存和网络IO,而不是CPU

使用多线程对Redis的性能提升并不大,而多线程本身也有线程安全和上下文切换问题,所以Redis开发者选择使用单线程。

但是在Redis6.0之后,在发送响应数据中引用了多线程操作,这主要是为了提升网络IO性能

6、Redis双写一致性如何保证

Redis跟数据库双写方案需要根据业务对数据一致性的需求来决定

1、需要强一致性

  采用Redission的读写锁,读数据采用读锁,写业务采用写锁

  比如在抢券业务中,券的剩余量是需要强一致性的

2、允许延迟一致

采用异步方案同步,即写操作先写入数据库,然后异步地去修改缓存

异步方案:MQ(基于消息队列),Canal(基于mysql binlog),

7、Redis持久化方式

Redis进行持久化时,在主进程中会fork一个子进程,子进程会共享主进程的内存,并且通过内存中的页表对物理内存进行读取,然后将数据写入磁盘文件中

Redis持久化有两种方式

1、RDB(Redis DataBase Backup file)

  RDB是Redis默认持久化方式,它按照配置好的时间周期策略将内存数据以快照的方式存入磁盘,生成rbg后缀文件,可以通过修改配置文件中的save参数来配置策略

2、AOF(Append only file)

  AOF会将每一个写命令通过wirte函数追加到aof文件最后,类似于Mysql的binlog,当redis重启后,会执行文件中保存的写命令恢复数据

  当两种方式都开启时,默认执行AOF

8、Redis过期删除策略

Redis采用惰性删除+定期删除策略

惰性删除:当访问key时判断是否过期,如果过期,就将key删除

定期删除:定期检查一定量key是否过期,如果过期就删除

惰性删除的问题是,如果key过期了,但却一直没有被访问,就无法删除,造成内存资源浪费

定期删除的问题是,会有很多key过期了但无法删除,不仅占用内存,访问时还会访问到过期数据

为什么不用到期自动删除?

因为这样就需要一个定时器去监视每个key是否过期,十分浪费CPU资源

9、Redis数据淘汰策略

Redis数据淘汰一共有8种策略

比较关键的是后面四种:

如果业务中数据访问频率差距大,有冷热数据之分,建议使用lru

如果要保留置顶数据,建议使用volatile,然后将置顶数据不设置过期时间

如果业务中有很多短时间内高频访问的数据,建议使用lfu,保留这些高频数据

场景题、数据库有1000w数据,但redis只能保存20w,如何让Redis存的数据更有价值?

首先建议采用lru算法,它能有效保存经常被访问的热点数据,然后如果有需要固定一些数据,则可以使用volatile-lru,然后不给固定数据设置TTL

10、在项目中Redis的使用场景

秒杀抢购逻辑:

在项目中,我使用了Redis保存了优惠券库存信息(string)和优惠券订单信息(set)

  • 用户抢购时,首先要从Redis中获取优惠券信息,是否库存大于0,是才能继续,否则直接返回

  • 接下来从Redis中获取优惠券订单信息,判断set集合中是否有用户id,如果是说明用户已经有订单了,就直接返回。否则将库存-1,然后在订单集合中增加该用户id。

以上均在lua脚本中完成

优惠券基本信息的缓存在添加优惠券时写入,TTL设置为优惠券过期时间减去当前时间。

判断有下单资格后,通过kafka消息队列异步生成订单,然后直接返回订单Id。

在项目中,我还使用了Redission分布式锁在生成订单操作前上锁,锁的key为order:user:{userId}

这里加锁主要是一个兜底,防止一人多单,实际上lua脚本中都判断过了

更多推荐