一、浅谈Redis?
1.为什么要用Redis?
前台系统中: 如用户访问的效率特别低 -> 用户体验差 -> 用户的粘稠低 -> 失去用户!
不常用的数据: 如果从mysql中查询 -> 放到数据磁盘上 -> (如用户访问量大)频繁进行I/O操作
造成硬件过热 -> 增加mysql的吞吐量 -> mysql性能降低 -> db层性能降低
-> app性能降低 -> 用户体验差!
如何解决该问题?
不进行I/O操作 -> 数据放在内存中就可以 -> 故用redis来操作.
当然除了redis,还有memcache , ssdb ( 内存数据库 )
- memcache: 纯的内存数据库 , 不能进行持久化 , 数据结构比较单一.
( 只有String类型 ) - ssdb: 想取代Redis , 一般是Redis(读) + ssdb(写) 组合使用( lua脚本 )
共用一套接口api!
2.Redis是什么?
Redis是一个开源的、使用C语言编写的、支持网络交互的、可基于内存也可持久化的Key-Value数据库。
redis的特点:
1.高性能. 官方数据: 读的速度是110000次/s,写的速度是81000次/s 。
2.持久存储.
3.适应高并发.
3.redis支持的五种数据类型。
- 1.String字符串: Map<String , String>
- 2.List链表 : Map<String , LinkedList
- 3.set集合 : Map<String , hashSet>
- 4.zset(sorted set – 有序集合):
- 5.hash哈希类型: Map<String , hash<String,String>>
4.Redis持久化:
1、RDB:默认—快照 每隔一段时间将数据持久化一次
策略: 时间 改变的次数
save 900 1 // 如果900秒内 , 有1个key发生改变持久化一次.
save 300 10 // 如果300秒内 , 有10个key发生改变持久化一次.
save 60 10000 // 如果60秒内 , 有10000个key发生改变持久化一次.
好处:增量备份---文件小
缺点:容易出现数据丢失
2、AOF:相当日志 ( 默认:关闭 ) 记录写操作!
xxx.aof文件中内容( 文件-大 )
格式:记录命令
例: set name 'tom'
set school 'beijing'
$6 select
$1 0
$3 set
$4 name
$3 tom
$n : 该命令的字符个数.
0: redis一共16号库 , 默认的数据存储在0号库.
5.可能遇到的问题?
-
缓存穿透:
并发访问时(多个人同时访问判空?) —> 都去db中查询!
解决:
1.请求排队等候 ( synchronized(this) )
2.二次校验. ( 再次进行判断缓存中有没有? ) -
缓存雪崩:
服务器崩溃 / 缓存失效 !
解决:
缓存降级 ( 将一些数据提前放到缓存中 )
注: 但数据不一定完整 或者 是实时的数据!
二、SpringDataRedis
1.SpringDataRedis概述?
Spring-data-redis是spring大家族的一部分,提供了在srping应用中通过简单的配置访问redis服务,对reids底层开发包(Jedis,JRedis, and RJC)进行了高度封装,RedisTemplate提供了redis各种操作、异常处理及序列化,支持发布订阅,并对spring 3.1 cache进行了实现。 spring-data-redis针对jedis提供了如下功能:
1.连接池自动管理,提供了一个高度封装的“RedisTemplate”类
2.针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口
String-ValueOperations:简单K-V操作
Set-SetOperations:set类型数据操作
Zset-ZSetOperations:zset类型数据操作
hash-HashOperations:针对map类型的数据操作
list-ListOperations:针对list类型的数据操作
2.SpringDataRedis的优点?
1、对jedis的高度封装
2、使用开发人员对redis的操作更简单
3、对数据进行序列化。
3.案例: 使用SpringDataRedis对首页广告轮播图添加缓存.
1.添加连接redis服务的配置文件: redis-config.properties
redis.host=192.168.200.128
redis.port=6379
redis.pass=
redis.database=0
redis.maxIdle=300
redis.maxWait=3000
redis.testOnBorrow=true
2.添加SpringDataRedis配置文件: applicationContext-redis.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath*:properties/*.properties" />
<!-- redis 相关配置 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxWaitMillis" value="${redis.maxWait}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>
<bean id="JedisConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}"
p:pool-config-ref="poolConfig" />
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="JedisConnectionFactory" />
</bean>
</beans>
3.service实现类进行缓存添加.
问题? 使用redis哪种数据结构?
String类型: set key(广告分类id) value(查询到的广告结果集) 注: key-value : 需要查询多次!
Hash类型: hset key filed value 注: 只查询一次!
而我们使用redis优化:
就是要减少与数据库的交互的次数!
@Override
public List<Content> findByCategoryId(Long categoryId) {
// 判断缓存是否有
List<Content> list = (List<Content>) redisTemplate.boundHashOps("content").get(categoryId);
if(list == null){
synchronized (this) {
list = (List<Content>) redisTemplate.boundHashOps("content").get(categoryId);
if(list == null){
// 从数据库中查询
ContentQuery contentQuery = new ContentQuery();
contentQuery.createCriteria().andCategoryIdEqualTo(categoryId).andStatusEqualTo("1");
list = contentDao.selectByExample(contentQuery);
// 将数据放入缓存
redisTemplate.boundHashOps("content").put(categoryId, list);
}
}
}
return list;
}