redis 笔记分享

Redis!!!

内存型数据库

特点

  • Redis是一个高性能key/value内存型数据库
  • Redis支持丰富的数据类型
  • Redis支持持久化
  • Redis单线程,单进程

安装

准备环境

centos7

下载redis安装包

redis-6.0.9.tar.gz

解压部署

  1. 解压文件
tar -xzvf redis-5.0.10.tar.gz 
  1. 安装gcc
yum install -y gcc
  1. 进入到解压文件夹
make MALLOC=libc
  1. 编译完成后
make install PREFIX=/usr/redis
  1. 进入/usr/redis/bin目录启动redis服务
./redis-server
  1. 新建一个会话窗口
./redis.cli -h localhost -p 6379 --raw(加上这个就可以显示中文 一般不用)

redis细节

  1. redis启动服务的细节
    直接使用. / redis-server方式启动使用的是redis-server这个shell脚本中默认配置

  2. 如何在启动redis时指定配置文件启动
    默认在redis安装完成之后再安装目录没有任何配置文件,需要在源码目录中复制配置文件到安装目录

    在原先的解压包下将redis.confⅠ

    cp redis.conf /usr/redis/
    
  3. 使用自己配置的配置文件启动

    ./redis-server ../redis.conf
    
  4. 库的概念

    库:database用来存放数据一个基本单元一个库可以存放key-value键值对 redis中每一个库都有一个唯一名称 编号从0开始默认库的个数:16个库 库的编号:0-15 默认使用是0号库;

    切换库命令:       select dbid (库编号)
    
  5. 如何清除库

    flushdb 清空当前库 flushall 清空所有

守护进程 后端开启

查看进程

ps aux|grep redis

基本命令

数据库操作命令

#选中库select 库的编号#清库flushdb  #清空当前库flushall #清空所有

key的操作命令

#del语法: del key [key...]作用:删除一个或多个key  不存在的key被忽略返回:删除的个数#exists语法:exists key作用:检查是否存在返回:存在返回1  否则0#expire key seconds#expire key 毫秒语法:expire key seconds作用:给指定的key设置生存时间  到期自动删除返回:设置成功返回1#keysKEYS *				匹配数据库中所有key .KEYS h?1lo			匹配hello , hallo和hxllo等。KEYS h*llo			匹配hllo和heeeeello 等。KEYS h[ae]llo		匹配hello和hallo ,但不匹配hillo。特殊符号用“\”隔开#move key名  库名移动#TTL ttl		查看还差多少秒过期pttl	查看还差多少毫秒过期返回-1:永久存储返回-2:没查到#renamerename key keynewname修改名字  要是keynewname存在则覆盖#TYPE语法:TYPE key作用:返回key所储存的值的类型。返回值:none (key不存在)string(字符串)list(列表)set(集合)zset(有序集)hash(哈希表)

String类型的操作指令

内存存储模型

常用命令

命令说明
set设置一个key/value
get根据key获取对应的value
mset一次设置多个key/value
mget一次获得多个key的value
getset获得原始key的值并重新赋值
strlen获得对应key存储value的长度
append为对应的key追加内容
getrange截取value的内容 索引0开始
setex设置一个key存活的有效期(秒)
psetex设置一个key存活的有效期(毫秒)
setnx存在不做任何操作,不存在则添加
msetnx可以同时设置多个key,只要有一个存在就不做任何操作
decr进行数值类型的-1操作
decrby根据提供的数据进行减法操作
lncr进行数值类型的+1操作
incrby根据提供的数据进行加法操作
incrbyfloat根据提供的数据加入浮点数

List类型

有序,可以重复

内部存储类型

常用命令

命令说明
lpush将某个值加入到一个key列表头部
lpushx将某个值加入到一个key列表头部,key必须存在
rpush将某个值加入到一个key列表末尾
rpushx同rpush,但是必须要保证这个key存在
lpop返回和移除列表的左边第一个元素
rpop返回和移除列表的右边第一个元素
lrange 0 -1获取某一个下标区间内的元素
llen获取列表元素个数
lset设置某一个指定索引的值(索引必须存在)
lindex获取某一个指定索引位置的元素
lrem删除重复元素
ltrim保留列表中特定区间内的元素
linsert在某一个元素之前,之后插入新元素

Set类型

元素无序,不能重复

内部存储模型

常用命令

