Redis简介

        Redis是一个高性能的内存数据库,redis内部是一个key-value存储系统,以Key-value的方式储存数据,可以作为缓存使用。Redis以内存作为数据存储介质,所以读写数据的效率极高,远远超过数据库,Redis基于内存运行并支持持久化的NoSQL数据库,是当前最热门的NoSql数据库之一,也被人们称为数据结构服务器。

为什么要使用缓存?

1.高并发(原因是它可以直接访问内存,而以往我们用的是数据库(硬盘),他提高了访问效率,解决了数据库服务器压力。)

2.高性能(基于内存,内存IO效率远远高于磁盘)

        

官方数据表示Redis读的速度是110000次/s,写的速度是81000次/s 。redis尽量少写多读,符合缓存的适用要求。单机redis支撑万级,如果10万+就需要用redis replication模式,也就是集群模式;

MySQL宕机:

MySQL的连接数存在瓶颈,连接过大可能会导致MySQL宕机

解决办法:

        1.部署多个MySQL服务,主从复制

        2.部署缓存,承担一部分的并发

Redis的特点:

1.性能高

2.支持多重储存类型(包括string(字符串)、list(链表)、set(集合)、zset(sorted set –有序集合)和hash(哈希类型,类似于Java中的map))

3.丰富的特性(发布订阅、事务、过期策略等)

4.支持持久化

5.单线程(避免了上下文的切换,线程同步问题等)

Redis持久化:

储存在Redis中的数据是持久化的,断电或重启后,数据也不会丢失。因为Redis的存储分为内存存储、磁盘存储和log文件三部分,重启后,Redis可以从磁盘重新将数据加载到内存中,这些可以通过配置文件对其进行配置,正因为这样,Redis才能实现持久化。 

 Redis怎么在项目中使用的?

1.导入依赖

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

2.配置文件

spring.redis.host=localhost
spring.redis.port=6379
spring.redis.database=0
spring.redis.jedis.pool.max-active=100
spring.redis.jedis.pool.max-wait=100ms
spring.redis.jedis.pool.max-idle=100
spring.redis.jedis.pool.min-idle=10

3.配置RedisTemplate

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

4.使用RestTemplate 常用方法:

        1.opsForValue 获得string类型的操作对象

        2.opsForHash 获得hash类型的操作对象

5.缓存使用的流程

        按id查询商品的过程

        1. 以id为键查询Redis缓存,如果能查到就返回数据,结束

        2.如果查不到,就查询数据库,数据库查到,缓存到Redis,返回数据

        3.如果数据库查不到,返回null,结束

        4.增删改数据库的同时,要修改缓存

 

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    public static  final String KEY = "Employee-";

    @Override
    public Product findById(Long id) {
        //获得key-value格式的操作对象
        ValueOperations<String, Object> ops = redisTemplate.opsForValue();
        //1 先查询redis中是否存在该id的数据
        Product emp = (Product) ops.get(KEY + id);
        if (emp != null) {
            //2.如果有 就直接返回数据
            System.out.println("redis中存在数据,返回" + emp);
            return emp;
        }
        // 3.如果没有 就查询数据
        emp = productMapper.selectById(id);
        if(emp != null){
            //4.如果数据库有 就保存到redis中,返回数据
            System.out.println("查询数据库,返回" + emp);
            ops.set(KEY + id,emp);
            return  emp;
        }
        System.out.println("数据库不存在");
        //5.如果数据库没有 就返回null
        return null;
    }

Redis的数据类型(Redis的数据以key-value方式存储)

数据类型有:

        1.string 字符串(适合保存单个数据)

        2.hash 哈希(适合保存复杂类型数据,如:对象)

        3.list 列表(适合保存有序的、可重复的数据)

        4.set 无序集合(适合保存无序的,不可重复的数据)

        5.zset 有序集合(适合保存有序的,不可重复的数据)

储存 :

存储字符串

        set key value
        get key

        可以设置失效时间
        set key value EX 10  //10秒钟之后失效

存储Hash值(hash存储,一般可以用来存储Java中的一个完整的自定义对象

//hmset是存储hash值的指令,
//user是当前hash的key
//name "zhangsan" age 23 sex "nan" 是 key对应的值
127.0.0.1:6379> hmset user name "zhangsan" age 23 sex "nan"
OK
//hmget获取hash中的某一个属性的值
127.0.0.1:6379> hmget user name
1) "zhangsan"
127.0.0.1:6379> hmget user age
1) "23"
//hgetall是获取hash中的所有属性对应的值
127.0.0.1:6379> hgetall user
1) "name"
2) "zhangsan"
3) "age"
4) "23"
5) "sex"
6) "nan"

存储List列表(有序列表)

        采用的链表结构进行数据存储

        lpush 从右向左添加

        rpush 从左向右添加

        lrange key start stop

