Redis分布式缓存
redis是一款开源的Key-Value数据库,存储数据是放在内存中,速度非常快,由C语言编写.企业开发通常采用redis来实现缓存.
业务流程
获取数据的时候先从redis中获取,如果获取到数据则直接返回,就不用访问数据库了,如果获取不到数据,可以从数据库中查询,查询到后放入redis中一份,下回就可以直接从redis中查询到,大大降低了数据库的高并发访问压力
持久化方案
rdb(默认支持,无需配置)分时持久化
- 可以在配置文件中设定,多长时间持久化一次,持久化次数越少也就是硬盘的操作次数越少,速度快,但是如果在没有完成持久化之前,如果服务器断电,则内存中没有持久化的数据会丢失
aof实时持久化
- 每次向redis中做增删改操作,都会将数据持久化到硬盘上,数据可靠性高,不会丢失,但是速度慢
redis可以同时使用RDB和AOF
redis中五大数据类型
- string:字符串
- hash:相当于map,是一种键值对形式.存入的数据是无序的,key不可以重复
- list:存入其中的数据是有序的,存入其中的数据可以重复
- set:存入其中的数据是无序的,存入其中的数据不可以重复
- zset:存入其中的数据是有序的,存入其中的数据不可以重复
redis同类型技术
- memcache是redis的同类型技术,底层也是使用C语言编写,memcache的速度和redis相当,但是memcache没有持久化方式
mongodb和redis区别
- mongodb也是一个nosql数据库,存储的数据是非结构化的
- redis:主要使用内存,有两种持久化方案,速度非常快,一般用做分布式缓存使用
- mongodb:主要使用硬盘存储,所以不会担心数据丢失,速度介于redis和传统数据库之间,但是mongodb更擅长存储大文本数据,以及一些非结构化的数据,mongodb比redis的数据类型更加丰富
redis应用场景
- 缓存(数据查询,短连接,新闻内容,商品内容等等)
- 聊天室的在线好友列表
- 任务队列(秒杀,抢购,12306等等)
- 应用排行榜
- 网站访问统计
- 数据过期处理(可以精确到毫秒)
- 分布式集群架构中的session分离
Spring Data Redis
- Spring-data-redis是spring大家族的一部分,提供了在srping应用中通过简单的配置访问redis服务,对reids底层开发包(Jedis, JRedis, and RJC)进行了高度封装,RedisTemplate提供了redis各种操作、异常处理及序列化,支持发布订阅,并对spring 3.1 cache进行了实现。
Jedis
- Jedis是Redis官方推出的一款面向Java的客户端,提供了很多接口供Java语言调用。可以在Redis官网下载,当然还有一些开源爱好者提供的客户端,如Jredis、SRP等等,推荐使用Jedis
用Jedis做个案例
配置文件config.properties
host=localhost
port=6379
maxTotal=50
maxIdle=20
工具类JedisUtiles
package com.ayyy.core.utils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.util.ResourceBundle;
/**
- 数据库连接池工具类
- 目的: 返回数据库连接对象Jedis
- 读取config配置文件
*/
public class JedisUtils {
//声明出连接池对象
private static JedisPool pool ;
//读取配置文件
static {
ResourceBundle resourceBundle = ResourceBundle.getBundle("config");
//获取服务器IP地址
String host = resourceBundle.getString("host");
//获取端口号
int port = Integer.parseInt(resourceBundle.getString("port"));
//获取最大连接数
int maxTotal = Integer.parseInt( resourceBundle.getString("maxTotal"));
//获取最大空闲连接
int maxIdle = Integer.parseInt( resourceBundle.getString("maxIdle"));
//创建连接池的配置信息对象
JedisPoolConfig config = new JedisPoolConfig();
//设置相关的信息
config.setMaxTotal(maxTotal);
config.setMaxIdle(maxIdle);
//创建连接池对象
pool = new JedisPool(config,host,port);
}
/**
- 创建方法
- 连接池对象,返回Jedis对象
*/
public static Jedis getJedis(){
return pool.getResource();
}
//释放资源方法
public static void close(Jedis jedis){
if(jedis!=null)
jedis.close();
}
public static void close(JedisPool pool){
if(pool!=null)
pool.close();
}
}
Service接口
package com.ayyy.core.service;
import com.ayyy.core.pojo.Category;
import java.util.List;
public interface CategoryService {
//查询所有的分类数据
List<Category> findAll();
}
Service实现类
package com.ayyy.core.service.impl;
import com.ayyy.core.dao.CategoryDao;
import com.ayyy.core.pojo.Category;
import com.ayyy.core.service.CategoryService;
import com.ayyy.core.utils.BeanFactory;
import com.ayyy.core.utils.JedisUtils;
import net.sf.json.JSONArray;
import redis.clients.jedis.Jedis;
import java.sql.SQLException;
import java.util.List;
public class CategoryServiceImpl implements CategoryService {
//bean工厂,获取dao层接口实现类
private CategoryDao categoryDao = BeanFactory.newInstance(CategoryDao.class);
/**
* 从redis数据库中查询导航数据
* 不存在数据
* 查询MySQL数据,取出category表数据
* 数据转成JSON字符串,存储redis
*
* 数据存储在
* redis数据库存储的是字符串
* 字符串转成集合返回
*/
public List<Category> findAll(){
List<Category> categoryList = null;
//获取Redis数据库连接对象
Jedis jedis = JedisUtils.getJedis();
//获取字符串
String category = jedis.get("category");
try {
//判断是否有数据
if (category == null) {
//查询MySQL数据库,取出category表数据
categoryList = categoryDao.findAll();
//数据集合,转成JSON存储到redis
//JSONArray.fromObject(categoryList).toString();
jedis.set("category",JSONArray.fromObject(categoryList).toString());
} else {
//redis数据库有数据
//category转成集合返回
/**
* toList,JSON格式字符串转成集合List
* 参数: JSONArray对象
* 被转换后的集合的泛型的class对象
*/
// JSONArray.fromObject(category);
categoryList = JSONArray.toList( JSONArray.fromObject(category),Category.class );
}
}catch (Exception ex){
ex.printStackTrace();
}finally {
JedisUtils.close(jedis);
}
return categoryList;
}
}
Spring Data Redis案例
表现层
package com.ayyy.core.controller;
import com.ayyy.core.pojo.ad.Content;
import com.ayyy.core.service.ContentService;
import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/content")
public class ContentController {
@Reference
private ContentService contentService;
@RequestMapping("/findByCategoryId")
public List<Content> findByCategoryId(Long categoryId) {
List<Content> list = contentService.findByCategoryIdFromRedis(categoryId);
return list;
}
}
Service接口
package com.ayyy.core.service;
import com.ayyy.core.pojo.ad.Content;
import com.ayyy.core.pojo.ad.ContentCategory;
import com.ayyy.core.pojo.entity.PageResult;
import javax.naming.Context;
import java.util.List;
public interface ContentService {
public List<Content> findByCategoryIdFromRedis(Long categoryId);
}
Service实现类
package com.ayyy.core.service;
import com.ayyy.core.dao.ad.ContentDao;
import com.ayyy.core.pojo.ad.Content;
import com.ayyy.core.pojo.ad.ContentQuery;
import com.ayyy.core.pojo.entity.PageResult;
import com.ayyy.core.util.Constants;
import com.alibaba.dubbo.config.annotation.Service;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import javax.naming.Context;
import java.util.List;
/**
* 使用redis分布式缓存原则:
* 一般关系型数据库作为我们的主数据库存储数据, 如果涉及到使用分布式缓存redis, 要保证redis中的数据
* 和数据库中的数据要一致.
*/
@Service
public class ContentServiceImpl implements ContentService {
@Autowired
private ContentDao contentDao;
@Autowired
private RedisTemplate redisTemplate;
/**
* 整个redis相当于一个大的hashMap, 在这个map中key是不可以重复的, 所以key是稀缺资源
* key value(value使用hash类型因为可以尽量少的占用key)
* field value
* 分类id 对应的这个分类的广告集合数据List<Content>
* 001 List<Content>
* 002 List<Content>
*
*
*/
@Override
public List<Content> findByCategoryIdFromRedis(Long categoryId) {
//1. 首先根据分类id到redis中获取数据
List<Content> contentList = (List<Content>)redisTemplate.boundHashOps(Constants.CONTENT_LIST_REDIS).get(categoryId);
//2. 如果redis中没有数据则到数据库中获取数据
if (contentList == null) {
//3. 如果数据库中获取到数据, 则放入redis中一份
contentList = findByCategoryId(categoryId);
redisTemplate.boundHashOps(Constants.CONTENT_LIST_REDIS).put(categoryId, contentList);
}
return contentList;
}
}