Redis详细笔记

简介:

Redis是基于内存可持续化的分布式非关系型数据库,结构化数据: 将一批数据集合抽取共有特点形成结构–学生信息,人体信息.mysql中每一张表格都是结构化数据,redis不仅支持像mysql结构化查询,也支持非结构化数据:无法公用一批共有特点的数据集合–如日志数据.网页

redis是采用key-value存储数据::redis支持的基本结构,对key值是字符串,value有五种不同的内部数据结构:string,hash,list,set,zset;

可持久化: redis是主要将数据存储在内存中(特点:快,可靠性不高),为了防止内存数据宕机丢失,创建的持久化功能,可以根据需求,将内存数据保存在磁盘中,一旦故障,可以实现数据的恢复功能.

支持分布式:redis服务端是单例单线程的,利用线程非阻塞原理实现高吞吐量(1秒钟万条数据的读写操作).单个的redis节点(启动的一个server进程)不足以实现某个服务器的cpu性能,redis都是以集群节点的结构存在的(分布式的,高可用的结构)

雪崩/缓存击穿:当某个一个功能,运转流程中包含了缓存数据库的技术(有大量的缓存数据),一旦缓存数据丢失,海量请求涌入访问,造成缓存数据未命中,海量请求涌入数据库,造成数据库压力过大–宕机–重启–海量请求未消失–宕机;

redis应用场景::
• 系统的缓存
• 内存的数据锁结构解决各种复杂的逻辑判断
• 队列(list)
• 标签(某个用户访问网站收集的兴趣爱好,社交网站共同好友) • 排行系统(热搜的排行,点击量排行)

数据操作

Redis基础命令+五种类型的命令
操作数据:redis具备五种数据类型,每种数据类型都是针对key-value结构中的value而言,对于系统生成value各种数据的不同实现场景做对应
String
Hash
List
Set
Zset

String类型(包含基础命令)

在这里插入图片描述
**keys *** :查看当前redis节点(启动的每一个redis服务都叫redis节点。)所有已存在的key值。

127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> set name hanlaoshi
OK
127.0.0.1:6379> set location beijing
OK
127.0.0.1:6379> keys *
1) "location"
2) "name"

**set key value:**存储一个key-value结构的数据
get key:获取当前key对应的value数据

127.0.0.1:6379> get age
"22"
127.0.0.1:6379> set age 33
OK
127.0.0.1:6379> get age
"33"
127.0.0.1:6379>

select 整数:redis中默认存在16个数据分库(database),index号0-15,在一个服务器节点上,可以区分多种类型,多种功能的数据库仓库,默认登录的是0号分库(现在使用不多)

127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> keys *
(empty list or set)
127.0.0.1:6379[1]> select 0
OK
127.0.0.1:6379> keys *
1) "location"
2) "age"
3) "name"
127.0.0.1:6379>

exists key:判断当前节点是否包含key的数据。get也可以完成这个任务。redis在key-value结构的value存储容量是512M。使用get判断存在会先读在判断数据是否为空,浪费资源

127.0.0.1:6379> exists haha kaka age
(integer) 1
127.0.0.1:6379> exists age
(integer) 1
127.0.0.1:6379> exists haha
(integer) 0
127.0.0.1:6379>

del key:删除key值对应的key-value数据;

127.0.0.1:6379> del age
(integer) 1
127.0.0.1:6379> del haha
(integer) 0
127.0.0.1:6379> keys *
1) "location"
2) "name"
127.0.0.1:6379>

type key:查看当前key的类型

127.0.0.1:6379> type name
string

save:将当前的内存数据保存到磁盘文件(根目录dump.rdb)

127.0.0.1:6379> keys *
1) "location"
2) "name"
127.0.0.1:6379> save
OK

save前,save后观察redis根目录的dump.rdb大小可以发现已经存储到持久化文件

flushall:将当前redis所有数据清空,包括持久化文件,内存数据(创建集群的时候使用)在这里插入代码片