命令说明
sadd为集合添加元素
smembers显示集合中所有元素无序
scard返回集合中元素的个数
spop随机返回一个元素并将元素在集合中删除
smove从一个集合中刺另一个集合移动元素
srem从集合中删除一个元素
sismember判断一个集合中是否含有这个元素
srandmember随机返回元素
sdiff去掉第一个集合中其它集合含有的相同元素
sinter求交集
sunion求和集

ZSet类型

可排序的set集合 排序,不能重复

存储模型

常用命令

命令说明
zadd添加一个有序集合元素
zcard返回集合的元素个数
zrange升序zrevrange降序返回一个范围内的元素
zrangebyscore按照分数查找一个范围内的元素
zrank返回排名
zrevrank倒序排名
zscore显示某一个元素的分数
zrem移除某一个元素
zincrby给某个特定元素加分

hash类型

key(String) value(key(无序的) value)

内存模型

常用命令

命令说明
hset设置一个key/value对
hget获得一个key对应的value
hgetall获得所有的key/value对
hdel删除某一个key/value对
hexists判断一个key是否存在
hkeys获得所有的key
hvals获得所有的value
hmset设置多个key/value
hmget获得多个key的value
hsetnx设置一个不存在的key的值
hincrby为value进行加法运算
hincrbyfloat为value加入浮点值

开启远程连接

默认是不开启的,需要去修改配置文件,修改为0.0.0.0 允许一切远程客户端连接

数据持久化

有两种方式 快照持久化 与 AOF持久化

快照持久化

就是每隔一段时间自动保存在磁盘上,可以自己设置时间,默认使用的就是快照持久化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XOiCkAZ1-1619789926314)(redis.assets/image-20201117153006877.png)]

AOF持久化

就是将写入操作打印出日志,之后执行一遍取回数据,但是容易造成多余的重复字段,出现了AOF重写

持久化总结

  • 两种持久化方案既可以同时使用(aof),又可以单独使用,在某种情况下也可以都不使用,具体使用那种持久化方案取决于用户的数据和应用决定。
  • 无论使用AOF还是快照机制持久化,将数据持久化到硬盘都是有必要的,除了持久化外,用户还应该对持久化的文件进行备份(最好备份在多个不同地方).

