Redis
Redis是一个开源的内存数据存储,常用作数据库,缓存和消息代理。
Redis常用场景
1.分布式缓存。
2.分布式锁。
3.实时排行榜。
4.计数器/自增ID。通过INCR命令来实现。
5.消息队列。PUB/SUB(发布/订阅)功能或者Stream来实现消息的发布和订阅。
6.地理位置应用。通过geohash算法和Geospatial数据类型来实现位置信息的查找和计算,以实现类似于附近的人、打车等服务。
7.限时业务。
Redis数据类型
1.String
String是Redis最基本的数据类型,它可以存储任意类型的数据,比如文本、数字、图片或者序列化的对象。一个String类型的键最大可以存储512MB的数据。
2.Hash
Hash是一个键值对集合,它可以存储多个字段和值,类似于编程语言中的map对象。
3.List
List是一个有序的字符串列表,它按照插入顺序排序,并且支持在两端插入或删除元素。List类型的应用场景主要是实现队列和栈。
4.Set
Set是一个无序的字符串集合,它不允许重复的元素。应用场景:去重、交集、并集、差集、随机抽取。
5.Sorted Set
有序集合,它可以存储不重复的字符串元素,并且给每个元素赋予一个排序权重值(score)。Redis通过权重值来为集合中的元素进行从小到大的排序。Sorted Set的成员是唯一的,但权重值可以重复。应用场景:排行榜、延时队列、访问统计。
6.Stream
Stream主要用于消息队列。Redis本身是有一个发布订阅来实现消息队列的功能,但消息无法持久化。Stream提供了消息的持久化和主备复制功能,可以让任何客户端访问任何时刻的数据,并且能记住每一个客户端的访问位置,还能保证消息不丢失。
7.Geospatial(地理位置)
一种用于存储和查询地理空间位置的数据类型,它基于Sorted Set数据结构实现,利用geohash算法将经纬度编码为二进制字符串,并作为Sorted Set的score值。
8.Hyperloglog(基数统计)
HyperLogLog是一种概率数据结构,用于在恒定的内存大小下估计集合的基数(不同元素的个数)。它不是一个独立的数据类型,而是一种特殊的String类型,它可以使用极小的空间来统计一个集合中不同元素的数量,也就是基数。
9.Bitmap(位图)
一种特殊的String类型,它可以将一个String类型的值看作是一个由二进制位组成的数组,并提供了一系列操作二进制位的命令。
10.Bitfield
基于String类型的一种扩展,可以让你对一个字符串中的任意位进行设置,增加和获取操作,就像一个位数组一样可以操作任意位长度的整数。
Redis为什么这么快
1.纯内存操作。
2.单线程,避免了锁和线程切换的消耗。
3.IO多路复用机制。多路是指多个TCP网络连接,复用是指复用一个线程。Redis使用epoll作为IO多路复用技术的实现,再加上Redis自身的事件处理模型将epoll的read、write、close等都转换成事件,不在网络IO上浪费过多的时间。
Redis持久化
Redis4.0之前提供两种方式进行持久化,RDB、AOF。Redis4.0之后支持同时开启RDB和AOF。
1.RDB(在指定的时间间隔将内存中的数据写入磁盘)
优点
压缩后的二进制文件,恢复性能好。
缺点
无法做到实时持久化,每次都要创建子进程,频繁操作成本过高。
2.AOF(把服务器每一次的写操作记录到文件中,Redis重启会根据日志文件的内容将写指令从前到后执行一次)
优点
以文本形式保存,记录写操作保证数据不丢失。
缺点
AOF文件比RDB文件大,恢复速度慢。
3.AOF+RDB混合
使用RDB进行快照存储,然后使用AOF记录所有的写操作。重启服务时会从RDB和AOF两部分恢复数据,即保证了数据完整性,又提高了恢复的性能。
缓存穿透
访问一个不存在的Key,缓存中获取不到数据,请求会穿透到DB。
解决方案
DB中也不存在的数据,将空值写入缓存,并且设置一个较短的过期时间。
缓存雪崩
大量的Key设置了相同的过期时间,导致缓存在同一时刻全部失效。
解决方案
1.设置过期时间时,加一个随机数,让缓存失效的时间点尽量均匀。
2.采用限流算法,限制流量。
3.采用分布式锁,加锁访问。
缓存降级
缓存降级是流量很大,缓存失效时的一种策略:为了保证核心业务的稳定,非核心业务不访问数据库,直接返回默认值。
缓存击穿
热点数据,不停的扛着大并发。当热点数据的Key失效,持续的大并发就穿破缓存,直接请求DB。
解决方案
热点数据永不过期。
缓存预热
缓存预热是指系统上线后,将相关的缓存数据直接加载到缓存。
Redis的过期策略以及内存淘汰机制
Redis采用定期删除+惰性删除
定期删除
Redis默认每隔100毫秒随机抽取Key进行检查,若Key过期,则删除。
惰性删除
获取某个Key的时,Redis会检查这个Key是否设置了过期时间,若过期就会删除。
内存淘汰机制
在redis.conf中有一行配置:# maxmemory-policy volatile-lru
1.volatile-lru
从已设置过期时间的数据中挑选最近最少使用的数据淘汰。
2.volatile-ttl
从已设置过期时间的数据中挑选将要过期的数据淘汰。
3.volatile-random
从已设置过期时间的数据中随机选择数据淘汰。
4.volatile-lfu
从已设置过期时间的数据中挑选使用频率最低的数据淘汰。
5.allkeys-lru
从数据集中挑选最近最少使用的数据淘汰。
6.allkeys-lru
从数据集中挑选最近最少使用的数据淘汰。
7.allkeys-random
从数据集中随机选择数据淘汰。
8.noeviction
驱逐,默认机制。当内存不足以容纳新入数据时,新写入操作就会报错。但对读请求正常处理。
Redis常见性能问题和解决方案
1.内存不足。解决方案:设置合理的内存上线,优化数据结构,设置过期时间。
2.持久化开销问题。解决方案:RDB+AOF
Pipeline有什么好处?
管道。多个指令之间没有依赖关系,可以使用pipeline一次性执行多个指令,减少IO,缩减时间。
Redis支持的Java客户端有哪些?
1.Jedis:学习成本低廉,简单实用。缺点:Jedis的实例是线程不安全的,在多线程的环境下需要基于线程池来使用。
2.Lettuce:基于Netty实现,支持同步、异步和响应式编程方式,并且是线程安全的。支持Redis的哨兵模式、集群模式、管道模式。
3.Redisson:分布式锁。
Redis集群模式
集群优势:高可用、负载均衡、容灾恢复、数据分片
1.主从复制模式
主从复制模式中包含一个主数据库实例(master)与一个或多个从数据库实例(slave)。客户端可对主数据库进行读写操作,对从数据库进行读操作,主数据库写入的数据会实时自动同步给从数据库。
优点
a.master能自动将数据同步到slave,可以进行读写分离,分担master的读压力。
b.master、slave之间的同步是以非阻塞的方式进行的,同步期间,客户端仍然可以提交查询或更新请求。
缺点
a.不具备自动容错与恢复功能,master或slave的宕机都可能导致客户端请求失败,需要等待机器重启或手动切换客户端IP才能恢复。
b.master宕机,如果宕机前数据没有同步完,则切换IP后会存在数据不一致的问题。
c.难以支持在线扩容,Redis的容量受限于单机配置。
2.Sentinel(哨兵)模式
哨兵模式基于主从复制模式,只是引入了哨兵来监控与自动处理故障。当master出现故障时,能自动将一个slave转换为master。
优点
a.哨兵模式基于主从复制模式,所以主从复制模式有的优点,哨兵模式也有。
b.哨兵模式下,master挂掉可以自动进行切换,系统可用性更高。
缺点
a.难以支持在线扩容,Redis的容量受限于单机配置。
b.需要额外的资源来启动sentinel进程,实现相对复杂一点,同时slave节点作为备份节点不提供服务。
3.Cluster模式
Cluster模式通过无中心化架构,实现分布式存储,可进行线性扩展,也能高可用。最少需配置6个节点,3主3从,主节点提供读写操作,从节点作为备用节点,不提供请求,只作为故障转移使用。
优点
a.无中心架构,数据按照slot分布在多个节点。
b.集群中的每个节点都是平等的关系,每个节点都保存各自的数据和整个集群的状态。每个节点都和其他所有节点连接,而且这些连接保持活跃,这样就保证了我们只需要连接集群中的任意一个节点,就可以获取到其他节点的数据。
c.可线性扩展到1000多个节点,节点可动态添加或删除。
d.能够实现自动故障转移,节点之间通过gossip协议交换状态信息,用投票机制完成slave到master的角色转换。
缺点
a.slave充当“冷备”,不能缓解读压力。
b.批量操作限制,目前只支持同一节点的key执行批量操作,对mset、mget、sunion等操作支持不友好。
c.事务操作支持有线,只支持同一节点的事务操作。
d.不支持多数据库空间,单机Redis可以支持16个db,集群模式下只能使用db0。
Redis6.0多线程问题
1.Redis6.0之前为什么一直不使用多线程?
单线程可维护性高。多线程模型虽然在某些方面表现优异,但是它却引入了程序执行顺序的不确定性,带来了并发读写的一系列问题,增加了系统复杂度、同时可能存在线程切换、甚至加锁解锁、死锁造成的性能损耗。Redis通过AE事件模型以及IO多路复用等技术,处理性能非常高,因此没有必要使用多线程。单线程机制使得Redis内部实现的复杂度大大降低,Hash的惰性Rehash、Lpush等等 “线程不安全” 的命令都可以无锁进行。
2.Redis6.0为什么要引入多线程?
对于大部分公司来说,单线程的Redis已经足够使用了。对于单线程Redis来说,性能瓶颈主要在于网络的IO消耗, 多线程任务可以分摊Redis同步IO读写负荷。
3.Redis6.0是否默认开启了多线程?
Redis6.0默认未开启多线程,只使用主线程。如需开启需要修改redis.conf配置文件:io-threads-do-reads yes
4.Redis6.0多线程开启时,线程数如何设置?
开启多线程后,还需要设置线程数,否则是不生效的。同样修改redis.conf配置文件。官方建议:4核的机器建议设置2、3个线程,8核建议设置为6个线程,线程数一定要小于机器核数,最多不要超过8个。
5.开启多线程后,是否会存在线程并发安全问题?
Redis的多线程只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程顺序执行。Redis客户端对服务端的每次调用都经历了发送命令,执行命令,返回结果三个过程。所有到达服务端的命令都不会立刻执行,所有的命令都会进入一个队列中,然后逐个执行,不会产生并发问题。所以我们不需要去考虑控制key、lua、事务、LPUSH/LPOP等等的并发及线程安全问题。
6.Redis6.0的多线程和Memcached多线程模型的对比?
相同点:都采用了master线程-worker线程的模型。
不同点:Memcached执行主逻辑也是在worker线程里,模型更加简单,实现了真正的线程隔离,符合我们对线程隔离的常规理解。而Redis把处理逻辑交还给master线程,虽然一定程度上增加了模型复杂度,但也解决了线程并发安全等问题。
7.Redis线程中经常提到IO多路复用,如何理解?
IO多路复用也称为异步阻塞IO。多路指的是多个网络连接,复用指的是复用一个线程。多路复用主要有三种技术:select,poll,epoll。epoll是最新的也是目前最好的多路复用技术。采用IO多路复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗)。
Redis6.0新特性
1.多线程IO。多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程。之所以这么设计是不想因为多线程而变得复杂,需要去控制Key、lua、事务、LPUSH/LPOP等等的并发问题。
2.重新设计了客户端缓存功能。实现了Client-side-caching(客户端缓存)功能。放弃了caching slot,而只使用key names。
3.RESP3协议。
4.支持SSL,更加安全。
5.ACL权限控制。支持对客户端的权限控制,实现对不同的key授予不同的操作权限。
6.提升了RDB日志加载速度。
7.发布官方的Redis集群代理模块Redis Cluster proxy。
Redis与Memcached的比较
1.Redis支持数据的持久化。Memcached不支持。
2.Redis支持丰富的数据类型,Memcached的数据类型比较单一。
3.Redis支持更多的集群模式。
4.Redis速度更快。Redis是单线程的IO复用模型,所有命令串行执行,避免了锁和线程切换的消耗。Memcached是多线程,非阻塞IO复用的网络模型。
Redis命令
连接到Redis,需进入redis-cli所在目录
redis-cli -h 127.0.0.1 -p 6379 -a root
查看Redis服务器的各种信息和统计数值
info
查看所有Key
keys *
添加数据
set token1 123 EX 60
查看数据
get token1
查看过期时间
ttl token1
删除数据
del token1
清空Redis当前DB
flushdb
清空Redis所有DB
flushall
清空Redis集群
进入到redis-cli所在的目录
find / -name redis-cli*
./redis-cli -h 127.0.0.1 -p 7000
./redis-cli -h 127.0.0.1 -p 7001
./redis-cli -h 127.0.0.1 -p 7002
./redis-cli -h 127.0.0.1 -p 7003
./redis-cli -h 127.0.0.1 -p 7004
./redis-cli -h 127.0.0.1 -p 7005
各节点执行flushall
Linux安装Redis
cd /usr/local/app
wget http://download.redis.io/releases/redis-5.0.12.tar.gz
tar -zxvf redis-5.0.12.tar.gz
cd redis-5.0.12
make && make install
测试Redis是否安装成功
redis-cli -v
设置后台启动
daemonize yes
设置密码
requirepass Cs123456
启动
redis-server /usr/local/app/redis-5.0.12/redis.conf
停止
redis-cli -h 127.0.0.1 -p 6379 -a Cs123456 shutdown
连接
redis-cli -h 127.0.0.1 -p 6379 -a Cs123456
配置集群
mkdir redis-cluster-conf
cd redis-cluster-conf
mkdir 7001
mkdir 7002
mkdir 7003
mkdir 7004
mkdir 7005
mkdir 7006
cd ../..
cp redis.conf /usr/local/redis-5.0.5/redis-cluster-conf/7001/
cp redis.conf /usr/local/redis-5.0.5/redis-cluster-conf/7002/
cp redis.conf /usr/local/redis-5.0.5/redis-cluster-conf/7003/
cp redis.conf /usr/local/redis-5.0.5/redis-cluster-conf/7004/
cp redis.conf /usr/local/redis-5.0.5/redis-cluster-conf/7005/
cp redis.conf /usr/local/redis-5.0.5/redis-cluster-conf/7006/
redis.conf内容
port 7006
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
daemonize yes
protected-mode no
pidfile /var/run/redis_7006.pid
redis-server /usr/local/redis-5.0.5/redis-cluster-conf/7001/redis.conf
redis-server /usr/local/redis-5.0.5/redis-cluster-conf/7002/redis.conf
redis-server /usr/local/redis-5.0.5/redis-cluster-conf/7003/redis.conf
redis-server /usr/local/redis-5.0.5/redis-cluster-conf/7004/redis.conf
redis-server /usr/local/redis-5.0.5/redis-cluster-conf/7005/redis.conf
redis-server /usr/local/redis-5.0.5/redis-cluster-conf/7006/redis.conf
redis-cli --cluster create 192.168.3.137:7001 192.168.3.137:7002 192.168.3.137:7003 192.168.3.137:7004 192.168.3.137:7005 192.168.3.137:7006 --cluster-replicas 1