127.0.0.1:6379> keys *
1) "location"
2) "name"
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> set name wanglaoshi
OK
127.0.0.1:6379[1]> set age 11
OK
127.0.0.1:6379[1]> keys *
1) "age"
 分区 day06 的第 6 页 
1) "age"
2) "name"
127.0.0.1:6379[1]> save
OK
127.0.0.1:6379[1]> flushall
OK
127.0.0.1:6379[1]> keys *
(empty list or set)
127.0.0.1:6379[1]> select 0
OK
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379>

flushdb:清空一个分库的数据,不删除持久化文件内容(进行测试使用)

127.0.0.1:6379[1]> flushdb
OK
127.0.0.1:6379[1]> keys *
(empty list or set)
127.0.0.1:6379[1]> select 0
OK
127.0.0.1:6379> keys (
(empty list or set)
127.0.0.1:6379> keys *
1) "name"

redis中string字符串如果是纯数字,也有一些简单的数字操作命令
incr decr key:自增1,自减1;计步器

127.0.0.1:6379> set num 100
OK
127.0.0.1:6379> incr num
(integer) 101
127.0.0.1:6379> decr num
(integer) 100

多步计步器:incrby decrby key 整数:对key的value数字自增自减去整数步数

127.0.0.1:6379> incrby num 10
(integer) 110
127.0.0.1:6379> decrby num 10
(integer) 100

mset mget:单节点批量操作(不支持多节点分布式,只能对本节点数据进行处理)(测试时确定当前节点拥有的数据)

127.0.0.1:6379> mset num1 200 num2 300 
num3 400
OK
127.0.0.1:6379> keys *
1) "num3"
2) "name"
3) "num"
4) "100"
5) "num1"
6) "num2"
127.0.0.1:6379> mget num1 num2 num3
1) "200"
2) "300"
3) "400"

redis作为内存缓存数据库虽然速度快,还是有缺点的,内存容量,不可能比硬盘容量大的.redis最常见的保护内存的机制(热点数据保存,冷点数据超时清除

expire key:对key添加超时过期的设定配合ttl key可以查看 key值剩余时间,已经超时的内容删除,ttl剩余时间是-2 -1表示永久数据

127.0.0.1:6379> ttl num1
(integer) -2
127.0.0.1:6379> keys *
1) "num3"
2) "name"
3) "num"
4) "100"
5) "num2"
127.0.0.1:6379> ttl name
(integer) -1

append key value :对key对应的value进行追加数据操作

Hash数据类型

对应value为hash结构的数据都是面向对象的数据结构
在这里插入图片描述
hset key field value:key 是 相当于变量名,filed是value中的key-value对中的key,但是面向对象时表示一个对象的属性名称, value表示属性的值

127.0.0.1:6379> hset user username hanlaoshi
(integer) 1
127.0.0.1:6379> hset user age 18
(integer) 1
127.0.0.1:6379> hset user location beijing
(integer) 1
127.0.0.1:6379> type user
hash

hget key field
127.0.0.1:6379> hget user username
"hanlaoshi"

hexists key field:判断hash类型中的属性值是否存在

127.0.0.1:6379> hexists user haha
(integer) 0
127.0.0.1:6379> hexists user age
(integer) 1
(integer) 1

hmset和hmget:批量设置,不支持分布式

127.0.0.1:6379> hmset student age 19 name 
wangxiaoxiao gender male
OK
127.0.0.1:6379> hmget student age name gender
1) "19"
2) "wangxiaoxiao"
3) "male

Hdel key field:删除属性和值

127.0.0.1:6379> hdel student age
(integer) 1
127.0.0.1:6379> hget student age
(nil)
``
**Hkeys Hvals**:单独获取对象的属性名,或者属性的值`
![在这里插入图片描述](https://img-blog.csdnimg.cn/2020040311034097.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM0ODAwOTg2,size_16,color_FFFFFF,t_70)

```bash
127.0.0.1:6379> hkeys user
1) "name"
2) "username"
3) "age"
4) "location"
127.0.0.1:6379> hvals user
1) "haha"
2) "hanlaoshi"
3) "18"
4) "beijing"