Java操作redis

  1. 环境准备

    引入依赖

        <!--引入redis依赖-->    <dependency>      <groupId>redis.clients</groupId>      <artifactId>jedis</artifactId>      <version>2.9.0</version>    </dependency>
    
  2. 创建对象

    package com.zhangan.test;import redis.clients.jedis.Jedis;import java.util.Set;//测试redis的连接public class TestRedis {    public static void main(String[] args) {        //创建jedis客户端对象        Jedis jedis = new Jedis("47.96.234.197",6379);        //选择使用一个库  默认0号库        jedis.select(0);        //获取redis所有key信息        Set<String> keys = jedis.keys("*");        keys.forEach(key -> System.out.println("key = "+key));//        //操作库相关的//        jedis.flushDB();//清除当前库//        jedis.flushAll();//全部清除        //释放资源        jedis.close();    }}
    
  3. 相关api测试

    package com.zhangan.test;import org.junit.After;import org.junit.Before;import org.junit.Test;import redis.clients.jedis.Jedis;public class TestKey {    private Jedis jedis;    @Before    public void before(){        this.jedis = new Jedis("47.96.234.197",6379);    }    @After    public void after(){        jedis.close();    }    //测试key相关    @Test    public void testKeys(){        //删除多个key//        jedis.del("name","sex");        //判断一个key是否存在//        Boolean flag = jedis.exists("height");//        System.out.println(flag);        //设置key超时时间        Long age = jedis.expire("age", 100);        System.out.println(age);        //查看一个key的超时时间        Long age1 = jedis.ttl("age");        System.out.println(age1);    }}
    
        //测试String相关    @Test    public void testKeys(){        //set        jedis.set("xingming","小陈");        String xingming = jedis.get("xingming");        System.out.println(xingming);        jedis.mset("name1","好人","name2","坏人");        List<String> mget = jedis.mget("name1", "name2");        System.out.println(mget);    }
    

Spring boot整合

Spring Boot pata(数据) Redis中提供了RedisTemplateStringRedisTemplate,其中stringRedisTemplateRedisTemplate的子类,两个方法基本一致,不同之处主要体现在操作的数据类型不同,RedisTemplate中的两个泛型都是object,意味着存储的key和value都可以是一个对象,而StringRedisTemplate的两个泛型都是String,意味着stringRedisTemplate的key和value都只能是字符串。

引入依赖

<!--引入spring data redis依赖--><dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-data-redis</artifactId></dependency>

配置application.yml

#redisspring:  redis:    host: 47.96.234.197    port: 6379    database: 0

使用StringRedisTemplate

public class TestStringRedisTemplate {    //注入StringRedisTemplate    @Autowired    private StringRedisTemplate stringRedisTemplate;    //操作redis中的key    public void testKey(){        stringRedisTemplate.delete("ceshi");        stringRedisTemplate.hasKey("测试");        stringRedisTemplate.type("cehsi");        stringRedisTemplate.keys("*");        stringRedisTemplate.getExpire("ceshi");        stringRedisTemplate.renameIfAbsent("ceshi","new");        stringRedisTemplate.move("ceshi",1);    }    //操作redis字符串  //opsForValue()操作的就是String    @Test    public void testString(){                stringRedisTemplate.opsForValue().set("ceshi","小吃");        //设置超时时间        stringRedisTemplate.opsForValue().set("code","2357",120, TimeUnit.SECONDS);        //追加        stringRedisTemplate.opsForValue().append("ceshi","lalalala");    }            //操作redis  List    @Test    public void testList(){        stringRedisTemplate.opsForList().leftPushAll("listlist222","lalala","lalala2");        stringRedisTemplate.opsForList().leftPush("listlist","lalala","lalala2");        stringRedisTemplate.opsForList().range("listlist",0,-1);    }    //操作redis   set    @Test    public void testSet(){        stringRedisTemplate.opsForSet().add("setset","1","2");        stringRedisTemplate.opsForSet().members("setset");        stringRedisTemplate.opsForSet().size("setset");    }        //操作redis  Zset    @Test    public void testZset(){        stringRedisTemplate.opsForZSet().add("zetzet","张安",100);        stringRedisTemplate.opsForZSet().range("zetzet",0,-1);        //获取指定元素和分数        Set<ZSetOperations.TypedTuple<String>> zetzet = 			    	stringRedisTemplate.opsForZSet().rangeByScoreWithScores("zetzet", 0, 100);        zetzet.forEach(typed -> {            System.out.println(typed.getValue());            System.out.println(typed.getScore());        });}                    //操作redis   hash    @Test    public void testHash(){        //创建一个hash类型  放入key  value        stringRedisTemplate.opsForHash().put("maps","name","张安");        //获取hash中每个key的值        stringRedisTemplate.opsForHash().get("maps","name");        //获取所有的key/value的值        stringRedisTemplate.opsForHash().keys("maps");        stringRedisTemplate.opsForHash().values("maps");        //存放多个key        Map<String,String> map = new HashMap<>();        map.put("name","张三");        map.put("age","19");        stringRedisTemplate.opsForHash().putAll("poeple",map);        //取出        stringRedisTemplate.opsForHash().multiGet("people", Arrays.asList("name","age"));    }

使用RedisTemplate

@SpringBootTest(classes = RedisApplication.class)public class TestRedisTemplate {    //注入    @Autowired    private RedisTemplate redisTemplate;    //opsFor Value String List Set Zset Hash    //注入RedisTemplate key Object Value 0bject===> 对象序列化   name new User ()====>  name序列化      对象序列化结果    @Test    public void testRedisTemplate(){        /**        redisTemplate对象中 key和 value 的序列化都是JdkSerializationRedisSerializer        key : string        value: object        修改默认key序列化方案:key StringRedisserializer        */        redisTemplate.setKeySerializer(new StringRedisSerializer());        User user = new User();        user.setId(1);        user.setName("张安");        user.setAge("18");        redisTemplate.opsForValue().set("user",user);    }}

简化操作,多次操作一个key时可以使用绑定

@SpringBootTest(classes = RedisApplication.class)public class bound {    //注入    @Autowired    private RedisTemplate redisTemplate;    @Autowired    private StringRedisTemplate stringRedisTemplate;    @Test    public void testBound(){        BoundValueOperations<String, String> name = stringRedisTemplate.boundValueOps("name");        name.set("张安");        name.append("加油");//        stringRedisTemplate.boundGeoOps();//        stringRedisTemplate.boundHashOps();//        stringRedisTemplate.boundListOps();//        stringRedisTemplate.boundSetOps();//        stringRedisTemplate.boundZSetOps();//        stringRedisTemplate.boundStreamOps();                        //        redisTemplate.boundValueOps();//        redisTemplate.boundGeoOps();//        redisTemplate.boundHashOps();//        redisTemplate.boundListOps();//        redisTemplate.boundSetOps();//        redisTemplate.boundStreamOps();    }

总结

1.针对于日后处理key value都是String使用StringRedisTemplate
2.针对于日后处理的key value存在对象使用RedisTemplate
3.针对于同一个key多次操作可以使用boundXXxOps () value List set Zset. Hash的api简化书写

redis在项目中的运用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F5lYN0fp-1619789926316)(redis.assets/image-20201118143422854.png)]

分布式缓存实现

  • 什么是缓存(Cache)

    定义:计算机内存中的一段数据

  • 缓存(内存)中的数据特点

    读写快 断电即失

  • 如何使用缓存,他解决了什么问题

    提高网站吞吐量,运行效率,为了减轻数据库的访问压力

  • 注意:

    使用缓存时,一定是数据库中的数据极少发生修改,更多用于查询

  • 本地缓存与分布式缓存

    本地缓存:存在应用服务器内存中数据称之为本地缓存(local cache)
    分布式缓存:存储在当前应用服务器内存之外数据称之为分布式缓存(distribute cache)
    集群:将同一种服务的多个节点放在一起共同对系统提供服务过程称之为集群
    分布式:有多个不同服务集群共同对系统提供服务这个系统称之为分布式系统(distribute system)

  • 利用mybatis自身本地缓存结合redis实现分布式缓存
    a.mybatis中应用级缓存(二级缓存)sqlSessionFactory级别缓存﹑所有会话共享

    b.如何开启(二级缓存)
    mapper.xml 本地缓存
    c.查看cache标签缓存实现
    结论:mybatis底层默认使用的是 org.apache.ibatis.cache.impl.PerpetualCache实现
    d.自定义Rediscache实现
    1.通过mybatis默认cache源码得知可以使用自定义cache类implements Cache接口并对里面方法进行实现

    ​ 2.使用Rediscache实现
    ​ <cache type=”xxzx.RedisCache" / >

    ​ 3.解决关联关系(共享缓存)

    <cache-ref namespace="com.zhangan.dao.xxdao"></cache-ref>
    

缓存优化策略

将放入缓存的key长度减短一些

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-odn7hIbK-1619789926316)(redis.assets/image-20201119110728778.png)]

