缓存一般分为本地缓存和分布式缓存两种,本地缓存指的是将数据存储在本机内存中,操作缓存数据的速度很快,但是缺点也很明显:
1. 缓存数据的数量与大小受限于本机内存
2. 如果有多台应用服务器,可能所有应用服务器都要维护一份缓存,这样就占用了很多的内存
分布式缓存正好解决这两大问题,首先,数据存储在另外的机器上,理论上不断添加缓存机器,所以缓存的数据的数量可以是无限量的,其次的是,缓存几种放置在了远程的缓存服务器上,应用服务器不需要耗费空间来维护缓存,缺点也很明显,就是由于是远程操作,所以操作缓存数据的速度相较于本地缓存要慢很多
当前使用最好的本地缓存是GoogleGuavaCache
,用的较多的分布式缓存是Memcached
和redis
越来越多的公司使用redis
,因为它支持五种数据结构: String
, hash
, set
,
list
, sorted list
,redis还提供了两种持久化方式:(AOF
和RDB
),redis可以别看作是内存数据库,还支持事件调度,发布订阅等,还可以充当一下队列
下面介绍两种版本的使用方式: redis2.x 的客户都安分片和redis3.x的集群
操作系统: centos7
(两台)
下载 redis-2.6.14.tar.gz
将下载好的redis-2.6.14.tar.gz赋值到两台服务器中
解压安装…
cd /usr/local/src
tar -zxvf redis-2.6.14.tar.gz
cd redis-2.6.14.tar.gz
make && make install
启动服务
nohup redis-server redis.conf &
直接使用redis默认的配置文件来启动服务,并且设置为后台运行,将相关日志写入nohup.out文件中
使用rdm
(redis-desktop-manager)软件连接redis
客户端分片通常使用redis的ShardJedis来实现
在pom.xml文件中引入如下依赖
<!--jedis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.15</version>
</dependency>
Spring boot 1.4.1版本下,jedis的版本默认是2.8.2,所以我们不需要指定版本,太低的版本的jedis的jar包有一些bug,尽量使用2.0.8+的版本,引入commons-langs,该包有一系列的工具类,如: StringUtils,NumberUtils等,阿里的fastjson相较于Jackson使用起来更加方便
在spring boot项目中的application.properties配置redis信息
redis.shard.servers=ip地址:端口,ip地址:端口
redis.shard.timeout=5000
#最多分配多少个shardJedis实例,默认为8个,设置为-1表示不限制个数
redis.shard.maxTotal=32
配置好Redis之后,在spring boot中集成分片版的jedis,JedisShardConfig类代码如下:
package com.stscode.common.configure;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedisPool;
import java.util.ArrayList;
import java.util.List;
public class JedisShardConfig {
//spring boot配置环境对象
@Autowired
private Environment env;
/*
创建jedis池
*/
public ShardedJedisPool shardedJedisPool(){
//获取配置的服务器ip列表
String[] serverIp = env.getProperty("redis.shard.servers").split(",");
int timeout = Integer.valueOf(env.getProperty("redis.shard.timeout"));
int maxTotal = Integer.valueOf(env.getProperty("redis.shard.maxTotal"));
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); //创建jedis池配置信息
jedisPoolConfig.setMaxTotal(maxTotal); //设置最大jedis实例数量
//每一个JedisShardInfo就是一个服务器的redis实例
List<JedisShardInfo> jedisList = new ArrayList<JedisShardInfo>();
for (String server : serverIp) {
//将服务器地址和端口分开
String[] ipAndPort = server.split(":");
//构建jedis连接资源
JedisShardInfo jedisShardInfo = new JedisShardInfo(ipAndPort[0] , Integer.valueOf(ipAndPort[1]));
jedisShardInfo.setConnectionTimeout(timeout); //设置超时时间
jedisList.add(jedisShardInfo);
}
return new ShardedJedisPool(jedisPoolConfig , jedisList);//构建jedis池
}
}
构建的pool就是之后进行redis操作时获取连接的地方,其中在pool中可以同时获取多少个shardJedisPool,由maxTotal配置而定(ps: 如果不想让这些业务都共用几台redis服务器,可以创建多个ShardJedisPool,在每个pool中放置不同的服务器,之后不同的业务使用不同的ShardJedisPool)
然后,创建一个操作redis的工具类RedisUtils.代码如下:
package com.stscode.common.configure.utils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
@Component
public class RedisUtils {
//redis池
private ShardedJedisPool pool;
/**
* 存入redis数据库
* @param key
* @param value
*/
public void setData(String key , String value){
ShardedJedis jedis = null;
try {
if (pool != null) {
//获取redis资源
jedis = pool.getResource();
if (jedis != null) {
jedis.set(key, value);
}
}
}catch(Exception ex){}
finally{
if(jedis!=null){
jedis.close();
}
}
}
/**
* 从redis获取string数据
* @param key
* @return
*/
public String getData(String key){
ShardedJedis jedis = null;
try{
if (pool!=null){
jedis = pool.getResource();
if (jedis!=null){
return jedis.get(key); //从缓存服务器中获取string数据
}
}
}catch(Exception ex){}
finally {
if (jedis!=null){
jedis.close();
}
}
return StringUtils.EMPTY;
}
}
其他的数据结构可以自行在工具类中定义,首先先在方法中获取ShardedJedis,可以理解为操作Redis的一个连接,之后使用该连接进行数据库的操作,无论操作成功与否,都要将shardedJedis资源关闭
编写一个缓存前缀指定类DbAndCacheContants,代码:
package com.stscode.common.configure;
/**
* 前缀类
*/
public class DbAndCacheContants {
public static final String USER_CACHE_PREFIX = "user:";
}
在该类中指定缓存前缀,重要的作用是放置缓存key冲突和增强语义,一看缓存key就知道缓存是做什么的,缓存的key中不同的单词之间用:
分隔,这是redis推荐做法,如果以:
分隔的话,在rdm中我们可以看到key是按照:
来分层显示的
编写service层代码,如UserService:
package com.stscode.service.impl;
import com.alibaba.fastjson.JSON;
import com.stscode.common.configure.DbAndCacheContants;
import com.stscode.common.configure.utils.RedisUtils;
import com.stscode.dao.IUserDao;
import com.stscode.domain.User;
import com.stscode.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
private IUserDao userDao;
@Autowired
private RedisUtils redisUtils;
@Override
public User selectByPrimaryKey(Long id) {
return userDao.selectByPrimaryKey(id);
}
/**
* 根据用户id查询用户数据
* @param id
* @return
*/
public User getUser(Long id){
String data = redisUtils.getData(DbAndCacheContants.USER_CACHE_PREFIX + id);
//如果获取的数据不为空,将该数据返回
if (data!=null){
return JSON.parseObject(data , User.class);
}
//如果为空,从数据库中获取,并存入redis中
User user = userDao.selectByPrimaryKey(id);
//如果获取的用户信息对象不为空,存入reids中
if (user!=null) {
redisUtils.setData(DbAndCacheContants.USER_CACHE_PREFIX + id, JSON.toJSONString(user));
}
return user;
}
}
最后编写Controller类的方法并调用该service中的方法即可