欢迎访问shiker.tech

请允许在我们的网站上展示广告

您似乎使用了广告拦截器,请关闭广告拦截器。我们的网站依靠广告获取资金。

Spring Data Redis源码解析
(last modified May 19, 2024, 5:04 PM )
by
侧边栏壁纸
  • 累计撰写 176 篇文章
  • 累计创建 61 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

Spring Data Redis源码解析

橙序员
2024-05-19 / 0 评论 / 0 点赞 / 176 阅读 / 2,944 字 / 正在检测百度是否收录... 正在检测必应是否收录...
文章摘要(AI生成)

Spring Redis是在多个领域中展现了高效性和实用性的集成和应用。在各种场景中的应用研究表明,Redis作为高性能的键值对数据库,在提高数据读写性能和解决系统负载问题方面发挥了重要作用。特别是在处理高并发请求、提升系统响应速度和吞吐量方面,Redis显示出了其独特的优势。在Spring Boot框架下,Redis被广泛应用于缓存技术,以解决数据同步、提升搜索查询效率和实现线上无纸化信息存储等问题。使用Spring Redis需要添加依赖、配置连接信息、创建RedisTemplate Bean,并可以选择使用Spring Data Redis或Spring Cache进行数据库操作和缓存管理。总体而言,Spring Redis的集成和应用在提高系统性能和优化用户体验方面具有显著效果,是现代软件开发中不可或缺的一部分。

Spring Redis的集成和应用在多个领域中展现了其高效性和实用性。从电信运营系统、购物网站性能优化、高校Web服务器会话保持、大数据交易集市平台、企业一卡通系统、下沉市场交易平台、车辆寄售后台系统、在线教育平台、高并发Web场景下的缓存研究与设计等不同场景的应用研究表明,Redis作为一种高性能的键值对数据库,在提高数据读写性能、解决系统负荷过高问题方面发挥了重要作用。

特别是在处理高并发请求、提升系统响应速度和吞吐量方面,Redis显示出了其独特的优势。例如,在基于Nginx和Redis架构的高并发框架设计与研究中,通过利用Redis映射服务模式处理数据库,进一步提升了集群服务器的响应能力。此外,Redis集群配置管理平台的设计与实现,以及原生redis集群的优化与实现,都体现了Redis在处理大规模数据存储和高并发访问需求时的强大能力和灵活性。

在Spring Boot框架下,Redis被广泛应用于缓存技术,以解决数据同步问题、提升搜索查询效率、实现线上全程无纸化信息存储等,这些应用不仅提高了系统的运行效率,也简化了开发过程,使得系统集成更加简单快捷。

Spring Redis的集成和应用在提高系统性能、优化用户体验方面具有显著效果,是现代软件开发中不可或缺的一部分。

在我们使用spring redis时,通常需要以下步骤:

  1. 添加Redis依赖到项目的pom.xml 文件中,通常使用spring-boot-starter-data-redis依赖。
  2. 配置Redis连接信息,这可以通过编辑properties或yml配置文件来完成。
  3. 创建RedisTemplate Bean,这是进行Redis操作的关键组件。
  4. 可以选择使用Spring Data Redis进行数据库操作,或使用Spring Cache进行缓存管理。

这篇文章我们来看下Spring redis是如何运作的.

Redis命令执行-RedisTemplate

查看RedisTemplate的类关系图,我们可以看到它实现了RedisOperations接口。RedisOperations接口具有泛型参数K和V,用于指定键和值的类型。它被用于操作Redis数据库,提供了多种方法来执行Redis的操作,例如设置和获取键值对、执行事务、操作列表、集合、有序集合等。

image-20240519151153804

RedisTemplate的连接池由父类RedisAccessor提供,而RedisTemplate中则提供对应的键值对序列化器以及对应的redis命令执行器

实例初始化

RedisTemplate初始化时指定了其默认的序列化器和redis脚本执行器。其序列化器使用了JdkSerializationRedisSerializer

defaultSerializer = new JdkSerializationRedisSerializer(
       classLoader != null ? classLoader : this.getClass().getClassLoader());

其由spring默认的序列化器DefaultSerializerDefaultDeserializer提供。