package com.zhangan.cache;import com.zhangan.util.ApplicationContextUtils;import org.apache.ibatis.cache.Cache;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.StringRedisSerializer;import org.springframework.util.DigestUtils;import java.util.concurrent.locks.ReadWriteLock;//自定义redis缓存实现public class RedisCache implements Cache {    //当前放入缓存的mapper的namespace    private final String id;    //必须存在构造方法    public RedisCache(String id) {        this.id = id;    }    //封装redisTemplate    private RedisTemplate getRedisTemplate() {        //使用application工具类获取redisTemplate        RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");        redisTemplate.setKeySerializer(new StringRedisSerializer());        redisTemplate.setHashKeySerializer(new StringRedisSerializer());        return redisTemplate;    }    //封装一个对key进行md5处理方法    private String getKeyToMD5(String key){        return DigestUtils.md5DigestAsHex(key.getBytes());    }    @Override    public ReadWriteLock getReadWriteLock() {        return null;    }    //返回cache唯一标识    @Override    public String getId() {        return this.id;    }    //放入缓存值    @Override    public void putObject(Object key, Object value) {        //使用redisHash类型作为缓存存储模型  key  hashKey value        getRedisTemplate().opsForHash().put(id.toString(), getKeyToMD5(key.toString()), value);    }    //缓存中获取数据    @Override    public Object getObject(Object key) {        //使用redisHash类型作为缓存存储模型  key  hashKey value        return getRedisTemplate().opsForHash().get(id.toString(), getKeyToMD5(key.toString()));    }    //注意:这个方法为mybatis保留方法默认没有实现后续版本可能会实现    @Override    public Object removeObject(Object key) {        return null;    }    //增删改都走这个  清空缓存  之后查询重新建立新的缓存    @Override    public void clear() {        //清空namespace        getRedisTemplate().delete(id.toString());//清空缓存    }    //用来计算缓存的数量    @Override    public int getSize() {        //获取hash中的key   value数量        return getRedisTemplate().opsForHash().size(id.toString()).intValue();    }}