Hlen key:获取属性个数(长度)

127.0.0.1:6379> hlen user
(integer) 4
127.0.0.1:6379> hlen student
(integer) 2

List链表数据结构

在这里插入图片描述
查看链表list和设置链表数据(左表示上,右表示下)
lpush key value [vlaues]:对于一个链表,从左(上)开始插入数据;

lrange key 起始下标 结束下标(展示范围,结束下
标是-1表示到尾部)

127.0.0.1:6379> lpush mylist01 100 200 300
(integer) 3
127.0.0.1:6379> lrange mylist 0 1
(empty list or set)
127.0.0.1:6379> lrange mylist01 0 1
1) "300"
2) "200

Rpush key value:从下向上插入数据

127.0.0.1:6379> rpush mylist01 400
(integer) 4
127.0.0.1:6379> lrange mylist01 0 -1
1) "300"
2) "200"
3) "100"
4) "400

Linsert:从链表中间操作数据,从左侧找到第一个
相同元素插入数据(before after)

127.0.0.1:6379> linsert mylist01 before 100 
one
(integer) 5
127.0.0.1:6379> lrange mylist01 0 -1
1) "300"
2) "200"
3) "one"
4) "100"
5) "400"
127.0.0.1:6379> linsert mylist01 after 100 two
(integer) 7
127.0.0.1:6379> lrange mylist01 0 -1
1) "100"
2) "two"
3) "300"
4) "200"
5) "one"
5) "one"
6) "100"
7) "400"

lset key index value将下标对应的数据修改

127.0.0.1:6379> lset mylist01 1 2
OK
127.0.0.1:6379> lrange mylist01 0 -1
1) "100"
2) "2"
3) "300"
4) "200"
5) "one"
6) "100"
7) "400"

**Lrem:**这个没有rrem,内部逻辑涉及到了从下往上删除
lrem key count value:从左向右寻找count个在这里插入代码片value相同值元素删除。
count>0从左往右删
count=0 全删
count<0从右往左删

127.0.0.1:6379> lpush mylist01 200 200 200
(integer) 9
127.0.0.1:6379> lrange mylist01 0 -1
1) "200"
2) "200"
3) "200"
4) "2"
5) "300"
5) "300"
6) "200"
7) "one"
8) "100"
9) "400"
127.0.0.1:6379> lrem mylist01 -2 200
(integer) 2
127.0.0.1:6379> lrange mylist01 0 -1
1) "200"
2) "200"
3) "2"
4) "300"
5) "one"
6) "100"
7) "400"
127.0.0.1:6379> lrem mylist01 0 200
(integer) 2

ltrim 保留链表的范围内的数据元素,ltrim key 起始位置 结束位置(-1表示到尾部)

127.0.0.1:6379> ltrim mylist01 4 6
OK
127.0.0.1:6379> lrange mylist01 0 -1
1) "400
``lpop 从list头部删除元素,返回删除结果(类似remove) 秒杀逻辑,可以利用list对象实现阻止超卖现象`

```bash
127.0.0.1:6379> lrange mylist01 0 -1
1) "six"
2) "five"
2) "five"
3) "four"
4) "three"
5) "two"
6) "one"
7) "400"
127.0.0.1:6379> lpop mylist01
"six"
127.0.0.1:6379> lrange mylist01 0 -1
1) "five"
2) "four"
3) "three"
4) "two"
5) "one"
6) "400"

两个链表的数据交互
rpoplpush:从第一个list的尾部删除数据,移动到第二个list的头部添加

127.0.0.1:6379> rpoplpush mylist01 mylist02
"400"
127.0.0.1:6379> lrange mylist02 0 -1
1) "400"
2) "5"
3) "4"
4) "3"
5) "2"
6) "1

