redis 的安装和简单使用

  • 一种nosql 单线程的数据库做缓存的简单使用
  • 对curd的操作,为了保证数据的一致性,除了查询,其它操作都要进行数据更新和删除
  • redis的协议:Redis服务器与客户端通过RESP(REdis Serialization Protocol)协议通信,了解了Redis协议的基本数据类型,就可以通过Socket与Redis Server进行通信,客户端和服务器发送的命令或数据一律以 \r\n (CRLF)结尾。
  • 缓存击穿 指查询一个一定不存在的数据要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞;解决方法如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短
  • 缓存雪崩 缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩;解决方案在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,降低几率;或则加锁,队列
  • 关于缓存击穿和雪崩参考

安装服务

  • http://www.redis.cn/ 下载最新redis4.01
  • #tar -zxvf redis... --解压
  • #mv file filepath --移动到你想安装的目录
  • #cd /redis
  • #make MALLOC=libc --管理内存碎片
  • #make && make install --编译并安装
  • #cd src && redies-server 如图
    image.png

配置

  • #vi /home/redis/src/redis.conf --编写配置文件,写入已下配置,指定端口-父进程pid-守护进程
    image.png
  • #通过src/redis-server redis.conf 启动 通过#redis-cli shutdown 来停止服务
  • 会有两个端口打开未发现有什么错误,可以禁用ip6,
    image.png
  • #firewall-cmd --zone=public --add-port=6379/tcp --perment
  • #vi /usr/lib/systemd/system/redis.service --编写服务单元交与systemd 管理
    image.png

rdb和aof 两种持久化方案详情1详情2

  • rdb 是基于快照的,通过save seconds frequency,进行周期性的备份默认开启,当服务器出现故障可能会丢失最后一次的数据。数据恢复速度快适合数据的备份。
    image.png
  • aof 是基于日志记录的,保持了高一致性。数据比较全面文件比较大恢复稍慢。默认是关闭的通过appendonly yes开启。


    image.png

简单的使用

  1. 使用java操作jedis客户端
  • 编写jedisManager
导入依赖
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
  </dependency>
redis的连接初始化类

public class JedisManager {
    private static String host = "192.168.1.200";
    private static int port = 6379;
    private JedisPoolConfig jpc;
    private static JedisPool jedisPool;
    private static int timeOut;

    /**
     * 初始化连接池
    **/
    static {
        if (timeOut == 0){
            timeOut = Protocol.DEFAULT_TIMEOUT;
        }
        jedisPool = new JedisPool(host,port);
    }

    /**
     * 从连接池中获取连接
    **/
    public static Jedis getJedis(){
        if (jedisPool != null){
            return jedisPool.getResource();
        }else throw new NullPointerException("jedisPool is null");
    }

    /**
     * 把连接返回给jedispool
    **/
    public static void  returnResource(Jedis jedis) {
        if (jedis != null){
            jedisPool.close();
        }
    }

    /**
     * 关闭连接池
    **/
    public void shutDown(){
        if (jedisPool != null){
            jedisPool.destroy();
        }
    }
}
String类型的测试

public class JedisDao {
    //存储字符串
    public void cacheString(){
        Jedis jedis = JedisManager.getJedis();
        System.out.println(jedis);
        jedis.set("name","hahaha");
        System.out.println(jedis.get("name"));
        //拼接
        jedis.append("name","is 爸爸");
        System.out.println(jedis.get("name"));
        JedisManager.returnResource(jedis);
    }

}
测试结果

redis.clients.jedis.Jedis@7bb11784
hahaha
hahahais 爸爸
Process finished with exit code 0
  1. 整合spring 分别在service层缓存,利用spirng aop 对目标方法进行缓存
导入依赖

<dependency>
      <groupId>redis.clients</groupId>
      <artifactId>jedis</artifactId>
       <version>2.9.0</version>
  </dependency>
  <dependency>
      <groupId>org.springframework.data</groupId>
       <artifactId>spring-data-redis</artifactId>
        <version>1.7.2.RELEASE</version>
 </dependency>
<dependency>
    <groupId>aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
     <version>1.5.4</version>
  </dependency>
redis.properties

