redis

nosql分类
键值key-value存储的nosql 1.redis,Memcached
文档型数据库 MongoDb
图形数据库 Graph
redis和memcached是key-value的nosql,主要用来做缓存的

​ redis是一个高性能的开源的、c语言开发的nosql,数据保存在内存中

​ redis是以key-value形式存储,和传统的关系型数据库不一样,不一定遵循传统数据库的一些
​ 基本要求,比如说,不遵循sql标准,事务,表结构等等,redis严格上不是一种数据库,是一种
​ 数据结构化存储方法的集合

​ 数据结构:数组,list,set,map

​ redis提供了一堆操作方法,我们使用这些方法就可以存入字符串,组织成各种类型数据结构
​ (String,list,set,map等),使用起来更加方便。

特点(优势)
1.数据保存在内存,存取速度快,并发能力强
2.支持存储的value类型相对更多,包括String(字符串),list(列表),set(集合),zset(sorted set–有序集合)和hash(哈希)
3.支持集群(主从同步),数据可以主服务器向任意数量从服务器上同步,从服务器可以是关联其他从服务器的主服务器。
4.支持持久化,可以将数据保存在硬盘的文件中
5.支持订阅/发布功能 qq群-群发

总结:
1.开源免费
2.数据存储:存放在内存,还支持持久化-存取速度快,并发能力强,数据安全高
3.支持value类型更多
4.支持多个语言客户端
5.还支持集群(支持高并发,海量数据)

在这里插入图片描述

存储过期:一个数据存储时为他设置过期时间,时间一到数据就没有了,例如道具,会员,优惠券,订单,红包等

入门:key
set name “zs” 设置name的值为zs
get name
expire name 20 设置name的过期时间为20s
ttl name 查看name还有多少剩余时间
keys * 查看所有的key
del name 删除name

设置密码
requirepass 123456 //在redis配置文件中redis.windows.config(前面不能有空格)
启动的时候要用配置文件启动 redis-server.exe redis.windows.conf,否则不成功

string

mset age 18 id 01 设置多个
mget age id 获取多个
incr age 自增
decr age 自减
incrby age 5 自增5个
decrby age 5 自减5个

flushall 清空整个redis服务器数据,所有数据库清空
flushdb 清空当前库 ,redis默认有16个数据库,默认是0,切换1,select 1

list

lpush stars ls //从左给stars插入ls
rpush stars zs //从右给stars插入zs
lrange stars 0 -1 //展示stars所有值
lpop stars //删除stars最左边的值
rpop stars //删除stars最右边的值

lrem stars 2 ls //从左开始删除stars 2个ls
lrem stars 0 ls //删除所有的ls
lrem stars -2 ls //从右开始删除stars 2个ls

lindex 2 //展示下标2

ltirm stars 0 1 //只保留stars下标0到1

hash则是h开头

连接redis

  Jedis jedis = new Jedis("localhost");
    //    jedis.auth("123456");//密码,配置文件中写的redis.windows.conf,没有可以不写
        jedis.set("name","肖战");
        System.out.println(jedis.get("name"));
        jedis.close();

连接池连接

 JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(10);//最大连接池
        config.setMaxIdle(2);//最大空闲连接
        config.setMaxWaitMillis(2000);//最大等待时间
        config.setTestOnBorrow(true);//在获取连接的时候验证连接是否有效
        JedisPool pool = new JedisPool(config,"localhost",6379,2000,null,0);//配置,ip,端口,超时时间,密码,数据库
        Jedis jedis = pool.getResource();//获取连接
        jedis.flushAll();//清空所有数据库数据
        jedis.set("name","zs");
        System.out.println(jedis.get("name"));
        jedis.close();//如果检测到使用了连接池就是归还,否则就是关闭连接
        pool.destroy();//销毁连接池对象

封装工具类测试

package org;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public enum JedisUtil {
    INSTANCE;
    static JedisPool pool =null;
    static{
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(10);//最大连接池
        config.setMaxIdle(2);//最大空闲连接
        config.setMaxWaitMillis(2000);//最大等待时间
        config.setTestOnBorrow(true);//在获取连接的时候验证连接是否有效
         pool = new JedisPool(config,"localhost",6379,2000,null,0);//配置,ip,端口,超时时间,密码,
    }
    public Jedis getConnection(){
        return pool.getResource();
    }

    public void closeConnection(Jedis jedis){
        if(jedis!=null) {
            jedis.close();
        }
    }

}


