一、Redis是什么

  • Redis是一个由C 语言开发的一个高性能键值对(key-value)的内存数据库,可以用作数据库、缓存、消息中间件等。
  • 它是一种NoSQL(not-only sql,泛指非关系型数据库)的数据库。

        特点:
            1.性能优越,数据存储在内存中,读写速度非常快,支持10W QPS。
            2.单线程单进程,线程是安全的,同时避免了线程上下文的切换带来的损耗。
            3.采用多路IO多路复用机制。
            4.支持丰富的数据类型:字符串(strings)、散列(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)等。
            5.支持数据持久化,可以将内存中数据保存在磁盘中,有RDB和AOF方式。
            6.支持主从复制、哨兵模式。
            7.另外也可以用作分布式锁、可以作为消息中间件使用、支持发布订阅。
            

 二、Redis为什么这么快

1. 因为Redis完全是基于内存操作,绝大部分请求是纯粹的内存操作,非常迅速,数据存在内存中,类似于HashMap(HashMap的优势就是查找和操作的时间复杂度是O(1))。
2. 采用单线程,避免了不必要的上下文切换和竞争条件,不存在多线程导致的CPU,也不用考虑锁所带来的问题。

(官方解释:因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了。)
3. 使用多路复用IO模型,非阻塞IO。

备注:

  • 1.为什么Redis在6.0之后加入了多线程(在某些情况下,单线程出现了缺点,多线程可以解决?)

            引入多线程说明Redis在有些方面,单线程已经不具有优势了。
            因为读写网络的read/write系统调用在Redis执行期间占用了大部分CPU时间,如果把网络读写做成多线程的方式对性能会有很大提升。
            Redis的多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程。之所以这么设计是不想Redis因为多线程而变得复杂,需要去控制key、lua、事务、LPUSH/LPOP等等的并发问题。
            举例:
                我们知道Redis可以使用del命令删除一个元素,如果这个元素非常大,可能占据几十兆或者是几百兆,那么在短时间内是不能完成的,这样一来就需要多线程的异步支持,现在删除工作可以在后台进行。

三、Redis支持的五种数据类型

首先,Redis内部使用一个redisObject对象来表示所以的key和value。

Redis是什么类型的数据库 redis是一个什么样的数据库_Redis

redisObject最主要的信息有:

        1.type:表示一个value对象具体是何种数据类型,一般有5种,在后续讲解。
        2.encoding是不同数据类型在Redis内存的存储方式。
        
        比如:type = string表示value存储的是一个普通字符串,那么encoding可以是raw或者int。

    

  • Redis中5种数据类型:

类型

简介

特性

场景

string (字符串)

二进制安全

可以包含任何数据,  比如jpg图片或者序列化对象

...

hash (字典)

键值对集合, 即编程语言中的map类型

适合存储对象,并且可以像数据库一样update一个属性

存储、读取、修改用户属性

list (列表)

链表(双向链表)

增删快,提供了操作某一元素的api

最新消息排行;消息队列

set (集合)

hash表实现,元素不重复

添加、删除、查找的复杂度都是O(1), 提供了求交集、并集、差集的操作

共同好友;利用唯一性,统计访问网站的所有ip

sorted set (有序集合)

set集合中增加score参数,元素按score有序排序

数据插入集合时,已经进行了天然排序

排行榜;带权重的消息队列

四、Redis缓存使用

  • 直接通过 RedisTemplate 来使用,使用 Spring Cache 集成 Redis

(1)pom.xml 中加入以下依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

(2)配置文件 application.yml 的配置:

server: port: 8082 
servlet: session: timeout: 30ms
spring:cache:type: redis 
redis: host: 127.0.0.1 
       port: 6379
       password:# redis默认情况下有16个分片,这里配置具体使用的分片,默认为0
       database: 0 
       lettuce: pool:# 连接池最大连接数(使用负数表示没有限制),默认8
       max-active: 100

(3)创建实体类 User.java:

public class User implements Serializable{
    private static final long serialVersionUID = 662692455422902539L;
    private Integer id;
    private String name;
    private Integer age;
    public User(){ }
    public User(Integer id, String name, Integer age){
        this.id = id;
        this.name = name;
        this.age = age; 
    }
    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 Integer getAge(){
        return age; 
    }
    public void setAge(Integer age){
        this.age = age; 
    }
    @Override
    public String toString(){
        return "User{" +"id=" + id +", name='" + name + '\'' +", age=" + age +'}'; 
    }
}