相关概念

缓存穿透(击穿)

定义:客户端查询数据库没有的数据记录,导致缓存在这种情况下无法利用

mybatis中cache解决了缓存穿透:将数据库中没有查询到结果也进行缓存

缓存雪崩

定义:在系统运行的某一时刻,突然系统中缓存全部失效,恰好在这一时刻涌来大量客户端请求,导致所有模块缓存无法利用,大量请求涌向数据库导致极端情况,数据库阻塞或挂起

缓存存储时:业务系统非常大模块多业务数据不同―不同模块在放入缓存时都会设置一个缓存超时时间
解决方案:

1.缓存永久存储[不推荐]
2.针对于不同业务数据一定要设置不同超时时间

//        解决雪崩//        if(id.equals("com.zhangan.dao.UserDao")){//            getRedisTemplate().expire(id.toString(),30, TimeUnit.MINUTES);//        }

Redis主从复制

无法解决:1.master节点出现故障的自动故障转移|

  • 主从复制

    主从复制架构仅仅用来解决数据的冗余备份,从节点仅仅用来同步数据

  • 架构图

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fWQ0n8i0-1619789926317)(redis.assets/image-20201119123944291.png)]

  • 搭建主从复制

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2dC2F3I6-1619789926317)(redis.assets/image-20201119124438368.png)]

​ 分别启动即可主从复制

Redis哨兵机制

Sentinel(哨兵)是Redis的高可用性解决方案︰由一个或多个Sentinel 实例组成的Sentinel系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器。简单的说哨兵就是带有自动故障转移功能的主从架构。

无法解决:1.单节点并发压力问题 2.单节点内存和磁盘物理上限

  • 架构原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PcQme1ET-1619789926318)(redis.assets/image-20201119125553058.png)]

  • 搭建过程

    #1.在主节点上创建哨兵配置在Master对应redis .conf同目录下新建sentinel.conf文件,名字绝对不能错;#2配置哨兵,在sentinel.conf文件中填入内容:sentinel monitor   被监控数主从架构的名字(自己起名字)   ip(主机IP地址) port(主机端口) 1(启动哨兵的数量)#3.启动哨兵模式进行测试./redis-sentinel(在源目录里的脚本 复制过来)     /myredis/sentinel.conf说明:这个后面的数字2,是指当有两个及以上的sentinel服务检测到master宕机,才会去执行主从切换的功能。
    
    • 配置文件

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CSIK8vry-1619789926319)(redis.assets/image-20201119160851769.png)]

  • spring boot如何整合

    #redisspring:  redis:    sentinel:      master:(之前起的名字)      nodes:192.xx.xx.x:6379,192.xx.xx.x:6380(多个哨兵的结点)
    

Redis集群

​ Redis在3.0后开始支持Cluster(模式)模式,目前redis的集群支持节点的自动发现,支持slave-master选举和容错,支持在线分片(sharding shard )(分槽的意思)等特性。|

  • 架构图

    ping pong协议 内部的二进制协议 互相查看是否存活

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WEOCR6UV-1619789926320)(redis.assets/image-20201119161402384.png)]

  • 集群细节

    集群维护主结点 槽维护value

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I8fYXZW7-1619789926320)(redis.assets/image-20201119161940665.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LFXcLVxS-1619789926320)(redis.assets/image-20201119200213026.png)]

  • 搭建redis集群

    判断一个是集群中的节点是否可用,是集群中的所用主节点选举过程,如果半数以上的节点认为当前节点挂掉,那么当前节点就是挂掉了,所以搭建redis集群时建议节点数最好为奇数,搭建集群至少需要三个主节点,三个从节点,至少需要6个节点。

Redis实现分布式Session管理

管理机制

​ redis的session管理是利用spring提供的session管理解决方案,将一个应用session交给Redis存储,整个应用中所有session的请求都会去redis中获取对应的session数据。

开发Session管理

  1. 引入依赖

    <dependency>    <groupId>org.springframework.session</groupId>    <artifactId>spring-session-data-redis</artifactId></dependency>
    
  2. 开发Session管理配置类(自己写个类 加上注解 不需要写内容)

    @Configuration@EnableRedisHttpSessionpublic class RedisSessionManager {}
    
  3. 打包测试

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值