Set结构

集合用来保存多个字符串元素,和列表不同的是不允许有重复元素,并且集合中元素是无序的

在这里插入图片描述
最常见的应用场景,比如爱好,兴趣,社交平台中关注人存在一个集合中,将其所有粉丝存在一个集合

单集合操作
sadd key member添加

127.0.0.1:6380> sadd favor histroy english 
math
(integer) 3

srem key member删除

127.0.0.1:6380> srem favor histroy
(integer) 1

scard key 返回元素个数

127.0.0.1:6380> scard favor
(integer) 2

sismember key element判断元素是否在集合中

127.0.0.1:6380> sismember favor histroy
(integer) 0
127.0.0.1:6380> sismember favor math
(integer) 1

srandmember key 随机抽取几个元素

127.0.0.1:6380> sismember favor math
(integer) 1
127.0.0.1:6380> srandmember favor 1
1) "math"
127.0.0.1:6380> srandmember favor 1
1) "english

smembers key获取所有元素

127.0.0.1:6380> sadd favor english histry
(integer) 2
127.0.0.1:6380> smembers favor
1) "english"
2) "histry"
3) "math"

集合间的操作
sinter set1 set2 交集

在这里插入代码片127.0.0.1:6380> sadd favor1 english histroy 
math
(integer) 3
127.0.0.1:6380> sadd favor2 english math 
chinese
(integer) 3
127.0.0.1:6380> sinter favor1 favor2
1) "english
2) "math"

suinon set1 set2 并集

127.0.0.1:6380> sunion favor1 favor2
1) "english"
2) "chinese"
3) "histroy"
4) "math

sdiff set1 set2 差集属于set1不属于set2的元素
为差集

127.0.0.1:6380> sdiff favor1 favor2
1) "histroy"
127.0.0.1:6380> sdiff favor2 favor1
1) "chinese"
127.0.0.1:6380>

ZSet结构(sorted set)

在集合的基础上绑定一个score作为排序的依据比较典型的使用场景是排行榜系统。例如视频网站需要对用户上传的视频做排行榜
在这里插入图片描述
单集合操作
zadd key score memeber添加成员

127.0.0.1:6380> zadd result 10 xiaolaoshi
(integer) 1
127.0.0.1:6380> zadd result 20 wanglaoshi
(integer) 1
127.0.0.1:6380> zadd result 30 chenlaoshi
(integer) 1

zscore key member计算某个成员的分数

127.0.0.1:6380> zadd result 30 chenlaoshi
(integer) 1
127.0.0.1:6380> zscore result chenlaoshi
"30"
127.0.0.1:6380> zadd result 40 chenlaoshi
(integer) 0
127.0.0.1:6380> zscore result chenlaoshi
"40"

计算成员的排名 zrank key member

127.0.0.1:6380> zrank result xiaolaoshi
(integer) 0
127.0.0.1:6380> zrank result wanglaoshi
(integer) 1

删除成员 zrem key member

127.0.0.1:6380> zrem result wanglaoshi
(integer) 1
127.0.0.1:6380> zrank result wanglaoshi
(nil)

增加成员的 分数zincrby key increment member

127.0.0.1:6380> zincrby result 50 xiaolaoshi
"60

返回指定排名范围的成员 zrange key start end

返回指定排名范围的成员 zrange key start end
127.0.0.1:6380> zrange result 0 1
1) "chenlaoshi"
2) "xiaolaoshi"

返回指定分数范围的成员 zrangebysore key min max
返回指定分数范围成员个数 zcount key min max
删除指定排名内的升序元素 zremrangebyrank key start end

127.0.0.1:6380> zremrangebyrank result 0 1
(integer) 2
127.0.0.1:6380> zrange result 0 1
(empty list or set)

删除指定分数范围的成员 zremrangebyscore key min max key min max

