前文:
最近看了狂神的直播公开课,又get到了一点,Docker部署高可用的redis集群,简直不要太爽!
附狂神B站地址:https://space.bilibili.com/95256449/ 大家可以看一下,良心up主~
正文:
1.Redis是什么?
开篇先简介一下redis,真不会有小伙伴还不知道redis是什么吧。Redis(Remote Dictionary Server ),即远程字典服务 :是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
2.使用Redis能做什么?
1.redis是基于内存存储、持久化的(rdb、aof机制,一般混合使用) 2.效率高,可以用于高速缓存(内存>>磁盘)3.发布订阅系统(一般用MQ)4.地图信息分析(附近的人、外卖)5.计时器、计数器(浏览量、点赞数)6.分布式锁…
3.Redis特性?
1.多样的数据类型2.持久化3.集群4.事务…
4.面试问到redis,必问知识点:redis为什么这么快?
1.纯内存KV操作2.内部是单线程实现的(不需要创建/销毁线程,避免上下文切换,无并发资源竞争的问题)3.异步非阻塞的I/O(多路复用)
4.1.1 存内存KV操作快在哪里?
Redis是一个纯kv的操作。并且Redis绝大部分请求是纯粹的内存操作,所以速度非常快。数据存在内存中,类型存在hashMap中,那么为什么那么快呢?我们可以一起来看一下几种常用数据结构的对比,和他们的优势。
从上图我们可以看出,HashMap的优势就是查找和操作的时间复杂度都是O(1),所以Redis内部采用这种结构能够从根本上获得足够的优势,当然,Redis的快速不仅仅是数据结构成就的,还有单程成和异步I/O。
4.2.1 Redis为什么使用单线程?
官方表示,Redis是基于内存操作,CPU不是Redis性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,就使用单线程了。Redis 是C 语言写的,官方提供的数据为 100000+ 的QPS。
4.2.2Redis使用单线程,相比于多线程快在哪里?
4.2.2.1避免上下文的切换
上下文其实不难理解,它就是CPU寄存器和程序计数器。主要的作用就是存放没有被分配到资源的线程,多线程操作的时候,不是每一个线程都能够直接获取到CPU资源的,我们之所以能够看到我们电脑上能够运行很多的程序,是因为多线程的执行和CPU不断对多线程的切换。但是总有线程获取到资源,也总有线程需要等待获取资源,这个时候,等待获取资源的线程就需要被挂起,也就是我们的寄存。这个时候我们的上下文就产生了,当我们的上下文再次被唤起,得到资源的时候,就是我们上下文的切换。
4.2.2.2竞争资源
竞争资源相对来说比较好理解,CPU对上下文的切换其实就是一种资源分配,但是在切换之前,到底切换到哪一个上下文,就是资源竞争的开始。在我redis中由于是单线程的,所以所有的操作都不会涉及到资源的竞争。
4.2.2.3锁的消耗
对于多线程的情况来讲,不能回避的就是锁的问题。如果说多线程操作出现并发,有可能导致数据不一致,或者操作达不到预期的效果。这个时候我们就需要锁来解决这些问题。当我们的线程很多的时候,就需要不断的加锁,释放锁,该操作就会消耗掉我们很多的时间。
4.3.1 I/O复用,非阻塞模型
对于I/O阻塞可能有很多人不知道,I/O操作的阻塞到底是怎么引起的,Redis又是怎么解决的呢?
I/O操作的阻塞:当用户线程发出IO请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线程就会处于阻塞状态,用户线程交出CPU。当数据就绪之后,内核会将数据拷贝到用户线程,并返回结果给用户线程,用户线程才解除block状态。
Redis采用多路复用:I/O 多路复用其实是在单个线程中通过记录跟踪每一个sock(I/O流) 的状态来管理多个I/O流。select, poll, epoll 都是I/O多路复用的具体的实现。epoll性能比其他几者要好。redis中的I/O多路复用的所有功能通过包装常见的select、epoll、evport和kqueue这些I/O多路复用函数库来实现的。
核心:redis 是将所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文会切换:耗时的操作),对于内存系统来说,如果没有上下文切换效率就是最高的!多次读写都是在一个CPU上的,在内存情况下,这个就是最佳的方案!
5.Redis五大数据类型
全段翻译:Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件MQ。它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
我们常用的五大数据类型为:String,Hash,List,Set,Zset。
三大特殊数据类型为:Geospatial,Hyperloglog,Bitmap。
五大基本类型简单使用:
package com.jpunster.locks.redis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
/***
* @author Jpunster
* @date 2020/5/17 5:14 下午
*/
@RestController
@RequestMapping("/BaseRedis")
public class BaseRedis {
@Autowired
private RedisTemplate redisTemplate;
//测试String
@GetMapping("/string")
public String string() {
redisTemplate.opsForValue().set("String", "String");
String str = redisTemplate.opsForValue().get("String").toString();
return str;
}
//测试List
@GetMapping("/list")
public String List() {
redisTemplate.opsForList().leftPush("leftList",1);
redisTemplate.opsForList().rightPush("rightList",2);
String leftList = redisTemplate.opsForList().range("leftList", 0, -1).toString();
return leftList;
}
//测试Hash
@GetMapping("/hash")
public Map<String,Object> Hash(){
Map<String,Object> map = new HashMap<>();
map.put("key1","value1");
map.put("key2","value2");
map.put("key3","value3");
redisTemplate.opsForHash().putAll("map1",map);
Map entries = redisTemplate.opsForHash().entries("map1");
//Object key1 = redisTemplate.opsForHash().get(map, "key1");
return entries;
}
//测试set
@GetMapping("/set")
public Set<String> set(){
SetOperations<String,Object> set = redisTemplate.opsForSet();
set.add("set1","1");
set.add("set1","2");
Set<String> members = redisTemplate.opsForSet().members("set1");
return members;
}
//测试zset
@GetMapping("/zset")
public String zset(){
ZSetOperations zset = redisTemplate.opsForZSet();
zset.add("zset1","A",1.0);
zset.add("zset1","B",2.0);
String zset1 = redisTemplate.opsForZSet().range("zset1", 0, -1).toString();
return zset1;
}
}
结果:
最后提供一个redis序列化方法:
下面展示一些 内联代码片
。
package com.jpunster.locks.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/***
* @author Jpunster
* @date 2020/5/17 6:05 下午
* redisTemplate 序列化使用的jdkSerializeable, 存储二进制字节码, 所以自定义序列化类
*
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();//创建 RedisTemplate,key 和 value 都采用了 Object 类型
redisTemplate.setConnectionFactory(redisConnectionFactory);//绑定 RedisConnectionFactory
//创建 Jackson2JsonRedisSerializer 序列方式,对象类型使用 Object 类型,
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);//设置一下 jackJson 的 ObjectMapper 对象参数
// 设置 RedisTemplate 序列化规则。因为 key 通常是普通的字符串,所以使用 StringRedisSerializer 即可。
// 而 value 是对象时,才需要使用序列化与反序列化
redisTemplate.setKeySerializer(new StringRedisSerializer());// key 序列化规则
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// value 序列化规则
redisTemplate.setHashKeySerializer(new StringRedisSerializer());// hash key 序列化规则
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);// hash value 序列化规则
redisTemplate.afterPropertiesSet();//属性设置后操作
return redisTemplate;//返回设置好的 RedisTemplate
}
}
结尾:
上篇讲了讲Redis的基础知识。中篇计划写一下docker搭建Redis以及实战中出现的缓存失效、雪崩等常见问题的解决方案。下篇计划依赖docker搭建高可用的Redis集群。你值得拥有~
参考资料:
https://space.bilibili.com/95256449/
https://www.cnblogs.com/xlecho/archive/2019/11/10/11832118.html
微信扫一扫
关注该公众号