除了上述两个,为了支持对redis的写入,RedisTemplate还对Redis不同数据类型的操作进行了初始化:

	//普通键值对
 	private final ValueOperations<K, V> valueOps = new DefaultValueOperations<>(this);
	//数组
	private final ListOperations<K, V> listOps = new DefaultListOperations<>(this);
	//集合
	private final SetOperations<K, V> setOps = new DefaultSetOperations<>(this);
	//流
	private final StreamOperations<K, ?, ?> streamOps = new DefaultStreamOperations<>(this,
			ObjectHashMapper.getSharedInstance());
	//有序集合
	private final ZSetOperations<K, V> zSetOps = new DefaultZSetOperations<>(this);
	//地理位置
	private final GeoOperations<K, V> geoOps = new DefaultGeoOperations<>(this);
	//HyperLogLog
	private final HyperLogLogOperations<K, V> hllOps = new DefaultHyperLogLogOperations<>(this);
	private final ClusterOperations<K, V> clusterOps = new DefaultClusterOperations<>(this);

连接redis方法-execute

RedisTemplate中实现了连接redis命令的执行方法 -execute方法​:

public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) 

其主要逻辑为:

  1. 验证模板是否已初始化和action对象是否为空。
  2. 获取Redis连接工厂和连接。调用RedisAccessor获取连接工厂。
  3. 根据是否已存在连接和是否启用事务支持,对连接进行预处理(RedisTemplate不做任何处理,留给子类拓展)。
  4. 根据是否启用管道和当前是否已使用管道,决定是否打开管道。
  5. 根据是否暴露连接,创建一个连接代理或直接使用处理后的连接执行Redis操作,并获取结果。
  6. 如果启用了管道且之前未使用管道,则关闭管道。
  7. 对结果进行后处理,并返回处理后的结果。
  8. 释放Redis连接。

Redis连接获取–RedisConnectionUtils.doGetConnection()

该方法通过TransactionSynchronizationManager对我们的RedisConnectionFactory进行管理。通过事务管理器来获取或绑定我们的redis连接:

  1. 检查当前事务或当前线程是否已绑定连接。如果是,则使用已有的连接。
  2. 如果没有找到连接,并且allowCreate参数为true,则创建一个新的连接。
  3. 如果bind参数为true,将连接绑定到当前线程。
  4. 如果事务支持开启并且存在活跃的事务,则创建一个连接的代理。
  5. 在事务完成时,通过同步机制自动释放绑定的连接。

TransactionSynchronizationManager是Spring框架中处理事务同步的类。它是通过使用线程本地变量来存储和管理与事务相关的资源和同步器的。具体功能如下:

  • 管理事务相关的资源:通过bindResourceunbindResource方法来绑定和解绑事务相关的资源,例如数据库连接等。这些资源会与当前线程绑定,确保在事务中对资源的访问是线程安全的。
  • 管理事务同步器:通过initSynchronizationregisterSynchronizationclearSynchronization方法来初始化、注册和清除事务同步器。事务同步器是在事务执行过程中执行特定操作的回调接口,例如更新缓存、发送消息等。
  • 提供事务相关信息:通过getResourceMaphasResourcegetResource等方法可以获取当前事务中绑定的资源信息;通过isSynchronizationActivegetSynchronizations等方法可以获取当前事务中注册的同步器信息。
  • 管理事务属性:通过setCurrentTransactionNamesetCurrentTransactionReadOnlysetCurrentTransactionIsolationLevel等方法可以设置当前事务的名称、只读属性和隔离级别;通过相应的get方法可以获取这些属性的值。
  • 清理线程本地变量:通过clear方法可以清除当前线程中绑定的资源、注册的同步器和事务属性等信息。
  • 总之,TransactionSynchronizationManager是Spring事务管理的重要组成部分,它提供了对事务相关资源和同步器的管理能力,以及获取事务属性信息的功能。

Redis命令执行–RedisCallback.doInRedis()

Redis命令执行和结果解析由RedisCallback.doInRedis()实现,该RedisCallback接口只提供了这一个方法,而spring redis则提供了对这个方法的两个实现:AbstractOperations中的ValueDeserializingRedisCallback内部类和DefaultStreamOperations中的RecordDeserializingRedisCallback内部类,分别提供对普通键值读写操作和流式读写操作的支持

ValueDeserializingRedisCallback具体逻辑:

// utility methods for the template internal methods
abstract class ValueDeserializingRedisCallback implements RedisCallback<V> {
    private Object key;

    public ValueDeserializingRedisCallback(Object key) {
       this.key = key;
    }
	
    //对redis中的指定key进行操作,并对返回结果进行反序列化
    public final V doInRedis(RedisConnection connection) {
       byte[] result = inRedis(rawKey(key), connection);
       return deserializeValue(result);
    }