//lpush用来存储一个列表的命令。interesting是列表的名称,"basketball"列表中的值
127.0.0.1:6379> lpush interesting "basketball"
(integer) 1
127.0.0.1:6379> lpush interesting "football" "ball"
(integer) 3
//lrange输出列表中的数据的命令, interesting就是列表的名称 。 0 2是列表的开始输出索引和结束索引。
127.0.0.1:6379> lrange interesting 0 2
1) "ball"
2) "football"
3) "basketball"

存储Set集合(无序集合)

        不能有重复的数据

sadd key member //存数据
smembers key //取数据
案例:
127.0.0.1:6379> sadd strset "a" "b" "c"
(integer) 3
127.0.0.1:6379> smembers strset
1) "b"
2) "c"
3) "a"

存储zset集合(有序集合)

zadd key score member  (score是一个数字,zset就是通过这个数字进行排序,可以重复)
zrangebyscore key 0 1000 //通过分数排序输出
有序集合是按照score进行排序

Redis的常见问题

1.缓存击穿

        高并发的情况下,短时间内缓存会被穿过,请求直接打到数据库上,可能导致数据库压力过大。

解决方案:对代码上锁(双重检查锁)

2.缓存穿透

        高并发的情况下,如果查询不存在的数据,因为缓存和数据库都不存在,请求都会打到数据库上,可能导致系统崩溃。

解决方案:

1) 保存不存在的数据到缓存中,设置一定过期时间

2) 布隆过滤器(直接过滤掉不存在数据的请求) 不能准确判断是否存在数据,能准确判断数据不存在

3.缓存雪崩

        高并发的情况下,缓存服务器重启或热点数据同时过期,全部访问数据库,导致数据库宕机

解决方案:

1)配置缓存集群

2)尽量给热点数据设置不一样的过期时间,相对均匀

解决代码:

 public Goods getGoodsById(Long id){
        ValueOperations<String, Object> ops = redisTemplate.opsForValue();
        Object value = ops.get(TYPE + id);
        //外层先读缓存,缓存如果有,就不执行同步块
        if(value == null) {
            synchronized (this) {
                //1) 以id为键查询Redis缓存,如果能查到就返回数据,结束
                value = ops.get(TYPE + id);
                //2)如果查不到,就查询数据库
                if (value == null) {
                    System.out.println("缓存不存在,查询数据库");
                    // 数据库查到,缓存到Redis,返回
                    Goods goods = this.getById(id);
                    if (goods != null) {
                        System.out.println("数据库存在,保存到缓存");
                        ops.set(TYPE + id, goods);
                    } else {
                        System.out.println("数据库不存在,返回null");
                        //保存空数据到缓存中,设置过期时间
                        ops.set(TYPE + id,new Goods(),30, TimeUnit.SECONDS);
                    }
                    return goods;
                } else {
                    System.out.println("缓存存在,返回" + value);
                    //如果能查到就返回数据,结束
                    return (Goods) value;
                }
            }
        }
        System.out.println("缓存存在,返回" + value);
        return (Goods) value;
    }

声明式缓存

SpringBoot项目需要的依赖,配置文件同上

1.在启动类上添加注解 @EnableCaching

 2.Redis的配置类

@Configuration
public class RedisConfig {

    @Bean
    public RedisCacheConfiguration provideRedisCacheConfiguration(){
        //加载默认配置
        RedisCacheConfiguration conf = RedisCacheConfiguration.defaultCacheConfig();
        //返回Jackson序列化器
        return conf.serializeValuesWith(
                RedisSerializationContext.SerializationPair
                .fromSerializer(new GenericJackson2JsonRedisSerializer()));
    }
}

3.缓存注解

        @CacheConfig 使用在Service类上,如:@CacheConfig(cacheNames = "books")

        @Cacheable 使用在查询方法上,让方法优先查询缓存

        @CachePut 使用在更新和添加方法上,数据库更新和插入数据后同时保存到缓存里

        @CacheEvict 使用在删除方法上,数据库删除后同时删除缓存

注意:实体类必须实现序列化接口:

@CacheConfig(cacheNames = "brand")
@Service
public class BrandServiceImpl extends ServiceImpl<BrandMapper, Brand> implements IBrandService {

    @Autowired
    private BrandMapper brandMapper;

    @Cacheable(cacheNames = "brand-category",key = "T(String).valueOf(#cid)")
    @Override
    public List<Brand> findBrandsByCategory(Integer cid) {
        return brandMapper.selectBrandsByCategory(cid);
    }

    @Cacheable(key = "T(String).valueOf(#id)")
    @Override
    public Brand findBrandById(Long id) {
        return this.getById(id);
    }

    @CachePut(key = "T(String).valueOf(#brand.id)")
    @Override
    public Brand saveBrand(Brand brand) {
        this.saveOrUpdate(brand);
        return brand;
    }

    @CacheEvict(key = "T(String).valueOf(#id)")
    @Override
    public void deleteBrand(Long id) {
        this.removeById(id);
    }

    @Cacheable(cacheNames = "brand-page",key = "T(String).valueOf(#page)")
    @Override
    public IPage<Brand> pageBrands(Long page) {
        return this.page(new Page<>(page,10));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值