Getting started with Redis

learning target

  Redis简介
  Redis的安装使用
  Redis的数据类型
  SpringBoot整合Redis
  声明式缓存
  Redis的常见问题
  事务
  分布式锁
  持久化策略
  淘汰策略

Introduction to Redis

Redis is completely open source, complies with the BSD protocol, and is a high-performance key-value database.

Features:

  1. Redis supports data persistence. The data in the memory can be stored on the disk, and it can be loaded again for use when restarting.
  2. Redis not only supports simple key-value type data, but also provides storage for list, set, zset, hash and other data structures.
  3. Redis supports data backup, that is, data backup in master-slave mode.

Advantages of Redis:

  1. Very high performance-Redis can read 110,000 times/s, and write 81,000 times/s.
  2. Rich data types-Redis supports Strings, Lists, Hashes, Set and Zset data type operations in binary cases.
  3. Atomic-All Redis operations are atomic, meaning that they either succeed in execution or fail and are not executed at all. A single operation is atomic. Multiple operations also support transactions, that is, atomicity, packaged by MULTI and EXEC instructions.
  4. Rich features-Redis also supports publish/subscribe, notification, key expiration and other features.

Redis installation and use

Linux installation

Install the gcc compiler first, which can be used to compile c, c++ and other codes

 yum -y install gcc //-y表示自动安装

Install Redis

 wget http://download.redis.io/releases/redis-3.2.5.tar.gz //下载redis安装包到usr/local
 tar xzf redis-2.8.17.tar.gz
 cd redis-2.8.17
 make

Redis configuration (enter the redis.conf file in the redis directory)

 cd /usr/local/redis-5.0.4
 vi redis.conf(本人的redis放在/usr/local)

Comment out bind 127.0.0.1

Protected mode no

Insert picture description here

Use Redis

1. Start the server src./redis-server …/redis.conf

Insert picture description here

2. Start the client src./redis-cli

Insert picture description here

Windows installation

  • Download the windows version of redis from the official website
  • Unzip
  • Start the server
  • Start the client
Insert picture description here

Redis data types

The data types are:

string (suitable for saving a single data)

 	set key value
 	get key
 	可以设置失效时间
 	set key value EX 10  //10秒钟之后失效
Insert picture description here

Hash (suitable for storing complex types of data, such as: generally can be used to store a complete custom object in 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 list (suitable for storing ordered and repeatable data)

Linked list structure used for data storage

lpush add from right to left

rpush add from left to right

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 unordered collection (suitable for storing unordered, non-repeatable data)

 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 ordered collection (suitable for storing ordered, non-repeatable data)

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

SpringBoot integrates Redis

1) Dependence

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

2) Configuration file

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) Configure 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)
Common methods of using RestTemplate :

  1. opsForValue obtains an operation object of string type
  2. opsForHash obtains the operation object of hash type
    ...

5) The process of using the cache

The process of querying products by id

1) Query the Redis cache with id as the key, return the data if it can be found, and end

2) If you can't find it, query the database, the database finds it, cache it to Redis, and return the data

3) If the database can't find it, return null and end

4) At the same time as adding, deleting and modifying the database, modify the cache

@Service
public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements IGoodsService {

    public static final String TYPE = "GOODS-";

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    public Goods getGoodsById(Long id){
        ValueOperations<String, Object> ops = redisTemplate.opsForValue();
        //1) 以id为键查询Redis缓存,如果能查到就返回数据,结束
        Object 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");
           }
           return goods;
        }else{
            System.out.println("缓存存在,返回"+value);
           //如果能查到就返回数据,结束
            return (Goods) value;
        }
    }
}

Declarative caching

The dependencies required by the SpringBoot project, the configuration file is the same as above

1. Add the annotation @EnableCaching on the startup class 2.
Redis configuration class

@Configuration
public class RedisConfig {

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

3) Cache annotations

@CacheConfig is used on the Service class, such as: @CacheConfig(cacheNames = "books")

@Cacheable is used in the query method, let the method first query the cache

@CachePut is used in the update and add methods. After the database is updated and the data is inserted, it is saved in the cache at the same time

@CacheEvict is used in the delete method, the cache is deleted at the same time after the database is deleted

@Caching can install multiple @CacheEvict in the delete method, and delete the cache at the same time after the database is deleted

Note: The entity class must implement the serialization interface

@CacheConfig(cacheNames = "GOOD")
@Service
public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goodss> implements IGoodService {
    @Autowired
    private GoodsMapper goodsMapper;

    @Cacheable(key = "T(String).valueOf(#id)")
    @Override
    public Goodss selectOneGoods(Long id) {
        return goodsMapper.selectOneGoods(id);
    }

    @Cacheable(cacheNames = "goods-page",key = "T(String).valueOf(#page.current)")
    @Override
    public IPage<Goodss> getSelectGoodss(IPage<Goodss> page) {
        return goodsMapper.getSelectGoodss(page);
    }

    @CachePut(key="T(String).valueOf(#goodss.id)")
    @CacheEvict(cacheNames = "goods-page",allEntries = true)
    @Override
    public Goodss saveGoods(Goodss goodss) {
         this.save(goodss);
         return goodss;
    }

    @CachePut(key="T(String).valueOf(#goodss.id)")
    @CacheEvict(cacheNames = "goods-page",allEntries = true)
    @Override
    public Goodss updateGoods(Goodss goodss) {
         this.updateById(goodss);
        return goodss;
    }


    @Caching(evict = {@CacheEvict (key = "T(String).valueOf(#id)"),@CacheEvict(cacheNames = "goods-page",allEntries = true)})
    @Override
    public void deleteGoods(Long id) {
        this.removeById(id);
    }

}

Frequently Asked Questions of Redis

1) Cache breakdown

In the case of high concurrency, the cache will be traversed in a short period of time, and the request will directly hit the database, which may cause excessive database pressure.

Solution: lock the code (double check lock)

2) Cache penetration

In the case of high concurrency, if you query data that does not exist, because the cache and the database do not exist, the request will hit the database, which may cause the system to crash.

solution:

​ 1) Save the non-existent data in the cache and set a certain expiration time

​ 2) Bloom filter (directly filter out the request that does not exist) Can not accurately determine whether there is data, and can accurately determine whether the data does not exist

3) Cache avalanche

In the case of high concurrency, the cache server restarts or the hot data expires at the same time, all access to the database, resulting in database downtime

solution:

​ 1) Configure the cache cluster

​ 2) Try to set different expiration times for hotspot data, relatively uniform

Solve the code

 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;
    }

Affairs

Redis's transaction is to package a series of operations and submit them together. There is no atomicity, isolation, and no rollback of transactions.

multi start transaction

Insert picture description here

exec commits the transaction

Insert picture description here

discard the transaction

watch monitors a certain data, if the data is modified in another transaction, the current modification is abandoned

Insert picture description here

to sum up:

If there is a syntax error in the transaction, the entire transaction cannot be executed; if there is a data error, the transaction can succeed partly and partly fail.

PS: incr increases the value, decr decreases the value

Distributed lock

Redis can be used as a distributed lock for common access by all services, and internal data can be monitored using the watch (optimistic lock) mechanism.

Pessimistic lock can guarantee thread safety, but the performance is low

Redis provides optimistic locking with high performance

Redis is single-threaded internally, and there is no thread safety issue

Case: The simulated spike function, the inventory of goods is 10, 1000 users come to grab, and there can be no overbought situation

@Repository
public class GoodsCache {

	Logger logger = Logger.getLogger(GoodsCache.class);
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

public String buy(){
    Boolean execute = redisTemplate.execute(new SessionCallback<Boolean>() {
        @Override
        public Boolean execute(RedisOperations redisOperations) throws DataAccessException {
            //开启乐观锁,当前线程执行exec释放锁之前,其他线程进行修改都会失败
            redisOperations.watch("goods_num");
            //读取商品数量
            int num = Integer.parseInt(redisOperations.opsForValue().get("goods_num").toString());
            if (num <= 0) {
                logger.error("已售完");
                return false;
            }
            //开启事务
            redisOperations.multi();
            //修改商品数量
            redisOperations.opsForValue().increment("goods_num", -1);
            //提交事务,释放锁
            List list = redisOperations.exec();
            //如果事务执行失败,返回的集合中数据为空
            if (list == null || list.isEmpty()) {
                logger.error("购买失败");
                return false;
            }
            logger.error("购买成功" + list.get(0));
            return true;
        }
    });
    return String.valueOf(execute);
}
}

@Controller
@RequestMapping("/products")
public class ProductController {
    @ResponseBody
    @RequestMapping("/buy")
    public JSONResult buy(){
        return new JSONResult(1,goodsService.buy());
    }
}

Persistence strategy

Why persist? Redis is an in-memory database. After the program is closed, the data will be cleared. Sometimes it is necessary to save the data in the memory in a file for a long time.

Persistence strategy

AOF: Persistence of data per second by default

RDB: Trigger the persistence operation according to the condition (any one)

900 1 modified 1 time in 900 seconds

300 10 Modify 10 times in 300 seconds

60 10000 Modify 10000 times in 60 seconds

Configuration method

RDB

Insert picture description here

AOF

appendonly yes / no yes enable AOF

appendfsync everysec saves every second

how to choose?

Allow a small amount of data loss, high performance-RDB

Only allow very little data loss-AOF

No data loss is allowed-RDB + AOF

Elimination strategy

Why should it be eliminated? Redis data is stored in memory. Too much data will cause overflow problems. Redis will eliminate some data according to certain strategies.

64-bit system, the upper limit is the upper limit of memory; 32-bit maximum 4G

Configure the maximum memory:

max-memory configuration 0 is unlimited (default)

LRU algorithm: Least Recently Used algorithm, eliminate long-term use of cache

Elimination strategy:

maxmemory-policy

value:

noevication (default) not eliminated

allkeys-lru (recommended) Use LRU to eliminate less used keys

Volatile-lru eliminates less used keys from expired keys

allkeys-random eliminates all keys randomly

Volatile-random is randomly eliminated from expired keys

Volatile-ttl eliminates short-lived keys from expired keys