集合间的操作
交集 zinterstore destination numkeys key 制定一个交集并集查询结果的输出集合desitination指定查询交集并集的总zset个数numkeys ,给定符合数量的多个zset集合

127.0.0.1:6380> zadd result1 10 xiao
(integer) 1
127.0.0.1:6380> zadd result1 20 wang
(integer) 1
127.0.0.1:6380> zadd result2 30 xiao
(integer) 1
127.0.0.1:6380> zadd result2 40 wang
(integer) 1
127.0.0.1:6380> zinterstore out1 2 result1 
result2
(integer) 2
127.0.0.1:6380> zrange out1 0 -1 withscores
1) "xiao"
2) "40"
3) "wang"
4) "60

并集 zunionstore destionation numkeys key

redis多实例部署

单实例单线程的redis进程不足以高效率使用cpu和内存资源,所以一般来讲redis在同一台机器上要启动多个进程完成多实例部署;默认占用6379的情况下无法完成直接的3个实例启动,这里我们需要了解如何通过指定配置文件,将多实例部署在linux上

启动redis服务的命令redis-server 没有加载任何配置文件指定各种各样的配置信息(端口指定,ip绑定,后台运行)
例如在根目录存在一个配置文件的模板(大部分与默认启动的配置相同)redis.conf

启动默认配置结点
#redis-server 配置文件的名称
/redis根目录/redis.conf
在这里插入图片描述
redis中的数据单位解释

在这里插入图片描述
在这里插入图片描述

配置文件配置

bind 用#注释掉
在这里插入图片描述
如果需要绑定监听的ip(客户端只有通过被绑定的ip才可以利用redis-cli -h ip地址链接服务器)
bing 127.0.0.1 106.75.101.219(外网可访问当前服务器的ip)

一旦用#注释bind,没有任何限制,只要可以链接服务器,都允许使用

保护模式不启动
在这里插入图片描述保护模式开启,需要登录密码,改成no

6379是默认端口(要启动其他的redis实例需要修改端口)
在这里插入图片描述
当客户端空闲时间达到一小时,就会自动断开连接,0秒表示不启用超时配置
在这里插入图片描述
daemonize 设置成yes让redis服务器启动有守护进程管理
在这里插入图片描述
对应不同的redis实例,pid的文件名称需要和端口同名每个进程在linux或者其他操作系统中都会占用pid号,当系统中的进程过多时,需要查找redis进程号可能比较麻烦,直接打开pid文件查看即可
在这里插入图片描述
3logfile 需要指定,利用端口号命名,放到redis根目录
在这里插入图片描述
save 900(秒) 1(变动的数据条数) 当900以内,至少有1条数据变动,flush保存数据到文件
save 300 10
300秒以内至少10条数据变动,保存文件
save 60 10000
在这里插入图片描述
指定dump的持久化文件,每个服务单独指向一个文件,重启时,数据不会错乱
在这里插入图片描述
启动第二和第三个redis实例,redis-server redis.conf(指定启动文件)
拷贝redis.conf,用redis6380.conf,redis6381.conf
将拷贝的文件中只修改与端口有关内容
用vim打开,命令:%s/6379/6380/g,将所有6379改为6380

6381结点类似

启动另外两个节点
#redis-server redis6380.conf
#redis-server redis6381.conf
#ps -ef|grep redis
在这里插入图片描述
指定端口登录客户端redis-cli -p [端口号] -h [ip]
在这里插入图片描述
在这里插入图片描述

redis的java客户端–jedis

SpringBoot整合redis

  1. List item

导入依赖:

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-redis</artifactId>
 <version>1.4.7.RELEASE</version>
</dependency
  1. 连接java客户端到redis服务
  2. 创建一个jedis的对象,提供ip地址,和端口等信息
@Test
//创建Jedis对象
Jedis jedis=new Jedis("10.9.151.60", 6380);
//jedis具备了操作6380的能力,
jedis.set("name","yuzb");
System.out.println(jedis.get("name"));
public void connectRedis(){
}

