一、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。
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讲解目录: