1 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;
}
}
2 Redis的分片技术
2.1 分片的配置
2.1.1 分片的概念
说明:早期如果使用一台redis时,其中保存的是服务器中全部的缓存数据.这样都放到一台服务器中会有风险,如果单台服务器宕机将直接影响整个服务.
策略:采用分片的技术,将原来由一台服务器维护整个缓存,现在换为由多台服务器共同维护整个缓存.假设之前一台服务器维护的内存数据量在9G.现在如果采用分片技术使用3台服务器共同维护内存空间,每台服务器维护3G内存.
好处:1.提高每台服务器的响应时间.
2.容灾性较好.
2.1.2 创建shard文件夹
- 先关机
redis-cli -p 6379 shutdown
- 创建shard文件夹
mkdir shard
- 复制文件
Cp 需要复制的文件名称 新建文件名称
- 移动文件
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 分片的缺点
- 使用分片时是多台redis一起使用的,.如果其中有某台机器出现宕机现象.则整个分片将不能执行.
- 虽然有分片技术能在一定程度上缓解内存的压力.但是没有实现高可用.
2.4.2 哨兵机制
说明:
0.先配置主从复制 6379当主机 6380 6381当从机
- 首先需要准备奇数(3)台哨兵
- 哨兵会实时的向主机发送心跳检测.如果主机出现长时间没有响应的情况
则判断主机死亡.
- 如果主机死亡,则从主机中配置的从机进行投票选举.
- 2台从机中由哨兵负责投票票数多着升级成为新的Master主机(6380)
- 另外的一台从机(6381)就会挂载到新的Master(6380)主机中
- 当原来的主机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
3 注意:
当配置了哨兵之后,将不能执行分片的代码,因为哨兵配置了主从,从机不允许进行写库操作.
但是分片的redis都是Master.与哨兵冲突.所以分开测试