品牌网站建设十a小蝌蚪,建设网站去工信部备案需要什么资料,网络组建与维护试题,网站免费视频title: redis基础结构 date: 2025-03-04 08:39:12 tags: redis categories: redis笔记 Redis入门
#xff08;NoSQL, Not Only SQL#xff09; 非关系型数据库 关系型数据库#xff1a;以 表格 的形式存在#xff0c;以 行和列 的形式存取数据#xff0c;一系列的行和列被…
title: redis基础结构 date: 2025-03-04 08:39:12 tags: redis categories: redis笔记 Redis入门
NoSQL, Not Only SQL 非关系型数据库 关系型数据库以 表格 的形式存在以 行和列 的形式存取数据一系列的行和列被称为表无数张表组成了 数据库。支持复杂的 SQL 查询能够体现出数据之间、表之间的关联关系也支持事务便于提交或者回滚。
非关系型数据库以 key-value 的形式存在可以想象成电话本的形式人名key对应电话号码value。不需要写一些复杂的 SQL 语句不需要经过 SQL 的重重解析性能很高可扩展性也比较强数据之间没有耦合性需要新加字段就直接增加一个 key-value 键值对即可。
Redis 是 速度极快的、基于内存的键值型 NoSQL 数据库。
为什么这么快 完全基于内存操作。 使用非阻塞的 IO 多路复用机制。 数据结构简单对数据操作也简单。 使用单线程避免了上下文切换和竞争产生的消耗。 支持多种数据类型包括 String、Hash、List、Set、ZSet 等。
IO 多路复用机制
Redis 使用的是 IO 多路复用机制 来处理 高并发请求这使得它能在 单线程 模式下仍然保持高吞吐量。 Redis 为什么要用 IO 多路复用
Redis 是单线程的但仍然能高效处理大量连接这依赖于 IO 多路复用。传统的 阻塞 IO 方式每次只能处理一个连接性能受限。多路复用可以 同时监听多个客户端请求只处理活跃连接减少 CPU 空转。 Redis 的 IO 多路复用机制
Redis 采用 epollLinux或 selectWindows 作为 IO 多路复用技术主要使用 aeEventLoop 事件处理机制
主线程通过 epoll/select/kqueue 监听多个客户端连接当某个连接有数据可读如命令请求Redis 触发相应的回调函数回调函数读取请求处理命令返回结果继续监听新的请求不会阻塞在某个请求上
Redis 使用 事件驱动模型主要有
可读事件AE_READABLE当客户端有数据可读时触发。可写事件AE_WRITABLE当客户端可以写数据时触发。文件事件File Event通过 epoll 监听 多个 socket 连接。时间事件Time Event用于定时任务比如 key 过期检测。 Redis 多路复用示意图
[多个客户端]│▼
[epoll/select 监听]│├── 客户端 A 可读 - 触发回调 - 读取数据├── 客户端 B 可写 - 触发回调 - 发送数据├── 客户端 C 可读 - 触发回调 - 读取数据│▼
[主线程执行 Redis 命令逻辑]Redis的基础结构类型
Key结构
让 Redis 的 key 形成层级结构使用 : 隔开项目名:业务名:类型:id。
set blog:user:1 {id:1, name:Jack, age:22}
set blog:user:2 {id:2, name:Mike, age:23}
set blog:article:1 {id:1, title:Spring}String类型
KeyValueblog:user:1‘{“id”:1, “name”:“Jack”, “age”:22}’blog:user:2‘{“id”:2, “name”:“Mike”, “age”:23}’
分配策略
Java 的 String 是不可变的无法修改。Redis 的 String 是动态的可以修改的。Redis 的 String 在内部结构实现上类似于 Java 的 ArrayList采用预分配冗余空间的方式来减少内存的频繁分配。如图所示当前字符串实际分配的空间为 capacity一般高于实际的字符串长度 len。当字符串长度小于 1M 时扩容是对现有空间的成倍增长如果长度超过 1M 时扩容一次只会多增加 1M 的空间。String 的最大长度为 512M。 Hash结构 list结构
List 类似 Java 中的 LinkedList可以看作一个双向链表有序可重复。使用 List 可以对链表的两端进行 push 和 pop 操作、读取单个或多个元素、根据值查找或删除元素、支持正向检索和反向检索。
栈LPUSH LPOP 或 RPUSH RPOP。
队列LPUSH RPOP 或 RPUSH LPOP。
Set结构
SADD key member [member ...] 向 Set 中添加一个或多个元素。
SMEMBERS key 获取指定 Set 中的所有元素。
SISMEMBER key member 判断 Set 中是否存在指定元素。
SCARD key 返回 Set 中的元素个数。
SREM key member [member ...] 移除 Set 中的指定元素。
SINTER key [key ...] 求 n 个 key 间的交集。
SDIFF key [key ...] 求 n 个 key 间的差集。
SUNION key [key ...] 求 n 个 key 间的并集。
Redis 的 Set 类似 HashSet可以看作一个 value 为 null 的 HashMap其特征也与 HashSet 类似无序不可重复支持 交集、并集、差集等功能。
ZSet
Redis 的 ZSet 是一个可排序的 Set 集合类似 ZSet。ZSet 的每一个元素都带有一个 score 属性可以基于 score 属性对元素排序。
ZADD key [score member ...] 以 score 为权重向 ZSet 中添加一个或多个元素如果存在则更新 score。
ZREM key member [member ...] 删除 ZSet 中的指定元素。
ZCARD key 返回 ZSet 中的元素个数。
ZSCORE key member 获取 ZSet 中指定元素的 score 值。
ZADD key [score member ...] 以 score 为权重向 ZSet 中添加一个或多个元素如果存在则更新 score。
ZREM key member [member ...] 删除 ZSet 中的指定元素。
ZCARD key 返回 ZSet 中的元素个数。
ZSCORE key member 获取 ZSet 中指定元素的 score 值。
ZRANGEBYSCORE key min max 按照 score 排序后获取 指定 score 范围 内的元素。
ZINTER numberKeys key [key ...] ZDIFF numberKeys key [key ...] ZUNION numberKeys key [key ...] 求 n 个 Zset 的交集、差集、并集。
Redis 基础结构及其操作指令总结
基础结构描述常用指令示例String字符串最基本的数据结构可以存储字符串、整数或浮点数SET、GET、INCR、DECR、APPEND、MSET、MGETSET key valueGET keyList列表有序集合允许重复元素底层为双向链表LPUSH、RPUSH、LPOP、RPOP、LRANGELPUSH mylist A B CLRANGE mylist 0 -1Set集合无序集合不允许重复元素SADD、SREM、SMEMBERS、SISMEMBERSADD myset A B CSMEMBERS mysetHash哈希类似于对象存储键值对HSET、HGET、HGETALL、HDELHSET user name AliceHGET user nameZSet有序集合具有权重score的集合元素按分数排序ZADD、ZRANGE、ZREM、ZSCOREZADD myzset 1 A 2 BZRANGE myzset 0 -1Bitmap位图位级别的存储用于高效存储和操作二进制数据SETBIT、GETBIT、BITCOUNTSETBIT mybitmap 10 1GETBIT mybitmap 10HyperLogLog近似去重计数结构适用于大数据计数PFADD、PFCOUNTPFADD myhll A B CPFCOUNT myhllGeo地理位置存储经纬度并计算地理距离GEOADD、GEODIST、GEORADIUSGEOADD mygeo 120.0 30.0 place1GEODIST mygeo place1 place2Stream流可持久化的消息队列结构XADD、XLEN、XREADXADD mystream * name AliceXREAD COUNT 1 STREAMS mystream 0
这些结构和指令在不同的应用场景中有不同的优势比如 String 适用于缓存数据List 适用于消息队列Set 适用于去重ZSet 适用于排行榜Hash 适用于存储对象Bitmap 适用于用户签到或活跃记录HyperLogLog 适用于大规模数据去重统计Geo 适用于地理位置存储Stream 适用于事件流和消息队列。
java客户端连接redis
使用Jedis
1.导入依赖
dependencygroupIdredis.clients/groupIdartifactIdjedis/artifactIdversion3.8.0/version
/dependency2.建立连接
public class JedisTest {private Jedis jedis;BeforeEachvoid setUp(){//1.建立连接jedis new Jedis(192.168.200.130,6379);//2.设置密码jedis.auth(1234);//3.选择库jedis.select(0);}Testvoid testString(){String result jedis.set(name, 小明);System.out.println(result result);String name jedis.get(name);System.out.println(name name);}AfterEachvoid tearDown(){if(jedis!null){jedis.close();}}}
3.jedis连接池
public class JedisConnectFactory {private static final JedisPool jedisPool;static{//配置连接池JedisPoolConfig poolConfig new JedisPoolConfig();poolConfig.setMaxTotal(8);poolConfig.setMaxIdle(8);poolConfig.setMinIdle(0);poolConfig.setMaxWait(Duration.ofMillis(1000));jedisPool new JedisPool(poolConfig,192.168.200.130,6379,1000,1234);}public static Jedis getJedis(){return jedisPool.getResource();}}
1 JedisConnectionFacotry工厂设计模式是实际开发中非常常用的一种设计模式我们可以使用工厂去降低代的耦合比如Spring中的Bean的创建就用到了工厂设计模式
2静态代码块随着类的加载而加载确保只能执行一次我们在加载当前工厂类的时候就可以执行static的操作完成对 连接池的初始化
3最后提供返回连接池中连接的方法.
使用springDataRedis连接 1.导入依赖 !--Redis依赖--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependency!--连接池依赖--dependencygroupIdorg.apache.commons/groupIdartifactIdcommons-pool2/artifactId/dependency2.配置连接信息
spring:redis:host: 192.168.200.130port: 6379password: 1234database: 0lettuce:pool:max-active: 8 #最大连接数max-idle: 8 #最大空闲连接min-idle: 0 #最小空闲连接max-wait: 100 #连接等待时间3.直接注入RedisTemplate出现的问题
// 自动注入的 RedisTemplate 需要加上泛型
Resource
private RedisTemplate redisTemplate;Test
public void test() {redisTemplate.opsForValue().set(k1, v1);MapString, String map new HashMap();map.put(k2, v2);map.put(k3, v3);map.put(k4, v4);map.put(k5, v5);redisTemplate.opsForValue().multiSet(map);redisTemplate.opsForValue().multiGet(Arrays.asList(k1, k2, k3, k4)).forEach(System.out::println); // v1 v2 v3 v4 v5
}//结果
# 在 Redis 中查看通过 RedisTemplate 插入的数据keys *
1) \xac\xed\x00\x05t\x00\x02k1
2) \xac\xed\x00\x05t\x00\x02k2
3) \xac\xed\x00\x05t\x00\x02k3
4) \xac\xed\x00\x05t\x00\x02k4
5) \xac\xed\x00\x05t\x00\x02k5 get \xac\xed\x00\x05t\x00\x02k1
\xac\xed\x00\x05t\x00\x02v1RedisTemplate 存在的问题
通过以上操作可以发现RedisTemplate 可以将任意类型的数据写入到 Redis 中在写入前会将其序列化为字节形式存储底层默认采用 ObjectOutputStream 序列化。
4.因此我们要重写他的序列化工具
导入 jackson-databind 依赖并编写配置类 RedisTemplateConfig。
Configuration
public class RedisTemplateConfig {Beanpublic RedisTemplateString, Object redisTemplate(RedisConnectionFactory redisConnectionFactory) {// 创建 RedisTemplate 对象RedisTemplateString, Object redisTemplate new RedisTemplate();// 设置连接工厂redisTemplate.setConnectionFactory(redisConnectionFactory);// 设置序列化工具GenericJackson2JsonRedisSerializer jsonRedisSerializer new GenericJackson2JsonRedisSerializer();// Key 和 HashKey 采用 String 序列化StringRedisSerializerredisTemplate.setKeySerializer(RedisSerializer.string());redisTemplate.setHashKeySerializer(RedisSerializer.string());// Value 和 HashValue 采用 JSON 序列化GenericJackson2JsonRedisSerializerredisTemplate.setValueSerializer(jsonRedisSerializer);redisTemplate.setHashValueSerializer(jsonRedisSerializer);return redisTemplate;}
}// 自动注入的 RedisTemplate 需要加上泛型
Autowired
private RedisTemplateString, Object redisTemplate;Test
public void test() {redisTemplate.opsForValue().set(k1, v1);redisTemplate.opsForValue().set(user:1, new User(Jack, 21));
}
通过以上的方法能够解决数据序列化时 可读性差、内存占用大 的问题。
但是 JSON 的序列化方式仍然存在一些问题为了反序列化时知道对象的类型JSON 序列化器会将类的 class 类型写入 JSON 结果存入 Redis 中会带来额外的内存开销。
5.使用StringRedisTemplate
为了节省内存空间Spring 提供了一个 StringRedisTemplate它的 key 和 value 的序列化方式默认就是 String统一使用 String 序列化器。
当需要存储 Java 对象时手动完成对象的序列化和反序列化。
使用 StringRedisTemplate。写入数据到 Redis 中手动将对象序列化为 JSON。从 Redis 中读取数据手动将读取到的 JSON 反序列化为对象。
Autowired
private StringRedisTemplate stringRedisTemplate;private static final ObjectMapper objectMapper new ObjectMapper();Test
public void ttt() throws JsonProcessingException {User user new User(Michael, 27);// 手动序列化String json objectMapper.writeValueAsString(user);// 写入数据stringRedisTemplate.opsForValue().set(user:1, json);// 读取数据String data stringRedisTemplate.opsForValue().get(user:1);// 反序列化User deserializedUser objectMapper.readValue(data, User.class);System.out.println(deserializedUser);
}
//结果
{username: Michael,age: 27
}