利用redis实现缓存

需求:查询某个商品数据,使用缓存数据返回响应;请求发送之后,到达系统中,利用jedis对象连接redis实现缓存功能

  1. 生成商品对象的缓存key-value中调用数据的key
    key值设计=“业务逻辑”+“业务id”

  2. 判断缓存是否存在这个key值,如果有key值:说明曾经至少访问过一次,已经从数据库中将数据查询并且存放到缓存,如果没有key值:需要调用数据库持久层封装product对象,并且存放到缓存供后续访问使用

public Product queryById(String productId) {
		//创建一个连接对象使用来操作redis缓存技术
		Jedis jedis=new Jedis("10.9.151.60",6381);
		//生成当前业务逻辑的key值,业务信息+productId
		String productKey="product_"+productId;
		//想办法将product序列化成String的json字符串,使用jackson的api
		ObjectMapper mapper=new ObjectMapper();
		try{
			//判断缓存数据是否存在
			if(jedis.exists(productKey)){//表示可以使用缓存
				//Map<String, String> productMap = jedis.hgetAll(productKey);
				//JSON字符串解析反序列化
				String json=jedis.get(productKey);
				Product product=mapper.readValue(json, Product.class);
				return product;
			}else{//商品key不存在,需要访问数据库,将数据存放redis
				Product product=productMapper.queryById(productId);
				//cong product对象映射到json字符串 writeValueAsString"":""
				String json=mapper.writeValueAsString(product);
				System.out.println(json);//{"":"","":"","":""}
				//将json存储到redis中
				jedis.set(productKey, json);
				return product;
			}
		}catch(Exception e){
			e.printStackTrace();
			return null;
		}
			
	}

上面缓存方案存在问题:
1.不应该直接new一个jedis,而是交给框架去处理
2.只有一个嗯结点,如果宕机就不可用,造成缓存失效,并发下容易击穿数据库

分片的自定义计算(三个节点)

在这里插入图片描述
数据分片:数据层的分布式体系中,有多个节点维护的是整体数据,每一个节点维护一部分数据–分片数据

自定义的分片计算逻辑—hash取余
key.hashCode():key值不变,hashCode结果不变并且是整数
key.hashCode()&Integer.MAX_VALUE:31保真运算,得到正整数
(key.hashCode()&Integer.MAX_VALUE)%N:N是节点个数,分片个数,取余结果时[0,1,2,…N-1]
可以通过这样的一个公式,将任意的key值,只要key值不变永远对应[0,1,2,…N-1]中一个整数

public void set(String key,String value){
		//根据hash取余结果的逻辑实现存储在不同的结点0,6379,1,6380,6381
		Jedis jedis6379=new Jedis("10.9.151.60", 6379);
		Jedis jedis6380=new Jedis("10.9.151.60", 6380);
		Jedis jedis6381=new Jedis("10.9.151.60", 6381);
		//hash取余得到3个结点的取余结果,
		int result=(key.hashCode()&Integer.MAX_VALUE)%3;
		if(result==0){//6379
			jedis6379.set(key, value);
			System.out.println("数据绑定在6379存储");
		}
		if(result==1){//6380
			jedis6380.set(key, value);
			System.out.println("数据绑定在6380存储");
		}
		if(result==2){//6381
			jedis6381.set(key, value);
			System.out.println("数据绑定在6381存储");
		}
	}

hash取余的算法存在的问题:在扩容,缩容,数据迁移特别大,导致数据缓存未命中的概率极高;

hash一致性分片对象ShardedJedis
jedis的客户端如果连接的是redis的集群,可以在内部直接生成一个hash一致性的算法,解决key值与结点的对应关系.ShardedJedis的分片对象就封装了对多个结点集群操作时的hash一致性计算