redis.host=192.168.1.200
redis.port=6379
redis.password=
#最大空闲数
redis.maxIdel=300
#连接池连接数
redis.maxActive=600
#最大等待时间
redis.maxWait=1000
#实例均用
redis.testOnBorrow=true
reids-cfg.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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--引入redis参数文件-->
     <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="order" value="1"/>
        <property name="ignoreUnresolvablePlaceholders" value="true"/>
        <property name="locations">
            <list>
                <value>classpath:redis.properties</value>
            </list>
        </property>
    </bean>
    <!--连接池-->
    <bean id="jedisConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxTotal" value="${redis.maxActive}"/>
        <property name="maxIdle" value="${redis.maxIdel}"/>
        <property name="maxWaitMillis" value="${redis.maxWait}"/>
        <property name="testOnBorrow" value="${redis.testOnBorrow}"/>

    </bean>

    <!--连接工厂-->
    <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="${redis.host}"/>
        <property name="port" value="${redis.port}"/>
        <property name="password" value="${redis.password}"/>
        <property name="poolConfig" ref="jedisConfig"/>
       <property name="usePool" value="true"/>

    </bean>

    <!-- redis操作模板-->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
        <property name="connectionFactory" ref="connectionFactory"/>
        <!--     如果不配置Serializer,那么存储的时候只能使用String,如果用对象类型存储,那么会提示错误 can't cast to String!!!-->
        <property name="keySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
        </property>
        <property name="valueSerializer">
            <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
        </property>
        <!--开启事务-->
        <property name="enableTransactionSupport" value="true"/>
    </bean>

</beans>
放入spring的主配置文件

    <!--redis-->
    <import resource="redis-cfg.xml"/>
  • 测试在service层进行缓存
 需要的工具类

@Component
public class RedisCacheUtil {

    @Autowired
    private RedisTemplate<Serializable,Object> redisTemplate;

    /**
     * 删除对应的value
    **/
    public void remove(final String... args){
        for (String key:args){
           remove(key);
        }
    }
    public boolean remove(String key){
        boolean flag = false;
        try {
            if (exists(key)){
                redisTemplate.delete(key);
                flag = true;
            }
        }catch (Exception e) {
            System.out.println(e.toString());
        }
        return flag;
    }

    public boolean exists(String key) {
        return redisTemplate.hasKey(key);
    }


    /**
     * 删除keys
    **/
    public void removePattern(final String pattern){
        Set<Serializable> keys = redisTemplate.keys(pattern);
        if (keys.size()>0){
            redisTemplate.delete(keys);
        }
    }

    /**
     * 根据key读取缓存
    **/
    public Object get(final String key){
         return redisTemplate.opsForValue().get(key);
    }
    
    /**
     * 写入缓存
    **/
    public boolean set(final String key, Object value){
        try {
            redisTemplate.opsForValue().set(key,value);
            return true;
        }catch (Exception e){
            System.out.println(e.toString());
            return false;
        }
    }

    public boolean set(final String key, Object value, Long time){ //设置过期时间
        try {
            redisTemplate.opsForValue().set(key, value);
            redisTemplate.expire(key,time, TimeUnit.SECONDS);
            return true;
        }catch (Exception e){
            return false;
        }
    }
}

缓存的容器

/**
 * Created by: jun on 2018/1/7.
 * description: 创建redis的储存器接口
 */
public interface RedisCacheStorage<k,v> {
    //key,value 普通类型
    boolean set(k key, v value);    //永久缓存
    boolean set(k key, v value, Long time); //time内缓存
    boolean remove(k key);  //删除
    v get(k key);   //获取
 }

// 实现的接口
@Component
public class RedisCacheStorageImpl<v> implements RedisCacheStorage<String,v> {

    @Autowired
    private RedisCacheUtil redisCacheUtil;

    @Override
    public boolean set(String key, v value) {
        return redisCacheUtil.set(key,value);
    }

    @Override
    public boolean set(String key, v value, Long time) {
        return redisCacheUtil.set(key,value,time);
    }

    @Override
    public boolean remove(String key) {
        return redisCacheUtil.remove(key);
    }

    @Override
    public v get(String key) {
        return (v) redisCacheUtil.get(key);
    }
}
实体类,一定要序列化,spirng 才能进行装配

public class User implements Serializable {

    private Integer id;
    private String name;
    private String pwd;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }
}
开始缓存

@Service
public class UserServiceImpl implements UserService {

    private static final String cacheKey = "userEntity"; //缓存的key
    private static final Long time = 10000l;

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RedisCacheStorage cacheStorage;

    @Override
    public List<User> select() {
        //先查找缓存
        List<User> userList;
        userList = (List<User>) cacheStorage.get(cacheKey);
        if (userList != null) {
            System.out.println("读取了缓存");
            return userList;
        } else {
            //开始查找数据库,放入缓存
            userList = userMapper.selectAll();
            cacheStorage.set(cacheKey, userList);
            System.out.println("已添加缓存");
            return userList;
        }
    }

测试结果


image.png
  • 测试基于aop配置文件目标方法的缓存
实现MethodInterceptor接口的切面,在redis.properties中写入需要缓存的方法如果与aop拦截的方法对应则进行缓存

/**
 * Created by: jun on 2018/1/3.
 * description: 实现spring MethodInterceptor接口,使用aop对方法级的进行缓存。
 *              根据配置文件给不同的方法加入判断是否缓存数据,第一次从缓存中读取,并将结果进行缓存
 */
public class MethodCacheInterceptor implements MethodInterceptor {

