redis分片和哨兵

时间:2023-03-09 08:38:04
redis分片和哨兵

Redis的使用

1.1 Redis入门案例

1.1.1 什么样的数据使用缓存

说明:使用缓存其实为了减少用户查询数据库的时间.如果数据频繁的变更.不适用缓存.缓存中的数据应该保存修改频率不高的数据.

例如:商品信息/邮编/省/市/县

1.2 Redis入门案例

1.2.1 导入jar包

说明:Jedis是java操作Redis的API工具包

<jedis.version>2.6.2</jedis.version>

<!-- jedis -->

<dependency>

<groupId>redis.clients</groupId>

<artifactId>jedis</artifactId>

<version>${jedis.version}</version>

</dependency>

导入jar包文件之后将parent Install

1.2.2 代码测试

//测试redis操作

@Test

public void test01(){

/**

* 创建Jedis工具类

* 参数说明

* host:表示redis的主机ip

*  port:表示redis的端口

*/

Jedis jedis = new Jedis("192.168.126.142", 6379);

//向redis中插入数据

jedis.set("tomcat", "tomcat猫");

System.out.println(jedis.get("tomcat"));

}

1.3 商品分类缓冲操作

1.3.1 需求说明

说明:用户在获取商品分类目录时每次都需要查询数据库.这样的操作效率不高.

可以将用户的分类信息插入到缓冲中,如果用户再次获取直接从缓存中获取数据即可.

1.4 将jedis交个spring管理

说明:如果将jedis直接写死到业务层,会造成代码的耦合,.所以通过spring的方式进行管理.如果需要进行依赖注入.

1.4.1 编辑配置文件

<!--通过spring的方式管理jedis

Jedis jedis = new Jedis("192.168.126.142", 6379);

补充:有时由于没有导入源码则新生成的方法中不会出现方法参数名称.而只有参数的类型

例如:String arg0,int arg1,arg2.

这样如果采用name的方式进行注入时可能会产生问题.

所以建议使用index下标进行参数赋值.

源码构造方法:

public Jedis(final String host, final int port)

问题:

通过构造注入的形式,可以通过参数的个数准确的匹配类中的方法.

如果参数个数形同时,切记需要通过类型区别,否则注入失败.

因为spring不知道到底应该调用哪个方法!!!

-->

<bean id="jedis" class="redis.clients.jedis.Jedis">

<constructor-arg index="0" value="${redis.host}"

type="java.lang.String"/>

<constructor-arg index="1" value="${redis.port}"

type="int"/>

</bean>

1.4.2 创建reids.properties文件

redis.host=192.168.126.142

redis.port=6379

1.4.3 Spring加载配置文件

1.5 商品分类缓冲实现

1.5.1 修改POJO对象

@Table(name="tb_item_cat")

//忽略未知属性  在爬虫时经常使用

@JsonIgnoreProperties(ignoreUnknown=true)

说明:由于Catlist集合在进行转化JSON时,调用get方法生成JSON数据.所以成功的JSON串格式如下

但是将JSON串转化为对象时,ObjectMapper会根据JSON的全部的的key值调用set方法为对象赋值.由于state和text属性是为了满足EasyUI格式时新增的.对实际业务不产生作用.所以应该告诉ObjectMapper.当执行到setState和setText时应该忽略这项操作.即添加@JsonIgnoreProperties(ignoreUnknown=true).忽略未知属性

@Service

public class ItemCatServiceImpl implements ItemCatService {

// 定义格式转化工具

private static final ObjectMapper objectMapper = new ObjectMapper();

@Autowired

private ItemCatMapper itemCatMapper;

@Autowired

private Jedis jedis;

@Override

public List<ItemCat> findItemCat() {

// 调用通用Mapper的方法

// 如果查询全部数据则不需要设定参数直接为null

return itemCatMapper.select(null);

}

/**

* 将商品分类信息插入到缓冲中 思路: redis中通过key-value 1.当用户查询数据时首先应该从缓存中获取数据

* 2.如果数据不为null,则代表缓存中有该数据.数据类型为JSON a.需要将JSON串转化为对象后返回给用户

* 3.如果数据为null,表示缓存中没有改数据.则需要连接数据库进行查询. a.将查询到的数据,转化为JSON存入redis中,方便下次使用.

*/

@Override

public List<ItemCat> findItemCatByParentId(Long parentId) {

// 1.定义查询的key值

String key = "ITEM_CAT_" + parentId;

// 2.根据key值查询缓存数据

String dataJSON = jedis.get(key);

// 最后定义公用的List集合

List<ItemCat> itemCatList = new ArrayList<ItemCat>();

try {

// 3判断返回值是否含有数据

if (StringUtils.isEmpty(dataJSON)) {

// 证明缓存中没有数据,则通过数据库查询数据

ItemCat itemCat = new ItemCat();

itemCat.setParentId(parentId); // 设定父级Id

itemCat.setStatus(1); // 设定为正常的数据 1

itemCatList = itemCatMapper.select(itemCat);

// 将返回数据转化为JSON串[{},{},{}]

String jsonResult = objectMapper.writeValueAsString(itemCatList);

// 将数据存入缓存中

jedis.set(key, jsonResult);

} else {

// 表示数据不为空 需要将JSON串转化为List<ItemCat>集合对象

// [{},{},{}] {id:1,name:"tom"}

ItemCat[] itemCats = objectMapper.readValue(dataJSON, ItemCat[].class);

for (ItemCat itemCat : itemCats) {

// 向集合中赋值

itemCatList.add(itemCat);

}

}

} catch (Exception e) {

e.printStackTrace();

}

return itemCatList;

}

}

