Redis
什么是NoSQL
NoSQL:(Not Only SQL)不仅仅是SQL
泛指非关系型数据库,随着web2.0互联网的诞生!传统的关系型数据库很难对付web2.0时代!尤其是超大规模的高并发社区!暴露出来很多难以克服的问题,NoSQL在当下大数据环境的发展十分迅速,Redis是发展最快的,是必须要掌握的技术
很多的数据类型用户的个人信息,社交网络,地理位置。这些数据类型的存储不需要一个固定的格式,不需要多余的操作就可以横向扩展
NoSQL特点
解藕
1、方便扩展(数据之间没有关系,很好扩展)
2、大数据量高性能(Redis每秒可以读取11万次,写8万次,NoSQL的缓存记录级,是一种细粒度的缓存,性能会比较高)
3、数据类型丰富(不需要事先设计数据库!如果是数据库量十分大的表,很多人就无法设计了)
4、传统RDBMS和NoSQL
传统的RDBMS
- 结构化组织
- SQL
- 数据和关系都存在单独的表中
- 操作操作,数据定义语言
- 严格的一致性
- 基础的事物
- 。。。。。
NoSQL
- 不仅仅是数据
- 没有固定的查询语言
- 键值对存储,列存储,文档存储,图形数据库(社交关系)
- 最终一致性
- CAP定理 和 BASE理论(异地多活)
- 保证高性能、高可用、高可扩展
- 。。。。
了解:3V+3高
大数据时代的3V:主要是描述问题的
- 海量Volume
- 多样Variety
- 实时Velocity
大数据时代的3高:对程序的要求
- 高并发
- 高可扩(随时可以水平扩展,增加服务器)
- 高性能(保证用户体验和性能)
真正在公司的实践:NoSQL + RDBMS一起使用才是最强的
NoSQL四大分类
KV键值对:
- 新浪:Redis
- 美团:Redis + Tair
- 阿里、百度:Redis + mamecache
文档型数据库(bson格式和json一样):
- MongoDB(一般必须掌握)
- MongoDB是一个基于分布式文件存储的数据库,C++编写,主要用来处理大量的文档
- MongoDB是一个介于关系型数据库和非关系型数据库中间的产品,是NoSQL中功能最丰富,最像关系型数据库的
列存储数据库
图关系数据库
- 拓扑图,存放的是关系,比如:朋友圈社交网络,广告推荐
- Neo4j,InfoGrid
分类 | Examples举例 | 典型应用场景 | 数据模型 | 优点 | 缺点 |
---|
键值对(key-value) | Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB | 内容缓存,主要用于处理大量数据的高访问负载,也用于一些日志系统等等。 | Key 指向 Value 的键值对,通常用hash table来实现 | 查找速度快 | 数据无结构化,通常只被当作字符串或者二进制数据 | 列存储数据库 | Cassandra, HBase, Riak | 分布式的文件系统 | 以列簇式存储,将同一列数据存在一起 | 查找速度快,可扩展性强,更容易进行分布式扩展 | 功能相对局限 | 文档型数据库 | CouchDB, MongoDb | Web应用(与Key-Value类似,Value是结构化的,不同的是数据库能够了解Value的内容) | Key-Value对应的键值对,Value为结构化数据 | 数据结构要求不严格,表结构可变,不需要像关系型数据库一样需要预先定义表结构 | 查询性能不高,而且缺乏统一的查询语法。 | 图形(Graph)数据库 | Neo4J, InfoGrid, Infinite Graph | 社交网络,推荐系统等。专注于构建关系图谱 | 图结构 | 利用图结构相关算法。比如最短路径寻址,N度关系查找等 | 很多时候需要对整个图做计算才能得出需要的信息,而且这种结构不太好做分布式的集群 |
Redis入门
概述
Redis是什么
Redis(Remote Dictionary Server) 远程字典服务
即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API,redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
免费和开源!是当下最热门的NoSQL技术之一!也被人们称为结构数据库!
Redis能干嘛
- 内存存储、持久化,内存是断电即失的,所以说持久化很重要(RDB、AOF)
- 效率高,可以用于高速缓存
- 发布订阅系统
- 地图信息分析
- 计时器、计数器(浏览量!)
- 。。。。。
特性
-
多样的数据类型 -
持久化 -
集群 -
事物 。。。。。
学习中需要用到的东西
- Redis官网https://redis.io/
- Redis中文网http:/www./redis.cn/
- 下载地址:通过官网下载(Windows版本在Github上下载)
Redis一般都是安装在Linux系统上的,我们基于Linux学习
Windows安装
下载地址:https://github.com/dmajkic/redis
-
解压安装包 -
开启redis-server.exe -
启动redis-cli.exe测试
Linux安装
-
下载安装包!redis-5.0.8.tar.gz -
解压Redis的安装包!程序一般放在 /opt 目录下 -
基本环境安装 yum install gcc-c++
# 然后进入redis目录下执行
make
# 然后执行
make install
使用的是5.0.8版本,高版本会有错误 -
redis默认安装路径 /usr/local/bin
-
将redis的配置文件复制到 程序安装目录 /usr/local/bin/redisconfig 下,以后使用这个配置文件启动 -
redis默认不是后台启动,需要修改配置文件 修改配置文件中的daemonize 将原来的no改为yes即可 -
通过制定的配置文件启动redis服务
- 使用
redis-cli 测试连接
- 查看redis的进程是否开启
- 关闭Redis服务
- 再次查看进程是否存在
可以发现客户端进程已经结束,只剩下了redis的服务
测试性能
| | | |
---|
序号 | 选项 | 描述 | 默认值 | 1 | -h | 指定服务器主机名 | 127.0.0.1 | 2 | -p | 指定服务器端口 | 6379 | 3 | -s | 指定服务器 socket | | 4 | -c | 指定并发连接数 | 50 | 5 | -n | 指定请求数 | 10000 | 6 | -d | 以字节的形式指定 SET/GET 值的数据大小 | 2 | 7 | -k | 1=keep alive 0=reconnect | 1 | 8 | -r | SET/GET/INCR 使用随机 key, SADD 使用随机值 | | 9 | -P | 通过管道传输 请求 | 1 | 10 | -q | 强制退出 redis。仅显示 query/sec 值 | | 11 | –csv | 以 CSV 格式输出 | | 12 | -l | 生成循环,永久执行测试 | | 13 | -t | 仅运行以逗号分隔的测试命令列表。 | | 14 | -I | Idle 模式。仅打开 N 个 idle 连接并等待。 | |
简单测试:
测试100个并发连接 1000000个请求
redis-benchmark -h locl -p 6379 -c 100 -n 1000000
Redis基本命令
redis默认有16个数据库
select 1
dbsize
Set name haiyang
get name
keys *
flushdb
flushall
Exist name
move name
expire name 10
type name
append name 123
strlen key
incr views
decr views
incrby views 10
decrby views 10
getrange name 0 3
gerrange name 0 -1
setrange name 1 xx
setex name 30 "hello"
setnx name "abc"
mset k1 v1 k2 v2 k3 v3
mget k1 k2 k3
msetnx k1 v1 k4 v4
mset user:1:name zhangsan user:1:age 10
mget user:1:name user:1:age
getset name zhangsan
为什么redis是6379,redis创始人的偶像
redis是单线程的!
Redis是基于内存操作的,CPU不是Redis的性能瓶颈,Redis的平静是根据机器的内存和网络带宽,所以redis使用单线程
Redis是C语言写的,官方数据,每秒10w+ QPS,不比Memecache差!
Redis为什么单线程还这么快?
单线程不用上下文切换,CPU上下文切换很耗费性能
核心:将所有的数据全部放在内存中的,所以所使用单线程操作效率就是最高的,多线程
(CPU上下文切换,是耗时的操作),对于内存系统来说,没有上下文切换效率就是最高的。
Redis五大数据类型
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(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(字符串)
String类型的使用场景:value除了是字符串还可以是数字!
List
基本的数据类型,列表
在Redis里面,我们可以把List当成栈、队列、阻塞队列来理解
所有的List命令都是L或者R
LPUSH list 1
LPUSH list 2
LPUSH list 3
LRANGE list 0 -1
LRANGE list 0 1
RPUSH list 1
Lpop list
Rpop list
Lindex list 1
Llen list
Lrem list 1 value
Lrem list 2 value
trim list 1 2
rpoplpush list1 list2
lset list 0 123
linsert list before "hello" "other"
linsert list after "hello" "other"
小结
- Redis中的List实际上是一个链表,在链表前面和后面都可以插入值
- 如果不存在,创建新的链表
- 如果key存在,新增内容
- 如果移除了所有的值,空链表,也代表不存在
- 在两边插入或者改动值效率最高,在中间插入效率会低一点
消息队列,队列(lpush rpop),栈(lpush lpop)
Set(集合)
set中的值不能重复
sadd myset "hello"
smembers myset
sismember myset "hello"
scard myset
srem myset "hello"
srandmember myset
spop myset
smove myset myset2 "hello"
sdiff key1 key2
sinter key1 key2
sunion key1 key2
Hash(哈希)
Map集合,key-map 这个时候这个值是一个map集合,hash本质和String类型没有太大区别,还是一个简单的key-value
hset myhash field1 123
hget myhash field1
hset myhash field1 hello field2 world
hget myhash field1 field2
hgetall myhash
hdel myhash field1
hlen myhash
hexists myhash field1
hkeys myhash
hvalues myhash
hincrby myhash field3 1
hdecrby myhash field3 1
hsetnx myhash field4 hello
hash变更的数据 user name age 尤其是用户信息,经常变动的信息!hash更加适合对象的存储,而String更适合字符串的存储
ZSet(有序集合)
在set的基础上,增加了一个值,set k1 v1 set k1 score1 v1
zadd myset 1 one
zrange myset 0 -1
zrangebyscore salary -inf +inf whithscores
zrangebyscore salary -inf 2500 whithscores
zrevrange salary 0 -1
zrem salary xiaohong
zcard salary
zcount myset 1 3
其余的一些API,如果工作中有需要,可以查看官方文档
案例思路:set 排序 存储班级成绩表,工资表排序,排行榜应用实现
普通消息,1 ,重要消息 2,带权重进行判断
三种特殊数据类型
geospatial 地理位置
Redis的Geo在Redis3.2版本就推出了,这个功能可以推算地理位置的信息,两地之间的距离,方圆几里的距离
可以查询一些地理位置数据:https://jingweidu.bmcx.com
Geo只有6个命令
geoadd china:city 116.40 39.90 beijing
geopos china:city beijing
两点之间的距离
geodist china:city beijing shanghai
附近的人(获得所有附近的人的地址,定位!)通过半径来查询
georadius china:city 116.40 39.90 1000 km withdist withcoord count 1
找出位于指定元素周围的其他元素!(用于地图)
georadiusbymember china:city beijing 1000 km
geohash(返回11位的GeoHash字符串)
Geo的底层实现原理就是ZSet,删除ZSet中的元素Geo中的元素也就删除了
Hyperloglog
什么是基数? 基数就是数据集中去重后的元素个数
A{1,2,3,4,5,6,7}
B{3,5,6,8,9,0,0}
A的基数是7,B的基数是6
简介
Redis 2.8.9更新了Hyperloglog数据结构!
Redis Hyperloglog基数统计的算法
优点:占用的内存是固定的,2^64不同的元素基数,只需要占用12KB内存!从内存角度考虑Hyperloglog是首选
网页的UV(一个人访问网站多次,还是算做一个人)
传统方式,用set保存用户的id,统计set中的元素数量作为标准判断!
这个方式如果保存大量的用户id,就会比较麻烦!目的是为了计数,而不是保存id
0.81%错误率!统计UV任务,可以忽略不计
PFadd mykey a b c d e f c q
如果允许容错,那一定可以使用Hyperloglog
如果不允许容错,就使用set集合或其他数据类型即可
Bitmap
位存储
统计疫情感染人数:0 1 0 0 0 1
统计用户信息,活跃,不活跃!登陆、未登录!打卡,365打卡!两个状态的都可以使用Bitmaps
Bitmaps 位图,数据结构!所有操作都是操作二进制位来进行记录,就只有0和1两种状态
365天 = 365bit 1字节=8bit 46个字节左右!
测试
setbit sign 0 0setbit sign 1 1setbit sign 2 1setbit sign 3 1setbit sign 4 0setbit sign 5 0setbit sign 6 1
统计操作
bitcount sign
事务
事务的本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程中会按照顺序执行
一次性、顺序性、排他性!执行一系列的命令!
Redis事务是没有隔离级别的概念
所有命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行!Execute
Redis单条命令是保证原子性的,但是事务不保证原子性
redis的事务
- 开启事务(multi)
- 命令入队(…)
- 执行事务(execute)
正常执行事务!
multi
放弃事务
multi
Redis不允许编译型异常(代码有错误,编译不通过)
Redis允许运行时异常(编译通过,运行报错)如果事务队列中存在语法性问题,有异常的语句会抛出异常,但不会影响其他语句执行
监控! Watch
悲观锁:
- 很悲观,认为什么时候都会出问题,无论做什么都会加锁!
乐观锁:
- 很乐观,认为什么时候都不会出现问题,所以不会上锁!更新数据的时候去判断一下,在此期间是否有人修改过数据
- 获取version
- 更新的时候比较version
Redis监视测试
set money 100set out 0watch money
测试多线程修改值,使用watch可以当redis的乐观锁操作
set money 100watch moneymultidecrby money 10incrby out 10exec
事务执行失败或者成功会自动放弃监视
如果修改失败,获取最新的值就好
Redis的watch监视的是对应元素的version,而不是值,所以即便发生ABA问题(狸猫换太子),事务执行还是会报错
Jedis
我们要用java来操作Redis
什么是jedis,是Redis官方推荐的java连接开发工具!使用java操作Redis中间件!如果你要使用java操作redis,那么一定要对jedis十分的熟悉!
测试
导入依赖:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
配置:
Jedis jedis = new Jedis("codekitty.cn", 6379);
使用:
public static void main(String[] args) {
Jedis jedis = new Jedis("codekitty.cn", 6379);
String set = jedis.set("key1", "value1");
System.out.println(set);
String key1 = jedis.get("key1");
System.out.println(key1);
}
基本的API
和之前的Linux命令行一样,只是换成了方法,此处省略
Jedis操作事务(与命令行操作类似)
Jedis jedis = new Jedis("codekitty.cn", 6379);
jedis.flushDB();
System.out.println(jedis.ping());
Transaction multi = jedis.multi();
try {
multi.set("key1","value1");
multi.set("key2","value2");
int i = 1/0;
multi.set("key3","value3");
multi.exec();
}catch (Exception e){
multi.discard();
e.printStackTrace();
}finally {
System.out.println(jedis.keys("*"));
System.out.println(jedis.get("key1"));
jedis.close();
}
SpringBoot整合
SpringBoot操作数据:spring-data jpa jdbc mongdb redis!
SpringData也是和SpringBoot齐名的项目!
在SpringBoot2.x之后原来使用的Jedis被lettuce替换了?
jedis:采用的是直连,多个线程操作不安全,如果想要避免不安全的,要使用jedis pool连接池!BIO
lettuce:采用netty,实例可以在多个线程中共享,不存在线程不安全的情况!可以减少线程数量,NIO
源码分析:
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
整合测试
1、导入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、编写配置文件:
spring:
redis:
host: codekitty.cn
port: 6379
3、连接测试
@Autowired
RedisTemplate redisTemplate;
@Test
void contextLoads() {
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
try {
connection.flushDb();
connection.flushAll();
redisTemplate.opsForValue().set("key1", "value1");
redisTemplate.opsForList().leftPush("list1","value");
redisTemplate.watch("key1");
redisTemplate.multi();
redisTemplate.exec();
}catch (Exception e){
redisTemplate.discard();
e.printStackTrace();
}finally {
connection.close();
}
}
自定义RedisTemplate
创建RedisConfig.java并将源码中的配置拷贝过来,然后修改,添加自己需要的配置
@Configurationpublic class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { Jackson2JsonRedisSerializer<Object> objectJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
企业级开发可以封装RedisUtil方便使用
Redis.conf配置
redis启动的时候通过配置文件来启动
单位
配置文件对大小写不敏感
包含
可以把其他配置文件引用过来
网络
bind 127.0.0.1 -::1
protected-mode yes
port 6000
通用配置
daemonize yes
pidfile /var/run/redis_6379.pid
loglevel notice
logfile ""
databases 16
always-show-logo yes
set-proc-title yes
proc-title-template "{title} {listen-addr} {server-mode}"
快照
在规定的时间内进行了多少次操作会持久化道 .RDB .AOF
redis是内存数据库,没有持久化就会断电即失
save 3600 1
save 300 100
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dir ./
主从复制 REPLICATION
replicaof <masterip> <masterport>
masterauth <master-password>
安全
requirepass 123456
config set requirepass "123456"
auth 123456
客户端限制
maxclients 10000
maxmemory <bytes>
maxmemory-policy noeviction
1、volatile-lru:只对设置了过期时间的key进行LRU(默认值)
2、allkeys-lru : 删除lru算法的key
3、volatile-random:随机删除即将过期key
4、allkeys-random:随机删除
5、volatile-ttl : 删除即将过期的
6、noeviction : 永不过期,返回错误
append only AOF配置
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
Redis持久化
Redis是内存数据库,一旦断电,数据即失去,所以Redis也提供了数据持久化功能
RDB(Redis DataBase)
什么是RDB
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里。
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程 都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的。 这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那 RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。我们默认的就是 RDB,一般情况下不需要修改这个配置!
dbfilename dump.rdb
save 60 5
触发机制
- save的规则满足时会触发rdb机制
- flushall会触发rdb机制
- 退出redis会触发rdb机制
如何恢复rdb文件
只需将rdb文件放入redis启动目录就可以了,redis启动的时候会自动恢复rdb文件的数据
127.0.0.1:6379> config get dir
1) "dir"
2) "/Users/mac"
默认配置即可
在生产环境会对rdb文件进行备份!
优点:
- 适合大规模的恢复
- 对数据的完整性要求不高
缺点:
- 需要一定的时间间隔进行操作,如果redis意外宕机了,最后一次修改的数据就没有了
- fork进程的时候会占用一定空间
AOF (Append Only File)
以日志形式将所有操作记录下来保存为.aof文件,恢复的时候就把这个文件全部执行一遍
是什么
aof配置:
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
将appendonly改为yes即为开启aof
重启服务:
shutdown
redis-server ./redis.conf
写入后的.aof文件被修改,则无法启动redis-server服务,redis提供了这样一个工具(Redis-check-aof)
redis-check-aof --fix appendonly.aof
优点和缺点
优点:
- 每一次修改都同步,让文件的完整性更好
缺点:
- 如果是每秒同步一次,会丢失一秒的数据
- 数据文件体积远远大于rdb,修复也比rdb慢,所以redis默认的配置是rdb,而不是aof
扩展:
-
rdb持久化方式能够在指定的时间间隔内对数据进行快照存储 -
aof持久化方式记录每次对服务器写的操作,服务器重启时,重新执行命令来恢复原始数据,追加在文件末尾,能对aof文件进行重写,避免体积过大 -
如果只做缓存不需要使用任何持久化 -
同时开启两种持计划方式,重启时优先载入aof文件来恢复数据 只会找aof文件,但是推荐只使用rdb用于备份,能快速重启,并且不会有aof可能潜在的bug 性能建议 -
rdb文件只做后背用途,建议只在slave上持久化rdb文件,15分钟备份一次,使用save 900 1 规则 -
使用aof,即便在最恶劣的环境下也不会丢失超过2秒的数据 -
代价:持续的io rewrite 过程中产生的新数据写到新文件造成的阻塞不可避免,尽量减少rewrite的频率 -
不使用aof,也可以通过Master-Slave Replication 实现高可用性也可以,能省去一大笔io,减少rewrite带来的系统波动 代价:如果Master-Slave 同时倒掉,回丢失十几分钟的数据,启动脚本也要比较Master-Slave中的rdb文件,选择最新的文件,载入新的,微博就是这种架构
发布订阅
Redis发布订阅(pub/sub)是一种通信模式:发送者(pub)发送消息,接收者(sub)接收消息,微博,微信。。。
Redis客户端可以订阅任何数量的频道
发布订阅消息图:
1、消息发送者
2、频道
3、消息接收者
Redis 发布订阅命令
下表列出了 redis 发布订阅常用命令:
测试
127.0.0.1:6379> psubscribe mychannel
Reading messages... (press Ctrl-C to quit)
127.0.0.1:6379> publish mychannel "send a message"
1) "pmessage"
2) "mychannel"
3) "mychannel"
4) "send a message"
原理
每个Redis 服务器进程都维持着一个表示服务器状态的 redis.h/redisServer 结构, 结构的 pubsub_channels 属性是一个字典, 这个字典就用于保存订阅频道的信息,其中,字典的键为正在被订阅的频道, 而字典的值则是一个链表, 链表中保存了所有订阅这个频道的客户端。
使用场景
- 实时消息系统
- 实时聊天(频道作为聊天室)
- 订阅、关注系统
稍微复杂的场景会使用消息中间件来做(RabbitMQ…)
主从复制
概念
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(Master/Leader),后者称为从节点(Slave/Follower), 数据的复制是单向的!只能由主节点复制到从节点(主节点以写为主、从节点以读为主)。
默认情况下,每台Redis服务器都是主节点,一个主节点可以有0个或者多个从节点,但每个从节点只能由一个主节点。
作用
- 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余的方式。
- 故障恢复:当主节点故障时,从节点可以暂时替代主节点提供服务,是一种服务冗余的方式
- 负载均衡:在主从复制的基础上,配合读写分离,由主节点进行写操作,从节点进行读操作,分担服务器的负载;尤其是在多读少写的场景下,通过多个从节点分担负载,提高并发量。
- 高可用基石:主从复制还是哨兵和集群能够实施的基础。
不能只使用一台redis的原因:
- 从结构上讲,单个redis服务器会发生单点故障,一台服务器需要处理所有请求,压力大
- 从容量上讲,单个redis服务器内存容量有限,并且不能完全使用全部的内存,单台redis的最大内存不应该超过20g
通常的电商网站都是一次上传吗,无数次浏览,读多写少 ,主从复制,读写分离,80%的情况都在进行读操作,起码一主二从
只要是在公司中使用,主从复制必须使用,不可能使用单机
环境配置
只配置从库,不用配置主库
redis-server
info replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:906a84bfb098cf9ddfc3cfc39b5b26419a644a77
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
复制三个配置文件,修改对应配置信息
- 端口号
- pid
- 日志名
- 备份名
修改后启动三个redis服务
redis-server ./redis.conf
...
主从复制配置
默认情况下每个节点都是主节点,一般只要配置从机就可以了
配置
slaveof 127.0.0.1 6000
真实的主从配置应该在配置文件配置,这里使用的是命令,重启会失效,配置规则在之前的Redis.conf章节
细节
- 主机负责写,从机负责读!主机中的所有数据都会被从机复制,从机写会报错
- 主机崩掉从机会保存数据
- 主机崩掉重新连接之后,从机依然可以读取到主机set的值
- 如果使用命令行配置主从,重启后默认作为主机启动,重新设置为主机的从机后会自动同步数据(全量复制),如果在配置文件中设置主机,启动时自动同步主机的数据
从机第一次连接会同步所有数据:全量复制
后续的每一次操作同步至从机:增量复制
主从复制的其他实现
上一个Master连接下一个Slave(既当主机又当从机,但此时这个节点不能写入,只有在主节点挂掉之后,使用slaveof no one 这个命令让该节点作为主节点进行写入)
这是哨兵模式没有出现的时候的方法
哨兵模式
概述
主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。
单机哨兵模式
哨兵的作用:
- 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
- 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。
多哨兵模式
假设master宕机,sentinel先检测到这个结果,系统并不会马上进行failover(故障切换、失效备援)这个现象称为主观下线,当后面的哨兵也检测到主服务器不可用,sentinel之间会发起一次投票,投票的结果由随机一个sentinel发起,进行failover操作,得到sentinel票数多的slave能成功切换为master,切换成功后,通过发布订阅模式,让各个哨兵把自己监控的服务器实现切换主机,这个过程称为客观下线。
测试
目前的状态是一主二从
- 配置哨兵配置文件sentinel.conf
sentinel monitor myredis 127.0.0.1 6000 1
后面的1代表如果主机挂了让从机投票,按票数选定新主机
- 启动哨兵
redis-sentinel ./conf/sentinel.conf
- 哨兵监控到的信息:
3128:X 03 Apr 2021 16:14:53.825
3128:X 03 Apr 2021 16:14:53.827 * +slave slave 127.0.0.1:6001 127.0.0.1 6001 @ myredis 127.0.0.1 6000
3128:X 03 Apr 2021 16:14:53.828 * +slave slave 127.0.0.1:6002 127.0.0.1 6002 @ myredis 127.0.0.1 6000
- 测试如果主机崩掉
3128:X 03 Apr 2021 16:19:57.771
3128:X 03 Apr 2021 16:19:57.771
3128:X 03 Apr 2021 16:19:57.771 * +slave slave 127.0.0.1:6001 127.0.0.1 6001 @ myredis 127.0.0.1 6002
3128:X 03 Apr 2021 16:19:57.771 * +slave slave 127.0.0.1:6000 127.0.0.1 6000 @ myredis 127.0.0.1 6002
如果主机挂掉后重新连接,原来的主机会作为从机
优点:
- 哨兵集群基于主从复制,所有主从配置的优点,它都有
- 主从可以切换,故障可以转移,系统的可用性就会更好
- 哨兵模式就是主从模式的升级,从手动到自动,更加健壮
缺点:
- redis不好做在线扩容,集群容量达到上限,在线扩展非常麻烦
- 实现哨兵模式的配置其实是很麻烦的,里面有很多配置
哨兵的全部配置
port 26379
dir /tmp
sentinel monitor mymaster 127.0.0.1 6379 1
sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
sentinel down-after-milliseconds mymaster 30000
这个数字越小,完成failover所需的时间就越长,
但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。
可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel notification-script mymaster /var/redis/notify.sh
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
以上配置一般由运维来配置
缓存穿透、击穿与雪崩
缓存的穿透、击穿与雪崩都是服务器的高可用问题
缓存穿透(查不到)
概念
在默认情况下,用户请求数据时,会先在缓存(Redis)中查找,若没找到即缓存未命中,再在数据库中进行查找,数量少可能问题不大,可是一旦大量的请求数据(例如秒杀场景)缓存都没有命中的话,就会全部转移到数据库上,造成数据库极大的压力,就有可能导致数据库崩溃。网络安全中也有人恶意使用这种手段进行攻击被称为洪水攻击。
解决方案
缓存击穿(量太大,一个点的缓存过期)
概念
相较于缓存穿透,缓存击穿的目的性更强,一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力骤增。这就是缓存被击穿,只是针对其中某个key的缓存不可用而导致击穿,但是其他的key依然可以使用缓存响应。
比如热搜排行上,一个热点新闻被同时大量访问就可能导致缓存击穿。
解决方案
设置热点数据永不过期
这样就不会出现热点数据过期的情况,但是当Redis内存空间满的时候也会清理部分数据,而且此种方案会占用空间,一旦热点数据多了起来,就会占用部分空间。
加互斥锁(分布式锁)
在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。保证同时刻只有一个线程访问。这样对锁的要求就十分高。
缓存雪崩(缓存在某一时间缓存集体失效)
概念
产生雪崩的原因之一,设置缓存的存活时间较短,大并发访问时刚好都过期,直接访问了数据库,对数据库而言,会产生周期性压力波峰,暴增时数据库可能会宕机
双十一时会停掉一些服务,保证主要的一些服务可用,比如:退款服务
解决方案:
小结
|