做网站用啥语言,WordPress网站hym地图,网站推广公司新锐,微信不能分享wordpress背景
在Java系统实现过程中#xff0c;我们不可避免地会借助大量开源功能组件。然而#xff0c;这些组件往往功能丰富且体系庞大#xff0c;官方文档常常详尽至数百页。而在实际项目中#xff0c;我们可能仅需使用其中的一小部分功能#xff0c;这就造成了一个挑战#…背景
在Java系统实现过程中我们不可避免地会借助大量开源功能组件。然而这些组件往往功能丰富且体系庞大官方文档常常详尽至数百页。而在实际项目中我们可能仅需使用其中的一小部分功能这就造成了一个挑战如何在有限的时间和精力下高效地掌握并使用这些组件的核心功能以实现投入产出最大化
针对这一问题我基于二八原则整理编写本文。
首先我会聚焦于组件的常见和核心功能这些功能通常是我们在日常开发中频繁使用到的也是构建稳定、高效系统的基石。通过深入了解这些核心功能的使用方法和最佳实践我们可以确保在关键点上投入足够的精力从而避免在实际使用中掉入陷阱。
其次我会以问题为导向将实用性作为第一要素对组件的功能进行筛选和整理。这意味着我会优先关注那些在项目中实际需要用到的功能而对于那些特定场景下才会用到的功能我会在文中提及但不做详细展开。这样做的好处是我们可以在保证核心功能得到充分理解的同时减少不必要的阅读负担提高学习效率降低投入成本。
最后我会注重内容的精炼和易读性。通过简明扼要的文字描述和直观的示例代码帮助读者快速理解并掌握组件的核心用法。 同时我也会结合经验指出常见的问题和注意事项以便读者在使用过程中能够规避一些常见的错误和陷阱。
综上所述通过这个系列的内容整理我希望能够帮助读者在有限的时间和精力下高效地掌握并使用这些开源功能组件的核心功能满足系统实现的需要。
注部分内容章节由AI辅助生成草稿我对其进行了复核和修订修复了有问题和有错误的部分。
理论
Redis是什么
RedisRemote Dictionary Server即远程字典服务是一个开源的、使用C语言编写的、支持网络的、可基于内存亦可持久化的日志型Key-Value数据库提供多种开发语言的API能够很好地补充关系数据库在某些场合的不足并为开发者提供了丰富的数据操作选项和高效的数据处理性能。
Redis与关系型数据库的区别是什么
Redis与关系型数据库在多个方面存在显著的区别主要涉及以下几个方面 数据模型与存储方式 Redis采用键值存储Key-Value Store模型支持多种数据结构包括字符串、哈希、列表、集合、有序集合等使得数据存储和操作更加灵活。这种模型能够很好地适应各种数据存储需求且更易于扩展和修改。 关系型数据库则采用表格的形式来组织数据数据被组织成行和列的形式并通过外键关系进行关联以支持复杂的关系模型。这种结构化的数据模型有助于数据的清晰表示和查询但也可能导致数据结构相对固定不易于灵活修改。
存储机制与性能 Redis主要将数据存储在内存中通过持久化机制可选地将数据写入磁盘。这种设计使得Redis能够实现高速的读写操作特别适用于需要快速响应的场景。 关系型数据库通常将数据存储在磁盘上通过缓存等机制提高读取速度。由于磁盘I/O操作通常比内存操作慢得多因此关系型数据库的读写速度相对较慢。
事务处理 Redis支持事务其事务是基于队列实现的即创建一个事务队列然后将事务操作都放入到队列中最后依次执行但没有提供回滚机制。 关系型数据库则支持基于ACID的事务提供更严格的一致性和隔离性。这使得关系型数据库在处理复杂的事务逻辑时更加可靠。
查询语言与操作能力 Redis提供简单的键值查询和一些特定的数据结构操作命令。客观地说其职责定位也没有复杂查询的需求。 关系型数据库使用SQL作为查询语言支持复杂的查询和连接操作能够处理各种复杂的业务逻辑和数据关系。
应用场景 Redis适用于需要快速读写、对数据结构操作要求较高、需要缓存或实时分析等场景。例如它可以作为缓存层减少数据库压力提高系统性能也可以用于实现分布式锁、消息队列等功能。 关系型数据库则更适用于需要严格事务控制、支持复杂查询和关联操作的应用场景。它们在企业级应用、数据仓库等领域有着广泛的应用。
综上所述Redis与关系型数据库在数据模型、存储方式、性能、事务处理、查询语言以及应用场景等方面都存在显著差异。 关系型数据库和Redis在应用系统中往往是同时存在配合使用用于实现不同的需求应对不同的场景。
Redis有哪些特点
Redis的特点主要体现在其速度快、简单稳定等方面。由于Redis数据都是缓存在内存中并且采用单线程避免了不必要的上下文切换和竞争条件因此其读写性能非常出色官方给出的数字显示Redis的读速度可以达到110000次/s写速度可以达到81000次/s。此外Redis还支持主从同步数据可以从主服务器向任意数量的从服务器上同步从而实现了数据的备份以及读写分离。
Redis应用场景有哪些
Redis在多个场合都有广泛的应用包括但不限于用作缓存来存储热点数据、提升数据访问速度、降低数据库压力实现各种复杂的排行榜应用作为计数器记录如电商网站商品的浏览量等实现分布式锁在社交网络应用中实现点赞、关注等功能以及在多个应用之间共享数据等。
Redis支持哪些数据类型
Redis支持以下五种主要的数据类型
字符串String 是Redis最基本的数据类型一个key对应一个value。字符串类型是二进制安全的这意味着你可以把Redis的字符串类型理解成字节数组你可以对字符串类型进行很多操作例如追加、获取子串等。 哈希Hash Redis hash是一个string类型的field和value的映射表hash特别适合用于存储对象。 列表List Redis列表是简单的字符串列表按照插入顺序排序。你可以添加一个元素到列表的头部左边或者尾部右边。 集合Set Redis的集合是无序的字符串集合并且集合成员是唯一的不存在重复的元素。Redis集合是通过哈希表实现的所以添加删除查找的复杂度都是O(1)。 有序集合Sorted Set Redis有序集合和集合一样也是string类型元素的集合,并且集合成员是唯一的。不同的是每个元素都会关联一个double类型的分数(score)。Redis正是通过分数来为集合中的元素进行从小到大的排序。有序集合的成员是唯一的,但分数(score)可以重复。集合是通过哈希表实现的所以添加删除查找的复杂度都是O(1)。
这些数据类型为Redis提供了丰富的功能集使其可以灵活地处理各种类型的数据和应用场景。
实际上使用最多的类型还是字符串。
哪些数据需要放到Redis缓存
一般来说放到缓存中的往往是更新频率低大量读取的数据。 对于应用系统而言主要分为两类 一是基础数据如系统的配置参数数据字典性别、证件号码等。 二是业务数据如某城市的天气预报数据电商系统中的秒杀商品。
使用缓存常见误区是什么
缓存数据没有及时刷新。 将数据放入缓存意味着内存中多了一份数据库中的数据副本。应用系统会优先从缓存中读取。若数据库中的数据被更新则需要主动将其更新到缓存否则业务上读取到的就是陈旧的错误数据在大多数情况下是不可接受的。 比如秒杀商品变更了价格用户看到的是缓存中的尚未改变的价格下单支付时才发现价格变了。
实战
Redis如何安装
服务器端往往是Linux或Docker环境如何安装部署根据环境操作即可。 开发环境大多都是windows中建议使用exe安装会自动将其注册为windows服务。 通过操作系统的服务管理面板可进行灵活控制启停及开机自动运行。 Redis的日志目录和配置文件参见安装路径在不清楚各参数含义的情况下保持默认即可如下图
Redis的读写库应该选哪个
java中进行redis读写操作的库主要有两个一个是Jedis另外一个是lettuce。 我们应该选哪个呢 先来看下简介。 Jedis是老牌的Redis的Java实现客户端提供了比较全面的Redis命令的支持。 Lettuce高级Redis客户端用于线程安全同步异步和响应使用支持集群Sentinel管道和编码器。 好像从简介中也看不出谁优谁劣再进一步看下技术实现。 Jedis使用阻塞的I/O且其方法调用都是同步的程序流需要等到sockets处理完I/O才能执行不支持异步。Jedis客户端实例不是线程安全的所以需要通过连接池来使用Jedis。 Lettuce基于Netty框架的事件驱动的通信层其方法调用是异步的。Lettuce的API是线程安全的所以可以操作单个Lettuce连接来完成各种操作。 这时候就看出来差别来了从技术实现上明显Lettuce更胜一筹。
使用jedis库去操作redis其流程跟访问关系型数据库非常像即创建一个连接池然后从连接池中获取一个连接进行读写操作最后再关闭连接将连接归还给连接池。
Slf4j
public class JedisUtil {Autowiredprivate JedisPool jedisPool;/*** 存入Redis缓存** param key* param value*/public void set(String key, String value) {Jedis jedis null;try {jedis jedisPool.getResource();jedis.set(key, value);} catch (Exception ex) {log.error(存储Redis出错 ex);} finally {if (jedis ! null) {jedis.close();}}}
}因此相比Jedis更推荐使用Lettuce。
如何在SpringBoot项目中使用
可以在SpringBoot项目中直接使用Lettuce但是更佳的方式是使用spring-data-redis。 spring-data-redis对redis底层开发包(Jedis、lettuce等 )进行了高度封装统一由RedisTemplate提供了redis各种操作、异常处理及序列化工作。 也就是说可以将spring-data-redis视作抽象的接口对redis的读写可以灵活更换为具体的客户端如jedis或lettuce。这种设计方式挺常见的比如slf4j提供日志门面接入logback、log4j2等实现日志功能。 使用spring-data-redis而不是直接使用lettuce等客户端一方面封装后的组件往往比未封装的组件更易用另一方面当需要更换组件时易于实现应用系统的代码无需调整。
添加哪些依赖
在SpringBoot项目的pom文件中添加如下依赖
!-- redis缓存 --
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId
/dependency
dependencygroupIdorg.apache.commons/groupIdartifactIdcommons-pool2/artifactId
/dependencySpringBoot从2.0版本开始将spring-boot-starter-data-redis内置的jedis更换为lettuce也间接说明了后者更优。 注意这里面有个坑点lettuce内部使用了apache的连接池但并没有强依赖因此需要单独引入commons-pool2。
如何配置
Spring Boot为Redis提供了自动配置的功能使得我们可以很容易地通过配置文件通常是application.yml或application.properties来设置Redis的相关参数。 在yml中配置redis及lettuce参数如下所示
spring: redis:host: localhostport: 6379password:#新版本Redis的timeout是一个duration需使用如下写法timeout: 10sdatabase: 0lettuce:pool:# 连接池中的最小空闲连接min-idle: 2# 连接池中的最大空闲连接max-idle: 2# 连接池的最大连接数max-active: 16#连接池最大阻塞等待时间max-wait: 30s
host 说明指定Redis服务器的地址。 值localhost 表示Redis服务器运行在本机上。 port 说明指定Redis服务器的端口号。 值6379 是Redis的默认端口号。 password 说明指定连接Redis服务器所需的密码。 值此处为空表示没有设置密码。如果Redis服务器设置了密码需要在这里填写。 timeout 说明指定连接Redis服务器的超时时间。 值10s 表示超时时间为10秒。在新版本的Redis中timeout已经是一个duration所以使用“s”来表示秒。 database 说明指定使用的Redis数据库索引。 值0 表示使用Redis的第一个数据库。Redis默认提供了16个数据库索引从0到15。
以下是Lettuce连接池的配置部分 min-idle 说明连接池中的最小空闲连接数。 值2 表示连接池中至少保持2个空闲连接。 max-idle 说明连接池中的最大空闲连接数。 值2 表示连接池中最多可以保持2个空闲连接。 max-active 说明连接池的最大连接数。 值16 表示连接池中最多可以有16个活跃连接。 max-wait 说明当连接池中没有可用连接时获取连接的最大阻塞等待时间。 值30s 表示如果连接池中没有可用连接获取连接的线程会最多等待30秒。
这些配置为Spring Boot应用程序提供了连接Redis服务器的所有基本信息以及关于连接池的行为参数。通过调整这些参数可以优化Redis的使用性能满足应用程序的不同需求。
如何读写Redis
spring-data-redis组件使用RedisTemplate来进行操作自行封装的一个工具类如下所示
package tech.abc.platform.common.utils;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;/*** 缓存工具类** author wqliu* date 2023-03-06*/
Component
Slf4j
public class CacheUtil {Autowiredpublic RedisTemplate redisTemplate;/*** 设置缓存对象** param key 缓存的键* param value 缓存的值*/public T void set(String key, T value) {redisTemplate.opsForValue().set(key, value);}/*** 设置缓存对象附带设定有效期** param key 缓存的键值* param value 缓存的值* param timeout 时间* param timeUnit 时间单位*/public T void set(String key, T value, long timeout, TimeUnit timeUnit) {redisTemplate.opsForValue().set(key, value, timeout, timeUnit);}/*** 获取缓存对象** param key 缓存键值* return 缓存键值对应的数据*/public T T get(String key) {ValueOperationsString, T operation redisTemplate.opsForValue();return operation.get(key);}/*** 删除缓存对象** param key 缓存的键*/public boolean remove(String key) {return redisTemplate.delete(key);}/*** 从redis缓存中移除指定前缀的所有值*/public void removePrefix(String prefix) {Set keys redisTemplate.keys(prefix *);redisTemplate.delete(keys);}/*** 设置缓存对象的有效期** param key 缓存的键值* param timeout 时间* param timeUnit 时间单位*/public T void expire(String key, Integer timeout, TimeUnit timeUnit) {redisTemplate.expire(key, timeout, timeUnit);}/*** 批量存入缓存** param cachedMap*/public void setBatch(MapString, String cachedMap) {for (String key : cachedMap.keySet()) {set(key, cachedMap.get(key));}}}
可以看出来使用封装后的RedisTemplate要比原生的jedis方便得多一句代码就能实现读或写而jedis更像是访问关系型数据库的模式需要先从连接池中获取1个连接然后执行读或写操作最后再关闭连接。
Redis工具查看数据异常怎么解决
经过上述步骤通过系统读写是没问题但是使用redis客户端工具直连redis服务器查看数据时则会显示多了一些不可读的前缀\xac\xed\x00\x05t\x00\这是另外一个坑点。 这是怎么出现的呢原来lettuce默认使用JdkSerializationRedisSerializer作为序列化与反序列化的工具将字符串转换为字节数组搞出来的幺蛾子。
怎么解决呢知道原因了解决思路也有了搞一个配置类将默认的序列化与反序列的类替换掉。
Configuration
public class RedisConfig {Beanpublic RedisTemplateObject, Object redisStringTemplate(RedisTemplateObject, Object redisTemplate) {StringRedisSerializer stringRedisSerializer new StringRedisSerializer();redisTemplate.setKeySerializer(stringRedisSerializer);redisTemplate.setValueSerializer(stringRedisSerializer);return redisTemplate;}}StringRedisSerializer是spring-data提供的同时还提供了负责json数据的处理类可用于键值为json的场景。
场景
缓存数据字典
将应用系统中的下拉列表中的字典数据如性别、证件类型在系统启动时从数据库写入到Redis缓存。 在使用这些字典作为属性的业务实体时如查询用户读取Redis将编码值转换为文本返回给前端从而避免了大量数据库读操作。
自助重置密码
用户忘记密码时提供自助方式让用户输入注册时的邮箱地址然后生成一个uuid一方面将该uuid作为键值存入到redis并设置24小时有效另一方面将uuid作为参数附加到自助重置密码的地址上作为邮件内容发送到用户邮箱。 用户点击邮件中的链接地址时系统获取到uuid去Redis中查询键是否存在若不存在则认为链接已超出时效。 这里是利用了Redis自身支持时效的特点来简化开发工作相比自己在关系型数据库中建表来保存失效时间验证环节自行比较。 同理用户登录环节验证码5分钟有效也可以采用这种方案。
扩展
集群模式有哪些
Redis集群模式主要有三种分别是主从复制模式、哨兵模式Sentinel和Cluster模式。
主从复制模式 主从复制模式是最简单的集群方式。它使用一个Redis实例作为主机master其他实例作为备份机slave。主机负责数据的写入和读取操作而从机只支持与主机数据的同步和读取操作。当主机发生故障时需要人工介入将某个从机提升为新的主机。 优点提高了服务器性能实现了读写分离。 缺点故障恢复效率较低需要人工介入。
哨兵模式Sentinel 哨兵模式通过一组哨兵实例组成的哨兵系统来监视主从节点的健康状态。一旦主节点故障被侦测到系统会自动选举出一个从节点晋升为新的主节点从而实现故障恢复的自动化。 优点提高了系统的高可用性故障恢复更加自动化提高了系统的稳定性和可靠性。 缺点内存容量和写入性能仍受限于单个节点。
Cluster模式 Cluster模式通过数据分片sharding和多节点水平扩展有效提高了内存利用率和写入性能适用于更大规模和更高要求的数据处理场景。Redis Cluster将数据分为16384个槽位每个节点负责管理一部分槽位。当客户端向Redis Cluster发送请求时Cluster会根据键的哈希值将请求路由到相应的节点。具体来说Redis Cluster使用CRC16算法计算键的哈希值然后对16384取模得到槽位编号。 优点为Redis集群的性能和扩展性提供了重要的支撑。 缺点复杂度高 在实际应用中可以根据具体的业务需求和场景来选择合适的Redis集群模式。如果需要更高级别的自动化故障恢复和更好的性能可以考虑使用哨兵模式或Cluster模式。如果只是简单的读写分离和备份需求主从复制模式可能是一个不错的选择。
如果应用规模有限用户量比较小单机模式也足够用了redis自身的稳定性相当高没必要集群。
Redis如何实现持久化
Redis的持久化模式主要有三种快照方式RDB, RedisDataBase、文件追加方式AOF, AppendOnlyFile以及混合持久化方式。
快照方式RDB 原理该方式将某个时刻的内存数据以二进制的方式写入磁盘。由于是二进制写入效率较高。 优点RDB文件为二进制数据占用内存小且紧凑适合作为备份文件。同时RDB持久化方式可以最大化Redis的性能因为父进程在保存RDB文件时只需要fork出一个子进程接下来的工作全部由子进程完成父进程不需要进行其他IO操作。 缺点当Redis意外终止时可能会导致数据部分丢失。此外RDB需要经常fork子进程来保存数据集到硬盘上当数据集较大时fork过程可能会非常耗时导致Redis在一段时间内无法响应客户端请求。 快照方式不会堵塞主进程。
文件追加方式AOF 原理该方式记录所有的操作命令并以文本的形式追加到文件中。由于是以文本形式写入效率相对较低但保证了数据的完整性。 优点由于AOF记录的是操作指令因此可以确保数据的完整性。 缺点由于AOF是以文本形式写入其效率不如RDB。同时AOF文件通常会比RDB文件大需要更多的磁盘空间。 AOF是先写redis再追加日志都在主进程中所以追加日志操作实际会堵塞主进程。此外若在写redis成功写日志还没完成这时候宕机通过AOF来恢复会丢数据。
混合持久化方式 原理Redis 4.0之后新增的方式结合了RDB和AOF的优点。在写入时先把当前数据以RDB形式写入文件的开头再将后续的操作命令以AOF的格式存入文件。 优点这种方式既能保证Redis重启时的速度又能降低数据丢失的风险。
在选择持久化策略时需要根据实际的应用场景和需求来权衡。例如如果更看重数据的完整性和可恢复性可以选择AOF或混合持久化方式如果更看重性能和磁盘空间的使用效率可以选择RDB方式。
需要注意的是在使用Redis的持久化功能时应定期检查和备份持久化文件以防止数据丢失或损坏。同时也应对Redis服务器进行监控和维护确保其正常运行和数据的安全性。
什么是缓存穿透
缓存穿透是指查询一个根本不存在的数据由于缓存也没有该数据每次请求都会直接打到数据库上而数据库中也没有该数据相当于进行了两次无效的查询。常见的场景是并发查询不存在的key时由于缓存未命中每次请求都会直接查询数据库造成数据库压力骤增甚至宕机。
为了避免缓存穿透带来的问题可以采取以下策略
布隆过滤器将所有可能存在的数据哈希到一个足够大的bitmap中一个一定不存在的数据会被这个bitmap拦截掉从而避免了对底层存储系统的查询压力。 缓存空对象当存储层不命中后即使是一个空对象或默认值也将其缓存起来这样后续再查询同样的key时就直接返回缓存中的空对象或默认值而不需要再去存储层查询。但这种方式需要特别注意缓存的失效时间和存储空间的问题。 限制访问频率针对同一个key的访问如果一段时间内访问频率过高可以考虑加入访问队列或使用滑动窗口限制访问频率从而减少对数据库的查询压力。
在实际应用中需要根据具体的业务场景和需求来选择合适的缓存穿透应对策略。
什么是缓存击穿
缓存击穿是指当一个热点即访问非常频繁的key在缓存中失效过期的瞬间大量的并发请求同时访问这个key时由于缓存中没有该key的数据这些请求都会直接穿透到数据库导致数据库瞬间承受巨大的压力。 简单来说就是数据库中有值而缓存中没有值。 为了避免缓存击穿问题通常有以下几种解决策略 使用互斥锁当多个线程同时访问失效的key时可以使用互斥锁如Redis的分布式锁来确保只有一个线程去数据库查询并重建缓存其他线程则等待锁释放后访问新缓存。 设置热点key永不过期如果某个key的访问非常频繁且数据基本不会更新可以考虑将其设置为永不过期避免其过期导致的缓存击穿。
主动更新缓存对于更新频率较高的数据可以利用定时线程在缓存过期前主动重新构建缓存或者在缓存过期时延后一段时间再过期以确保缓存始终有效。
注意上面说的的互斥锁方案加锁排队只是为了减轻数据库的压力并没有提高系统吞吐量。假设在高并发下缓存重建期间 key 是锁着的这是过来 1000 个请求 999 个都在阻塞的同样会导致用户等待超时。
需要注意的是在处理缓存击穿问题时应该结合具体的业务场景和需求来选择最合适的解决方案并在实施后进行充分的测试以确保其有效性和性能。
什么是缓存雪崩
缓存雪崩是指当大量缓存同时失效或过期后系统无法从缓存中获取数据转而请求数据库导致数据库承受巨大压力进而引起系统性能急剧下降的情况。 缓存击穿是对于单个key值失效来了大量并发请求缓存雪崩是大量key值在同一时间点或短暂的时间段失效。 具体来说当缓存中的数据失效或被清除时系统需要再次访问数据库重新计算并生成缓存数据。这个处理过程通常耗时较长可能达到上百毫秒甚至更久。对于高并发的系统而言在缓存失效的这段时间内系统会接收到大量的请求。由于旧的缓存已经失效且新的缓存尚未生成这些请求都会直接访问数据库从而导致数据库承受巨大的访问压力。如果这种压力超过了数据库的承受能力就可能引发数据库宕机进而使整个系统崩溃。
为了避免缓存雪崩可以采取以下策略 保持缓存层的高可用性使用Redis哨兵模式或集群部署方式确保即使个别Redis节点故障整个缓存层依然可用。 优化缓存过期时间为缓存中的每个key设置合适的过期时间避免大量key在同一时刻同时失效。有一个简单方案就是将缓存失效时间分散开比如我们可以在原有的失效时间基础上增加一个随机值比如 1-5 分钟随机这样每一个缓存的过期时间的集中程度就会降低很难引发集体失效的事件。
综上所述缓存雪崩是一个需要认真对待的问题合理的缓存策略和管理机制对于维护系统的稳定性和性能至关重要。