    @Autowired
    private RedisCacheUtil redisCacheUtil;
    private List<String> cacheClassList;    //缓存的serice类
    private List<String> cacheMethodList;    //缓存的方法
    private Long timeOut = 1000l;   //过期时间
    private String resourcesName = "redis.properties";  //缓存的配置文件
    private String fileClassValue = "cacheClass";    //配置文件设置缓存的类字段
    private String fileMethodValue = "cacheMethod";     //配置文件中缓存的方法字段


    /**
     * 初始化读取需要缓存的类和方法
     * 在reids.properties中添加缓存的方法和类
     *
    **/
    public MethodCacheInterceptor() throws IOException {
        // 在 resources 下读取配置文件
        InputStream in = ConfProLoader.getClassPathFile(resourcesName);
        Properties p = new Properties();
        p.load(in);
        //分割,获取每个类和方法
        String[] cacheClass = p.getProperty(fileClassValue).split(",");
        String[] cacheMethod = p.getProperty(fileMethodValue).split(",");
        //将方法和类存入list
        cacheClassList = new ArrayList<String>(cacheClass.length);
        cacheMethodList = new ArrayList<String>(cacheMethod.length);
        int maxLength = cacheClass.length > cacheMethod.length ? cacheClass.length
                : cacheMethod.length;
        for (int i=0; i<maxLength; i++){
            if (i<cacheClass.length) cacheClassList.add(cacheClass[i]);
            if (i<cacheMethod.length) cacheMethodList.add(cacheMethod[i]);
            }
        }


    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        //得到方法和参数列表
        String targetName = invocation.getThis().getClass().getName();
        String methodName = invocation.getMethod().getName();
        Object[] argument = invocation.getArguments();
        String key = getCacheKey(targetName,methodName,argument);
        System.out.println("key: "+key);
        //是否缓存方法 /否则放行
        System.out.println("拦截的类"+targetName);
        if (isAddCache(targetName,methodName)){
            //若果缓存存在,直接读取否则进行缓存
            if (redisCacheUtil.exists(key)){
                System.out.println("开始读取缓存");
                return redisCacheUtil.get(key);
            }
            Object value = invocation.proceed();   //对方法的结果进行缓存
            if (value != null){
                System.out.println("开始缓存");
                redisCacheUtil.set(key,value,timeOut);
            }
        }
        return invocation.proceed();
    }

    /**
     * 检查是否需要添加缓存
    **/
    public boolean isAddCache(String targetName,String metnodName){
        boolean flag= false;
        if (cacheClassList.contains(targetName) || cacheMethodList.contains(metnodName)) flag = true;
        return flag;
    }


    /**
     * 创建缓存的key
    **/
    public String getCacheKey(String targetName, String methodName, Object[] arguments){
        StringBuffer sb = new StringBuffer();
        sb.append(targetName).append("_").append(methodName);
        for (int i=0; i<arguments.length; i++){
            if (arguments.length>0) sb.append("_").append(arguments[i]);
        }
        return sb.toString();
    }
}
在reids的资源文件中写入需要缓存的类和方法。如果拦截的方法和配置文件中对应,则对该方法进行缓存

host=192.168.1.200
port=6379
password=

#连接池连接数
maxTotal=400
#最大空闲数
maxIdel=300
#最大等待时间
maxWait=1000
#实例均用
testOnBorrow=true


#需要缓存的类
cacheClass=comm.tianzhuan.web.service.impl.UserServiceImpl,test1
#要缓存的方法
cacheMethod=selectt,hahaha

#设置缓存失效时间
test=60
test1=60
defaultCacheExpireTime=3600

spirng中配置aop

 <!--配置拦截器实现方法的缓存-->
    <bean id="methodCacheInterceptor" class="com.tianzhuan.common.cache.MethodCacheInterceptor"/>
    <aop:config proxy-target-class="true">
        <!-- 切入点 -->
        <aop:pointcut id="redisCache" expression="execution(* com.tianzhuan.web.service.impl.*.*(..))"/>
        <!--拦截器-->
        <aop:advisor advice-ref="methodCacheInterceptor" pointcut-ref="redisCache"/>
    </aop:config>

测试的结果

@Test
    public void test(){
        List<User> userList = userService.select();
        System.out.println(userList);
    }
image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容