Redis的分片技术

2.1 分片的配置

2.1.1 分片的概念

说明:早期如果使用一台redis时,其中保存的是服务器中全部的缓存数据.这样都放到一台服务器中会有风险,如果单台服务器宕机将直接影响整个服务.

策略:采用分片的技术,将原来由一台服务器维护整个缓存,现在换为由多台服务器共同维护整个缓存.假设之前一台服务器维护的内存数据量在9G.现在如果采用分片技术使用3台服务器共同维护内存空间,每台服务器维护3G内存.

好处:1.提高每台服务器的响应时间.

2.容灾性较好.

2.1.2 创建shard文件夹

  1. 先关机

redis-cli -p 6379 shutdown

  1. 创建shard文件夹

mkdir shard

  1. 复制文件

Cp 需要复制的文件名称  新建文件名称

  1. 移动文件

mv redis-6379.conf  shard/

2.1.3 修改redis.conf文件

1.修改6379

2.修改6380

vim redis-6380.conf

3.修改6381

将上述步骤中的6379改为6381

2.1.4 启动多台redis实例

说明:通过redis-server redis-6379.conf等命令启动redis服务

2.2 分片技术的使用

2.2.1 分片入门案例

说明:现在需要同时操作3台redis实现缓存技术.

//测试分片技术

@Test

public void test02(){

//JedisPoolConfig

//定义redis的配置  poolConfig是过期类型

JedisPoolConfig poolConfig = new JedisPoolConfig();

poolConfig.setMaxTotal(1000);//表示最大连接数

poolConfig.setMinIdle(5);//最小空间数量

//定义redis多个节点信息

List<JedisShardInfo> list =

new ArrayList<JedisShardInfo>();

//为集合添加参数

list.add(new JedisShardInfo("192.168.126.142", 6379));

list.add(new JedisShardInfo("192.168.126.142", 6380));

list.add(new JedisShardInfo("192.168.126.142", 6381));

//定义redis分片连接池

ShardedJedisPool jedisPool =

new ShardedJedisPool(poolConfig, list);

//获取连接操作redis

ShardedJedis shardedJedis = jedisPool.getResource();

shardedJedis.set("tom", "tomcat猫"); //表示数据的赋值

System.out.println(shardedJedis.get("tom"));//从redis中获取数据

}

2.2.2 分片测试

说明:将数据插入到redis中发现数据的分布不均匀

2.2.3 Hash一致性算法

说明:由于使用了分片的方式管理多个redis节点信息,那么redis节点信息中保存的数据其实是根据key值进行hash一致性计算后得出的结果.最终将数据保存到某一台redis中

说明:

根据hash一致性算法,根据指定的key值计算出对应的地址,同时将该数据动态挂载的redis节点中,如果根据key进行查询时,先经过Hash计算最终找到对应的节点,获取该key的值.

2.3 Spring管理redis分片

2.3.1 编辑配置文件

定义redis.perproties

redis.maxTotal=1000

redis.maxIdle=100

#表示每次链接时 验证连接是否有效

redis.testOnBorrow=true

#分别代码三台主机

redis.host1=192.168.126.142

redis.port1=6379

redis.host2=192.168.126.142

redis.port2=6380

redis.host3=192.168.126.142

redis.port3=6381

2.3.2 定义Spring的配置文件

说明:其实就是将测试代码按照spring的方式进行配置。

<!--每次都创建jedis对象性能较低 创建jedis线程池  -->

<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">

<!--定义连接总数  -->

<property name="maxTotal" value="${redis.maxTotal}"/>

<!--定义最大闲置资源  -->

<property name="maxIdle" value="${redis.maxIdle}"/>

<!--定义连接是否自动检测,如果连接不能使用则会新创建连接 -->

<property name="testOnBorrow" value="${redis.testOnBorrow}"/>

</bean>

<!--定义6379主机   public JedisShardInfo(String host, int port) -->

<bean id="jedisShard1" class="redis.clients.jedis.JedisShardInfo">

<constructor-arg index="0" value="${redis.host1}" type="java.lang.String"/>