=============================================测试
        Jedis jedis = JedisUtil.INSTANCE.getConnection();
        jedis.flushAll();
        jedis.set("name","zs");
        jedis.set("age","15");
        jedis.keys("*").forEach(System.out::println);
        jedis.del("name");
        System.out.println(jedis.exists("name"));
        JedisUtil.INSTANCE.closeConnection(jedis);

springdata:spring对数据操作支持规范

springboot使用redis

pom依赖

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>
spring.redis.database=0
spring.redis.port=6379
spring.redis.host=localhost
#连接池最大连接数
spring.redis.jedis.pool.max-active=8
#连接池最大空闲连接
spring.redis.jedis.pool.max-idle=8
#连接池最大阻塞等待时间(复数没有限制)
spring.redis.jedis.pool.max-wait=2000ms
#连接池中最小空闲连接
spring.redis.jedis.pool.min-idle=0
  @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate; //操作字符串

    @Test
    public void test(String[] args) {
        System.out.println(redisTemplate);
        redisTemplate.opsForValue().set("name","张三");
        System.out.println(redisTemplate.opsForValue().get("name"));

        stringRedisTemplate.opsForValue().set("age","ls");
        

redis常用配置(redis.windows.conf)

在这里插入图片描述

保存策略,只要满足,60s操作10000次,300s操作10次,900s操作1次,就把内存持久化到磁盘

(绑定)bind 127.0.0.1 只允许ip地址为127.0.0.1的可以访问

protected-mode yes 保护模式,开启

port 6379 端口号

timeout 0 最大超时时间,即不操作(为0表示不归还)

loglevel notice 日志级别为notice

logfile " " 指定日志文件在当前路径下

redis持久化配置(重点)
rdb就是上面说的的save

在这里插入图片描述

满足save条件的持久化到磁盘,不满足持久化到日志中再保存到磁盘

在这里插入图片描述

小结:在这里插入图片描述

淘汰策略:

淘汰一些数据,达到redis数据都是有效的,节约内存资源。选择合适的淘汰策略

​ volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰
​ volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰
​ allkeys-lru:从所有数据集中挑选最近使用最少的数据淘汰

redis确定驱逐某个键值对后,会删除这个数据并,并将这个数据变更消息发布到本地(AOF持久化)
和从机(主从连接)

如何使用?
在配置文件中,设置淘汰策略,设置内存大小maxmemory

面试题
1.那些场景使用redis?
点赞,缓存,防攻击,做计数器,去重
2.使用什么来操作redis
spring-boot-starter-data-redis
3.你使用过memcached?
没有,使用redis,redis比他优秀,支持的数据类型更多,不仅支持存储在内存中,还支持在磁盘中
4.为什么要使用redis-优点
存储在内存中效率高,支持很多数据类型,支持存储在内存和磁盘中
5.redis怎么实现栈和队列
通过控制list一边进一边出,就是队列,控制进出同一个地方就是栈
6.事务四大特性
原子性,一致性,隔离性,持久性
7.为什么要使用连接池
节省资源,提高效率
8.redis是怎么存储数据的?
数据存储在内存中,为了安全支持持久化,rdb,aof
9.redis数据满了时会怎么办?
淘汰策略,在配置文件中设置一个最大内存和淘汰策略

什么是缓存?
通常将数据从数据库中同步一份到内存中,客户端直接从内存中查询数据,减少了和数据库的交互次数,提高查询性能(内存读写很快),减轻数据库压力

降低数据库压力,直接从缓存中查询
提高查询效率,内存级别查询效率要高于磁盘查询

哪些数据适合缓存?
1.经常查询的热点数据
2.不经常变的数据(数据变化会导致缓存中的数据跟着变,如果比较频繁,性能开销比较大)

缓存的流程
1.第一次查询,先看缓存是否有,有就返回
2.如果缓存没有数据,去数据库查询数据
3.把数据同步一份到缓存
4.返回数据
注意:数据库数据被修改,缓存要清空,或者重置

缓存分类:
传统缓存:mybatis二级缓存,jpa二级缓存。数据放到当前服务器内存中
中央缓存:redis,memcached等key value nosql

传统缓存方案的优缺点
1.再集群环境中,每个应用都有一个本地缓存,当缓存发生改变会造成不同步问题
2.本地缓存本身要占用应用的内存空间

分布式缓存方案-中央缓存
使用redis作为缓存,解决缓存不同步问题,redis是独立的服务,缓存不用占用应用本身的内存空间

springboot操作

1.依赖
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
2.配置
spring:
  redis:
    database: 0
    port: 6379
    host: localhost
    password: 123456
    jedis:
      pool:
        max-active: 8
        max-idle: 8
        max-wait: 2000ms
        min-idle: 0
3.实现类中
@Autowired
    private RedisTemplate redisTemplate;
    
    public List<CourseType> treeData() {
  Object object =redisTemplate.opsForValue().get(CourseConstants.COURSE_TYPE_DATA);
        if(object==null){
            List<CourseType> courseTypes = courseTypeMapper.loadTreeData();
           redisTemplate.opsForValue().set(CourseConstants.COURSE_TYPE_DATA,courseTypes);
            return courseTypes;
        }else{
            List<CourseType> courseTypes = (List<CourseType>) object;
            return courseTypes;
        }
 
        //以下三个方法需要重新实现新的逻辑,数据库数据发生改变后,redis数据也要改
    @Override
    public boolean insert(CourseType entity) {
        redisTemplate.delete(CourseConstants.COURSE_TYPE_DATA);
        return super.insert(entity);
    }

    @Override
    public boolean updateById(CourseType entity) {
        redisTemplate.delete(CourseConstants.COURSE_TYPE_DATA);
        return super.updateById(entity);
    }

    @Override
    public boolean deleteById(Serializable id) {
        redisTemplate.delete(CourseConstants.COURSE_TYPE_DATA);
        return super.deleteById(id);
    }

注意,存储redis的类需要序列化,实现序列化接口

以上操作太冗余,所以进行优化Spring cache

1.导包

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

2.配置

spring:
  redis:
    database: 0
    port: 6379
    host: localhost
    password: 123456
    jedis:
      pool:
        max-active: 8
        max-idle: 8
        max-wait: 2000ms
        min-idle: 0

3.配置类

@Configuration
public class CacheConfig extends CachingConfigurerSupport {

    @Resource
    private RedisConnectionFactory factory;

    @Bean
    @Override
    public KeyGenerator keyGenerator() {
        return (o,method,objects)->{
            StringBuilder sb = new StringBuilder();
            sb.append(o.getClass().getName()).append(".");
            sb.append(method.getName()).append(".");
            for (Object obj: objects) {
                sb.append(obj.toString());
            }
            System.out.println("keyGenerator=" + sb.toString());
            return sb.toString();
        };
    }

    @Bean
    public RedisTemplate<Object,Object> redisTemplate(){
        RedisTemplate<Object,Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        //JSON格式序列化
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        //key的序列化
        redisTemplate.setKeySerializer(genericJackson2JsonRedisSerializer);
        //value的序列化
        redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
        //hash结构key的虚拟化
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        //hash结构value的虚拟化
        redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
        return redisTemplate;
    }
}

4.需要的地方加 @Cacheable //触发缓存写入 @CacheEvict触发缓存清除

    @Override
   @Cacheable(cacheNames = CourseConstants.COURSE_TYPE_DATA,key = "'All'")
    public List<CourseType> treeData() {
      return courseTypeMapper.loadTreeData();
    }

 //以下三个方法需要重新实现新的逻辑,数据库数据发生改变后,redis数据也要改
   @CacheEvict(cacheNames = CourseConstants.COURSE_TYPE_DATA,key = "'All'")
   @Override
   public boolean insert(CourseType entity) {
       redisTemplate.delete(CourseConstants.COURSE_TYPE_DATA);
       return super.insert(entity);
   }
    @CacheEvict(cacheNames = CourseConstants.COURSE_TYPE_DATA,key = "'All'")
    @Override
    public boolean updateById(CourseType entity) {
        redisTemplate.delete(CourseConstants.COURSE_TYPE_DATA);
        return super.updateById(entity);
    }
    @CacheEvict(cacheNames = CourseConstants.COURSE_TYPE_DATA,key = "'All'")
    @Override
    public boolean deleteById(Serializable id) {
        redisTemplate.delete(CourseConstants.COURSE_TYPE_DATA);
        return super.deleteById(id);
    }

但是上面这种方式,可能会有缓存击穿/雪崩

缓存穿透
当查询redis和数据库都没有没有的数据,而用户不断发起请求,会导致数据库压力过大
解决方法 1.业务层校验数据 2.不存在数据设置短过期时间

缓存击穿
当redis中热点key过期的瞬间,大量请求访问,全部到达数据库,从而压垮数据库
解决方法 1.设置热点数据不过时 2.快过时的时候取更新(定时任务) 3.加

缓存雪崩
Redis中缓存的数据大面积同时失效,引起数据库压力过大甚至宕机
解决方法 1.集群 2.数据预热(提前走一遍) 3.热点数据永不过期

解决方案:
1.为了解决穿透问题,数据库没有的数据也要在redis中设置一个null
2.雪崩和击穿
方案1 热点数据永不过期
方案2 双重判断上锁(击穿) ,分开过期(雪崩)

    private Lock lock = new ReentrantLock();

    @Override
    public List<CourseType> treeData() {

        Object object = redisTemplate.opsForValue().get(CourseConstants.COURSE_TYPE_DATA);
        if(object==null){
            lock.lock();//击穿
            object = redisTemplate.opsForValue().get(CourseConstants.COURSE_TYPE_DATA);
            if(object!=null){         //第一个请求进来没有数据,那么就会在下面存入缓存,然后第二个人进来直接就可以在此处获取,避免缓存击穿
                return (List<CourseType>) object;
            }
            List<CourseType> courseTypes = courseTypeMapper.loadTreeData();
            //如果没有数据则放入一个空
            if(courseTypes == null){
                courseTypes = new ArrayList<>();
            }
            //方案1:热点数据永不过期
            //redisTemplate.opsForValue().set(CourseConstants.COURSE_TYPE_DATA,courseTypes);
            //方案2:热点数据要过期,但不一起过期
            redisTemplate.opsForValue().set(CourseConstants.COURSE_TYPE_DATA,courseTypes,new Random().
                    nextInt(10000)+30*24*60*60, TimeUnit.SECONDS);
            System.out.println("没有");
            lock.unlock();
            return courseTypes;
        }else{
            List<CourseType> courseTypes = (List<CourseType>) object;
            System.out.println("有");
            return courseTypes;
        }
    }
   @Override
   public boolean insert(CourseType entity) {
       redisTemplate.delete(CourseConstants.COURSE_TYPE_DATA);
       return super.insert(entity);
   }
    @Override
    public boolean updateById(CourseType entity) {
        redisTemplate.delete(CourseConstants.COURSE_TYPE_DATA);
        return super.updateById(entity);
    }
    @Override
    public boolean deleteById(Serializable id) {
        redisTemplate.delete(CourseConstants.COURSE_TYPE_DATA);
        return super.deleteById(id);
    }

这种双写存在延迟,如果并发来了就可能出问题,所以解决方案如下

缓存问题-双写一致性

​ 延时双删:
​ 先删缓存,再删除数据库
​ 睡眠3s
​ 放入消息队列–消费者一直删除直到删除成功,一直不成功则管理员手动删除

 @Override
   public boolean insert(CourseType entity) {
       redisTemplate.delete(CourseConstants.COURSE_TYPE_DATA);
       boolean result = super.insert(entity);
       try {
           TimeUnit.SECONDS.sleep(3);
       }catch (InterruptedException e){
           e.printStackTrace();
       }
       //@TODO rabbitMQ确保一定成功
       //删除缓存
       redisTemplate.delete(CourseConstants.COURSE_TYPE_DATA);
       return result;
   }
    //@CacheEvict(cacheNames = CourseConstants.COURSE_TYPE_DATA,key = "'All'")
    @Override
    public boolean updateById(CourseType entity) {
        redisTemplate.delete(CourseConstants.COURSE_TYPE_DATA);
        boolean result = super.updateById(entity);
        try {
            TimeUnit.SECONDS.sleep(3);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        //@TODO rabbitMQ确保一定成功
        //删除缓存
        return result;
    }
    //@CacheEvict(cacheNames = CourseConstants.COURSE_TYPE_DATA,key = "'All'")
    @Override
    public boolean deleteById(Serializable id) {
        redisTemplate.delete(CourseConstants.COURSE_TYPE_DATA);
        boolean result = super.deleteById(id);
        try {
            TimeUnit.SECONDS.sleep(3);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        //@TODO rabbitMQ确保一定成功
        //删除缓存
        return result;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值