@Test
	public void shardeJedisTest(){
		//收集结点信息
		List<JedisShardInfo> infoList=new ArrayList<JedisShardInfo>();
		//收集6379,6380,6381
		infoList.add(new JedisShardInfo("10.9.151.60", 6379, 500, 500, 5));
		infoList.add(new JedisShardInfo("10.9.151.60", 6380, 500, 500, 1));
		infoList.add(new JedisShardInfo("10.9.151.60", 6381, 500, 500, 1));
		//创建分片对象shardedJedis
		ShardedJedis sJedis=new ShardedJedis(infoList);
		//sJedis.set("name", "yuzb");
		//System.out.println(sJedis.get("name"));
		for(int i=0;i<10000;i++){
			String key="key_"+i;
			sJedis.set(key, "");
		}
	}

分片连接池
单个分片对象无法满足系统中操作集群使用hash一致性分片计算的要求;引入jedis提供的分片连接池,初始化一些分片对象,每次调用需要获取resource资源,用完了还回池子
生成代码步骤
1 收集结点信息;
2 连接池的各种属性设置:最大连接数,最大空闲,最小空闲等的的配置对象
生成
3 创建连接池对象
4 从连接池获取资源调用代码
5 返回资源到连接池

//连接池使用
	@Test
	public void connectPool(){
		//收集节点信息
		List<JedisShardInfo> infoList=new ArrayList<JedisShardInfo>();
		//收集6379,6380,6381
		infoList.add(new JedisShardInfo("10.9.151.60", 6379, 500, 500, 5));
		infoList.add(new JedisShardInfo("10.9.151.60", 6380, 500, 500, 1));
		infoList.add(new JedisShardInfo("10.9.151.60", 6381, 500, 500, 1));
		//连接池配置对象
		GenericObjectPoolConfig config=new GenericObjectPoolConfig();
		config.setMaxTotal(200);
		config.setMaxIdle(8);
		config.setMinIdle(3);
		//连接池对象
		ShardedJedisPool pool=new ShardedJedisPool(config, infoList);
		//从连接池获取分片对象操作集群
		ShardedJedis sJedis = pool.getResource();
		sJedis.set("name", "ghaha");
		pool.returnResource(sJedis);
	}
	
	

以上存在硬编码问题,如将redis结点直接写在代码中,连接池不是交给框架维护
SpringBoot整合redis改进版:

  1. 引入的技术准备配置文件application.properties
  2. 在配置文件写上redis配置
#redis-customize
redis.nodes=10.9.151.60:6379,10.9.151.60:6380,10.9.151.60:6381
redis.maxTotal=200
redis.maxIdle=8
redis.minIdle=3
  1. 读取properties属性:
    @ConfigurationProperties(prefix="")对属性赋值,调用getter/setter将读取的properties数据存放到生成bean中
/**
 * 在当前工程扫描范围之内的configuration配置类
 * 读取前缀是redis的所有key值,根据私有属性名称的getter/stter
 * 赋值给属性
 * @author yuzb
 *
 */
@Configuration
@ConfigurationProperties(prefix="redis")
public class RedisCumConfiguration {
	//根据前缀读取数据,私有属性名称,必须和
	//properties中的值相同
	private String nodes;
	private Integer maxTotal;
	private Integer maxIdle;
	private Integer minIdle;
	