<constructor-arg index="1" value="${redis.port1}" type="int"/>

</bean>

<!--定义6380主机  -->

<bean id="jedisShard2" class="redis.clients.jedis.JedisShardInfo">

<constructor-arg index="0" value="${redis.host2}" type="java.lang.String"/>

<constructor-arg index="1" value="${redis.port2}" type="int"/>

</bean>

<!--定义6381主机  -->

<bean id="jedisShard3" class="redis.clients.jedis.JedisShardInfo">

<constructor-arg index="0" value="${redis.host3}" type="java.lang.String"/>

<constructor-arg index="1" value="${redis.port3}" type="int"/>

</bean>

<!--定义分片连接池   final GenericObjectPoolConfig poolConfig, List<JedisShardInfo> shards-->

<bean id="shardedJedisPool" class="redis.clients.jedis.ShardedJedisPool">

<constructor-arg index="0" ref="poolConfig"/>

<constructor-arg index="1">

<list>

<ref bean="jedisShard1"/>

<ref bean="jedisShard2"/>

<ref bean="jedisShard3"/>

</list>

</constructor-arg>

</bean>

2.3.3 修改redis配置文件

redis.maxTotal=1000

redis.maxIdle=100

#表示每次链接时 验证连接是否有效

redis.testOnBorrow=true

#分别代码三台主机

redis.host1=192.168.126.142

redis.port1=6379

redis.host2=192.168.126.142

redis.port2=6380

redis.host3=192.168.126.142

redis.port3=6381

2.3.4 编写工具service

@Service //spring开启包扫描时的路径为jt.com

public class RedisService {

@Autowired //redis的spring管理中配置bean标签

private ShardedJedisPool jedisPool;

//定义set方法

public void set(String key,String value){

//通过池获取对象

ShardedJedis shardedJedis = jedisPool.getResource();

//通过jedis操作数据

shardedJedis.set(key, value);

//将链接还回池中

jedisPool.returnResource(shardedJedis);

}

//定义get方法

public String get(String key){

ShardedJedis shardedJedis = jedisPool.getResource();

String value = shardedJedis.get(key);

jedisPool.returnResource(shardedJedis);

return value;

}

}

2.4 Redis哨兵机制

2.4.1 分片的缺点

  1. 使用分片时是多台redis一起使用的,.如果其中有某台机器出现宕机现象.则整个分片将不能执行.
  2. 虽然有分片技术能在一定程度上缓解内存的压力.但是没有实现高可用.

2.4.2 哨兵机制

说明:

0.先配置主从复制 6379当主机  6380 6381当从机

  1. 首先需要准备奇数(3)台哨兵
  2. 哨兵会实时的向主机发送心跳检测.如果主机出现长时间没有响应的情况

则判断主机死亡.

  1. 如果主机死亡,则从主机中配置的从机进行投票选举.
  2. 2台从机中由哨兵负责投票票数多着升级成为新的Master主机(6380)
  3. 另外的一台从机(6381)就会挂载到新的Master(6380)主机中
  4. 当原来的主机6379启动后,会成为新的主机6380的从机

2.5 配置主从复制

2.5.1 关闭之前的redis

说明:配置哨兵之前首先将原有的redis关闭

2.5.2 启动多个redis服务

redis-server redis-6379.conf

redis-server redis-6380.conf

redis-server redis-6381.conf

2.5.3 查看redis信息

说明:进入redis客户端后,进入redis-6379客户端

执行命令:

2.5.4 实现主从挂载

进入到redis-cli -p 6380 后执行,实现主从挂载.将6380挂载到6379上

重复上述步骤将6381挂载到6379中

2.6 搭建哨兵

2.6.1 复制哨兵配置文件

cp sentinel.conf sentinel-6379.conf

2.6.2 移动配置文件到sentinel文件夹

mkdir sentinel

将sentinel-6379.conf 移动到sentinel中

mv sentinel-6379.conf sentinel

2.7 编辑哨兵配置文件

2.7.1 关闭保护模式:

sentinel.conf 哨兵的配置文件,将保护模式关闭的注释去掉

2.7.2 配置主机的ip

sentinel monitor mymaster 192.168.247.160 6379 1

1代表的含义:

超高半数的票数如果3台机器,半数为2  如果是5台机器半数为3

2.7.3 配置时间

表示master多少秒之后标记为down机

如果哨兵在规定的时间内内没有完成推举机制,则本次推举失败

2.7.4  哨兵启动

哨兵启动:可以通过控制台查配置信息

redis-sentinel sentinel6379.conf

2.7.5 哨兵测试:

当主机宕机后,哨兵会自动的推举出新的主机.旧的主机启动后,降为slave

注意:

当配置了哨兵之后,将不能执行分片的代码,因为哨兵配置了主从,从机不允许进行写库操作.

但是分片的redis都是Master.与哨兵冲突.所以分开测试