    //提供不同写操作支持
    @Nullable
    protected abstract byte[] inRedis(byte[] rawKey, RedisConnection connection);
}

RecordDeserializingRedisCallback具体逻辑:

abstract class RecordDeserializingRedisCallback implements RedisCallback<List<MapRecord<K, HK, HV>>> {

    @SuppressWarnings("unchecked")
    public final List<MapRecord<K, HK, HV>> doInRedis(RedisConnection connection) {
	   //获取该连接的已有的数据集合
       List<ByteRecord> raw = inRedis(connection);
       if (raw == null) {
          return Collections.emptyList();
       }
       //将获取到的结果反序列化
       List<MapRecord<K, HK, HV>> result = new ArrayList<>();
       for (ByteRecord record : raw) {
          result.add(deserializeRecord(record));
       }

       return result;
    }
	
    //提供流写入的支持
    @Nullable
    abstract List<ByteRecord> inRedis(RedisConnection connection);
}

写入操作支持-AbstractOperations

AbstractOperationsRedisTemplate中用于操作Redis数据的内部抽象类。它提供了一系列方法,用于序列化和反序列化键值对、哈希表等数据,并执行Redis操作。
rawKeyrawStringrawValuerawValuesrawHashKeyrawHashKeysrawHashValue等方法用于序列化键、值和哈希键值。
deserializeValuesdeserializeTupleValuesdeserializeHashKeysdeserializeHashValuesdeserializeHashMap等方法用于反序列化值、元组、哈希键和哈希值。
deserializeKeydeserializeValuedeserializeStringdeserializeHashKeydeserializeHashValue等方法用于反序列化键、值、字符串、哈希键和哈希值。
deserializeGeoResults法用于反序列化GeoLocationGeoResults
这些方法主要用于处理Redis操作中涉及的序列化和反序列化逻辑,确保数据在Java对象和Redis存储之间的正确转换。

image-20240519160635188

设置键值对-set

我们以设置键值对为例,看操作对象是如何执行的。键值对操作由DefaultValueOperations实现:

	@Override
	public void set(K key, V value) {

		byte[] rawValue = rawValue(value);
		execute(new ValueDeserializingRedisCallback(key) {

			@Override
			protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
				connection.set(rawKey, rawValue);
				return null;
			}
		});
	}

我们可以看到,set方法首先调用rawValue方法对设置的值进行序列化,然后调用excute方法(内部直接调用了RedisTemplateexecute方法)进行redis写入命令获取对应的redis连接,由通过ValueDeserializingRedisCallback进行实际命令执行,在执行是通过重写inRedis方法,在Redis连接中完了对值的写入操作。

读取键值对-get

同样的,在此类中我们也可以找到Redis读取的操作:

	@Override
	public V get(Object key) {

		return execute(new ValueDeserializingRedisCallback(key) {

			@Override
			protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
				return connection.get(rawKey);
			}
		});
	}

其同样重写了inRedis方法,在Redis连接中完了对值的读取操作。而读取后结果的反序列化则又通过ValueDeserializingRedisCallback中的反序列化器调用实现。

显示存储-StringRedisTemplate

上面我们讲RedisTemplate的时候,说到其使用Spring中的类序列化器和反序列化进行数据的读写的,这就导致我们无法直接通过我们的原始key在redis中查到对应数据,只能通过调用rawKeyrawValue方法对键值对进行序列化后才能在redis上查找。这就导致在问题排查方面比较困难,当让我们也可以手动重新指定序列化器和反序列化器。基于这个问题,Spring提供了StringRedisTeplate来简化序列化和反序列化过程中带来的成本,官方解释其目的为:以字符串为中心的RedisTemplate 扩展。由于针对 Redis 的大多数操作都是基于字符串的,因此此类提供了一个专用类,该类可最大程度地减少其更通用 template 的配置,尤其是在序列化程序方面。

StringRedisTemplate首先在构造函数指定了序列化方式:

public StringRedisTemplate() {
    setKeySerializer(RedisSerializer.string());
    setValueSerializer(RedisSerializer.string());
    setHashKeySerializer(RedisSerializer.string());
    setHashValueSerializer(RedisSerializer.string());
}

其重写了连接预处理方法,使其返回DefaultStringRedisConnection,以支持对字符串的读写操作

protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
    return new DefaultStringRedisConnection(connection);
}
0

评论区