	//编写初始化JedisShardPool对象的方法,@Bean将返回对象
	//作为框架管理的bean
	@Bean 
	public ShardedJedisPool initJedisPool(){
		//利用本类中读取的属性,创建连接池对象
		//先做一个config对象
		GenericObjectPoolConfig config=new GenericObjectPoolConfig();
		config.setMaxIdle(maxIdle);
		config.setMaxTotal(maxTotal);
		//config.setMinIdle(minIdle);
		//解析nodes,生成一个list对象
		//准备一个空内容
		List<JedisShardInfo> infoList=new ArrayList<JedisShardInfo>();
		String[] node = nodes.split(",");//{"10.9.9.9:6379","10l.9.9.9:6380",""}
		for (String hostAndPort : node) {
			String host=hostAndPort.split(":")[0];
			int port=Integer.parseInt(hostAndPort.split(":")[1]);
			infoList.add(new JedisShardInfo(host, port));
		}
		//list,config,构造连接池对象返回
		return new ShardedJedisPool(config,infoList);
	}
	
	
	public String getNodes() {
		return nodes;
	}
	public void setNodes(String nodes) {
		this.nodes = nodes;
	}
	public Integer getMaxTotal() {
		return maxTotal;
	}
	public void setMaxTotal(Integer maxTotal) {
		this.maxTotal = maxTotal;
	}
	public Integer getMaxIdle() {
		return maxIdle;
	}
	public void setMaxIdle(Integer maxIdle) {
		this.maxIdle = maxIdle;
	}
	public Integer getMinIdle() {
		return minIdle;
	}
	public void setMinIdle(Integer minIdle) {
		this.minIdle = minIdle;
	}
	
  1. 利用读取的属性生成框架维护的对象JedisShardPool连接池.
/**
 * jedis工具类,用于生成连接池和Jedis对象,并提供redis的常用操作
 * @author yuzb
 *
 */
@Component
public class RedisCumUtils {
	@Autowired
	private ShardedJedisPool pool;
	//query,addOrUpdate,delete,isExists
	public String query(String key){
		ShardedJedis jedis = pool.getResource();
		try{
			return jedis.get(key);
		}catch(Exception e){
			//异常处理逻辑
			return null;
		}finally{
			pool.returnBrokenResource(jedis);
		}
	}
	public void addOrUpdate(String key,String value){
		ShardedJedis jedis = pool.getResource();
		try{
			jedis.set(key, value);
		}catch(Exception e){
			//异常处理逻辑
		}finally{
			pool.returnBrokenResource(jedis);
		}
	}
	public void delete(String key){
		ShardedJedis jedis = pool.getResource();
		try{
			jedis.del(key);
		}catch(Exception e){
			//异常处理逻辑
		}finally{
			pool.returnBrokenResource(jedis);
		}
	}
	public Boolean isExist(String key){
		ShardedJedis jedis = pool.getResource();
		try{
			return jedis.exists(key);
		}catch(Exception e){
			//异常处理逻辑
			return false;
		}finally{
			pool.returnBrokenResource(jedis);
		}
	}
}

	@Autowired
	private RedisCumUtils jedis;
	public Product queryById(String productId) {
		//创建一个连接对象使用来操作redis缓存技术
		//Jedis jedis=new Jedis("10.9.151.60",6381);
		//通过注入进来的pool获取数据连接资源ShardedJedis
		//ShardedJedis jedis=pool.getResource();
		//生成当前业务逻辑的key值,业务信息+productId
		String productKey="product_"+productId;
		//想办法将product序列化成String的json字符串,使用jackson的api
		ObjectMapper mapper=new ObjectMapper();
		try{
			//判断缓存数据是否存在
			if(jedis.isExist(productKey)){//表示可以使用缓存
				//Map<String, String> productMap = jedis.hgetAll(productKey);
				//JSON字符串解析反序列化
				String json=jedis.query(productKey);
				Product product=mapper.readValue(json, Product.class);
				return product;
			}else{//商品key不存在,需要访问数据库,将数据存放redis
				Product product=productMapper.queryById(productId);
				//cong product对象映射到json字符串 writeValueAsString"":""
				String json=mapper.writeValueAsString(product);
				System.out.println(json);//{"":"","":"","":""}
				//将json存储到redis中
				jedis.addOrUpdate(productKey, json);
				return product;
			}
		}catch(Exception e){
			e.printStackTrace();
			return null;
		}
		
			
		}

hash一致性:集群中节点的数量越多,使用hash取余算法经过扩容缩容时,需要迁移的数据量就越大,否则,会造成大量的数据缓存未命中–雪崩/缓存击穿,hash一致性恰好与hash取余相反:节点越多,扩容缩容迁移越少;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章