(4.1)RedisTemplate 的使用方式

默认情况下的模板只能支持 RedisTemplate<String, String>,也就是只能存入字符串,所以自定义模板很有必要。

添加配置类 RedisCacheConfig.java:

@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisCacheConfig { 
    @Bean
    public RedisTemplate<String, Serializable> redisCacheTemplate(LettuceConnectionFactory connectionFactory) { 
        RedisTemplate<String, Serializable> template = new RedisTemplate<>();
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setConnectionFactory(connectionFactory);returntemplate; 
    }
}

测试类:

@RestController
@RequestMapping("/user")
public class UserController{
    public static Logger logger = LogManager.getLogger(UserController.class);
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RedisTemplate<String, Serializable> redisCacheTemplate;
    @RequestMapping("/test")
    public void test() {
        redisCacheTemplate.opsForValue().set("userkey", new User(1, "张三", 25)); 
        User user = (User) redisCacheTemplate.opsForValue().get("userkey");
        logger.info("当前获取对象:{}", user.toString()); 
    }
}

(4.2)使用 Spring Cache 集成 Redis

Spring Cache 具备很好的灵活性,
不仅能够使用 SPEL(spring expression language)来定义缓存的 Key 和各种 Condition,还提供了开箱即用的缓存临时存储方案,
也支持和主流的专业缓存如 EhCache、Redis、Guava 的集成。

定义接口 UserService.java:

public interface UserService {
    User save(User user);
    void delete(int id);
    User get(Integer id);
}

接口实现类 UserServiceImpl.java:

@Service
public class UserServiceImpl implements UserService{
    public static Logger logger = LogManager.getLogger(UserServiceImpl.class);
    private static Map<Integer, User> userMap = new HashMap<>();
    static { 
        userMap.put(1, new User(1, "肖战", 25)); 
        userMap.put(2, new User(2, "王一博", 26)); 
        userMap.put(3, new User(3, "杨紫", 24)); 
    }
    
    @CachePut(value ="user", key = "#user.id")
    @Override
    public User save(User user){ 
        userMap.put(user.getId(), user); 
        logger.info("进入save方法,当前存储对象:{}", user.toString());
        return user; 
    }
    
    @CacheEvict(value="user", key = "#id")
    @Override public void delete(int id){ 
        userMap.remove(id); 
        logger.info("进入delete方法,删除成功"); 
    }
    
    @Cacheable(value = "user", key = "#id")
    @Override public User get(Integer id){ 
        logger.info("进入get方法,当前获取对象:{}", userMap.get(id)==null?null:userMap.get(id).toString());
        return userMap.get(id); 
    }
}

为了方便演示数据库的操作,这里直接定义了一个 Map<Integer,User> userMap。

这里的核心是三个注解:

@Cachable@CachePut@CacheEvict

测试类:UserController

@RestController
@RequestMapping("/user")
public class UserController{
    public static Logger logger = LogManager.getLogger(UserController.class);
    @Autowired 
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RedisTemplate<String, Serializable> redisCacheTemplate;
    @Autowired
    private UserService userService;
    @RequestMapping("/test")
    public void test(){ 
        redisCacheTemplate.opsForValue().set("userkey", new User(1, "张三", 25)); 
        User user = (User) redisCacheTemplate.opsForValue().get("userkey");
        logger.info("当前获取对象:{}", user.toString());
    }
    @RequestMapping("/add")
    public void add(){ 
        User user = userService.save(new User(4, "李现", 30)); 
        logger.info("添加的用户信息:{}",user.toString());
    }
    @RequestMapping("/delete")
    public void delete(){ 
        userService.delete(4);
    }
    @RequestMapping("/get/{id}")
    public void get(@PathVariable("id") String idStr) throws Exception{
        if (StringUtils.isBlank(idStr)) {
            thrownew Exception("id为空"); 
        } 
        Integer id = Integer.parseInt(idStr); 
        User user = userService.get(id); 
        logger.info("获取的用户信息:{}",user.toString()); 
    }
}

用缓存要注意,启动类要加上一个注解开启缓存:

@SpringBootApplication(exclude=DataSourceAutoConfiguration.class)
@EnableCaching
public class Application{
    public static void main(String[] args){ 
        SpringApplication.run(Application.class, args); 
    }
}

  Redis讲解目录:

Redis(1)-基本概念及使用

Redis(2)-Redis的一些问题及策略

Redis(3)-集群:主从复制、哨兵模式、Cluster

Redis(4)-Redis遇到的问题