Redis
引入:
nginx反向代理多个服务器后如何解决session问题,让每个访问其都能得到session?
1.存储在客服端cookie(不安全)
2.session复制(每台服务器都复制,数据冗余,浪费大)
3.缓存数据库nosql,完全在内存中,速度快(不需要经过IO)
1.Nosql
指not only sql 不仅仅是sql,称为非关系数据库。打破业务逻辑存储的方式,而以简单的Key-value模式存储(大大增加了扩展能力)。支持事务(不支持ACID特性)。
2.Redis介绍
redis中占用6379端口是通过九键输入的对应Merz(女明星)。
redis是单线程+多路IO复用技术(黄牛(单线程)帮多个人(多路IO复用)买不同的车票)。
redis还支持多种数据结构存储如list、set、hash、zset等。
redis的数据都在内存中,支持持久化,主要用于备份恢复。
3.Redis指令(key)
keys * 查看当前库所有key
select 数字 表示切换数据库,redis中有16个数据库,默认0号库【0-15】
type key 查看你的key是什么类型
del key 删除指定key的数据
unlink key 也是删除key的数据但是根据value选择非阻塞删除(真正的删除在后续异步操作,离线删除)
expire key 10 给key设置过期时间10秒
ttl key 查看还有多少秒过期,-1表示永不过期、-2表示已经过期
dbsize 查看当前数据库的key数量
flushdb 清空当前库
flushall 通杀所数据库
4.常用命令(string)
set key value 设置key-value值(相同key会覆盖value值)
get key 获取key对应的value值
append key value 给value值后面追加内容(返回组合后的长度)
strlen key 查看该key值的长度
setnt key value 只有key不存在才能设置
incr key 将key中的数字值加一(要数字类型)
decr key 将key中的数字值减一
incrby/decrby 步长 将key中的数字值增减,自定义步长
mset key1 key2 … 同时设置一个或多个key-value (原子性的msetnt也可)
mget key1 key2 … 同时获取一个或多个value
getrange key 起始 结束 获得该范围的值类似java中的substring
setex key 过期时间 value 设置值的时候就设置过期时间
getset 以新值换旧值
5.原子操作
指不会被线程调度机制打断的操作。多线程中不被其他线程打断的操作。
Redis的原子操作主要得益于它的单线程。
6.Redis列表(list)
简单的字符串列表,底层是双向链表。
常用命令:
lpush/rpush k1 v1 v2 v3 从左边/右边插入一个或多个值
lrange k1 0 -1 按索引下标获取元素(从左到右,-1表示最后一个元素)
lpop/rpop key 从左/右吐出一个值(值在键在,值光键亡)
rpoplpush k1 k2 表示从k1右边张吐出一个值放到k2的左边
lindex k1 0 根据索引获取元素,从左到右
llen key 获取列表长度
linsert k1 before “v2” “new 2” 表示在k1中的v2元素前加入新元素new2(after之后)
lrem k1 2 “new 2” 删除k1中2个new 2元素
lset k1 1 at 替换索引1的元素为at
7.Redis集合(set)
和list类似是一个列表的功能,区别是set可以自动排重的,但要存储一个列表数据又不希望出现重复数据时set是个好的选择。
Redis的set是string类型的无序集合,底层是value为null的hash表,所以添加、删除、查找的复杂度都是O(1)。
命令:
sadd k1 v1 v2 v3 将v1、v2、v3加入到集合k1中(已存在的元素会被忽略)
smembers k1 取出k1集合的所有元素
sismember k1 v2 查看是否存在v2元素
scard k1 返回该集合的元素个数
srem k1 v1 删除
spop 随机从集合中吐出一个值
srandmember key 1 随机取出1个值但不会从集合中删除
smove 值从一个集合移动到另一个集合
sinter两集合的交集、sunion并集、sdiff差集
8.Redis哈希(hash)
是一个string类型的field和value的映射表(键值对),适合存储对象。(类似java中的Map<Stting,Object>)
value分成两部分(field和value)来存储
命令:
hset user id 1、hset user name jk 设置hash表
hget user name 取name字段的值
hkeys user 查看该hash所有的字段
hvals user 查看该hash所有的字段的值
9.Redis有序集合(Zset)
没有重复元素的字符串有序集合。不同处:每个成员都关联了一个评分(score),该score用来安装最低分到最高的方式排列集合中的成员。(集合中的成员是唯一的但是score可以是重复的)
zset底层使用了两种数据结构:
- hash,value为field是字段和value是score
- 跳跃表,目的在于给元素value排序,根据score的范围获取元素列表
命令:
zadd k1 200 java 300 php 有序集合k1中添加成员
zrange k1 0 -1 查看有序集合k1中所有成员,后面加withscores可显示评分
zrevrangebyscore k1 500 200 按分数从大到小显示(默认从小到大)
zincrby k1 50 java 给元素的score加上增量
zcount统计该集合score区间的元素个数
zrem删除指定值的元素
zrank返回值在集合中的排名从0 开始
10.redis的发布和订阅
是一种消息通信模式,发送者发送消息,订阅者接收。redis客户端可以订阅任意数量的频道。
- subscribe channel1 一个客服端订阅频道1
- publish channel1 java 另一个客服端给频道1发送消息java
11.新数据类型
-
Bitmaps:对位进行操作的字符串(以位为数组只能放0和1,下标叫做偏移量)
-
HyperLogLog:用来做基数统计的算法(和set差不多),优点是输入元素数量很大是计算基数所要空间是固定的且是很小的。
基数:如{1,3,5,7,5,7,8},其邸的基数集为{1,3,5,7,8}(去重)
-
Geospatial:(GEO地理信息的缩写)该类型就是元素的2维坐标,在地图上就是经纬度。如添加地理位置geoadd key 经度 纬度 名称
12.使用Jedis
java中操作redis的使用Jedis。
需要在配置文件中注释掉# bind 127.0.0.1,**如果指定了bind,则说明只允许来自指定网卡的Redis请求。*如果没有指定,就说明可以接受来自任意一个网卡的Redis请求。**可让其他ip主机访问redis;再protected-mode no默认是yes需要改为no。
简单使用:
public class JedisDemo1 {
public static void main(String[] args) {
Jedis jedis = new Jedis("114.116.74.100",6379);//连接
//测试是否连接成功
System.out.println(jedis.ping());
jedis.close();
}
@Test
public void demo1(){
Jedis jedis = new Jedis("114.116.74.100",6379);
//添加
jedis.set("name", "luck");
//获取value
String s = jedis.get("name");
System.out.println(s);
//获取所有key
Set<String> keys = jedis.keys("*");
for (String key : keys) {
System.out.println(key);
}
jedis.close();
}
}
13.Redis事务
multi开启事务,之后的操作会放到指令队列中,当输入exec就执行(相当于comit提交)
输入discard则撤销命令不执行。
- 组队时有语法错误,整个队列的命令都不会执行。
- 组队中没有语法错误,在执行的时候谁有错误谁就不执行其他没有错误的正常执行。
14.锁机制
解决事务冲突的问题。
-
悲观锁
顾名思义,很悲观,每次别人拿数据的时候都认为别人会修改,所以每次都上锁,这样别人那这个数据时就会block直到它拿到锁。传统关系型数据库用到了很多该锁机制,如行锁、表锁等,读锁、写锁等,都是在操作之前上锁。
-
乐观锁
乐观, 每次别人拿数据的时候都认为别人不会修改,所以不会上锁,而通过版本号来操作。在数据上加个版本号,谁操作了数据就要把版本号同步更新,这样别人在进行操作时还需判断版本号与数据库中的是否一致,一致才允许操作。适用于多读的应用类型,可提高吞吐量。Redis就是利用这种check-and-set机制实现事务的。
Redis中乐观锁操作:
执行multi之前先执行watch key1 … 可以监视一个或多个key,如果在事务执行前这个key被其他命令所改动,则事务将被打断。unwatch取消监视。
秒杀案例中Redis使用乐观锁可以解决超卖的问题,但是会出现库存遗留问题(只有一个人抢到,其他人版本号不一致都买不到,但是库存还有),可使用lua脚本编写执行redis命令期间不会被别人打断(具有原子性)。
15.Redis持久化
-
rdb(redis database),某个时间段的快照,通过fork建立一个子进程先将数据写入一个临时文件再替换上次持久化好的文件。最后一次操作的数据存在丢失,写时复制技术。(默认开启)
dump.rdb文件就是备份文件,在配置文件中有操作次数多少时间保存的规则;通过cp该rdb文件可实现备份,恢复这改名为dump后redis会自己找。
-
rof 以日志的形式来记录每个写操作(读不记录),只许追加文件但不可改写文件,redis重启后会根据日志文件的内容将写指令从前到后执行一次以此完成数据恢复的工作。(默认不开启)
在redis.conf文件中开启yes,配置文件名称默认为appendonly.aof;aof与rdb保存路径一致。
备份恢复和rdb一样(复制粘贴改名)
- 如果aof文件损坏,可用redis-check-aof–fix appendonly.aof进行修复
-
aof与rdb同时开启,默认用aof(数据不存在丢失)
16.主从复制
主机更新数据后自动同步到备机中,主机以写为主M,备机以读为主S。
- 读写分离,性能扩展
- 容灾备份,快速恢复(一主多从,可用集群实现多主多从)
从机需要设置命令:slaveof 主机的ip 主机的端口
info replication可以查看当前服务器属于主还是从。
一主二仆:
- 当从服务器挂掉后需要重新加入主从关系,加入后会自动同步主机的所有数据。
- 主服务器挂掉后,从服务器保持(大哥永远是大哥)
薪火相传:
- 由从服务器发展下线,同步给其他从服务器,其他从服务器不与主服务器有主仆关系
反客为主:
- 大哥(主服务器)挂掉后兄弟从服务器直接上位。用slaveof no one将从机变成主机
哨兵模式(反客为主自动版):
- 自动上位,能够后台监控主机是否故障,故障了根据投票自动将从库转为主库。
17.集群
解决容量不够(开多台服务器redis)、并发写操作分摊(读写分离)问题。
传统是使用代理主机的方式(需要服务器过多),但redis3.0提供了解决方案无中心化集群配置。任何一台服务器都可作为集群的入口,进入一个就可互相访问。
redis集群实现了水平扩容,即启动n个节点,将整个数据库发布存储在这n个节点上,每个节点存储总数据的1/n。
通过分区来提供一定的可用性。(一部分节点失效也可继续处理命令)
redis集群不支持lua脚本。
redis -cli -c -p 端口 用集群的方式进入
cluster nodes 查看集群中节点的信息
分配原则:尽量保证每个主数据库运行在不同的ip地址,每个主从库都不在一个ip地址上。
slot插槽,每个主机都是总插槽值(自己给定)的一部分有范围,当set时拿着key去计算key所在的插槽(平均分摊到不同主机中),这样就实现切换到不同的主机。 可通过cluster keyslot key 查看key的插槽值。
主机挂掉后从机上位,如果主从都挂了,看配置cluster-require-fuil-coverage为yes则整个集群挂掉,no只是该插槽数据不能用。
18.缓存穿透
出现下列现象就叫缓存穿透:
- web服务区压力变大
- redis命中率降低
- 一直查询数据库
可能是由导致:
- redis查询不到数据库
- 出现很多非正常url访问(恶意攻击)
解决方案:
- 对空值进行缓存:如查询到的数据为空,将null进行缓存,设置空结果的过期时间会很短,最长不超过5分钟。
- 设置白名单:使用bitmaps定义一个访问名单,如访问id不在名单中直接拦截。
- 使用布隆过滤器
- 进行实时监控:当发现redis的命中率急速降低,要排查访问对象和访问数据,和运维设置黑名单限制服务。
19.缓存击穿
出现下列现象就叫缓存击穿:
- 数据库访问压力瞬时增加
- redis没有出现大量key过期
- redis还是正常运行
可能是由导致:
- redis中某个key过期,大量访问使用了这个key (热门访问key)
解决:
- 预先设置热门数据:加大key时长
- 实时调整:现场监控,实时调整key时长
- 使用锁(效率低)
20.雪崩问题
数据库压力变大导致服务器崩溃。
可能是由导致:
在极少的时间段,查询大量key的集中过期情况。
解决:
- 构建多级缓存架构:Nginx缓存+redis缓存+其他缓存(ehcache等)
- 使用锁或队列
- 设置过期标致更新缓存:设置提前值,如果过期会触发通知另外线程去后台更新实际的缓存
- 将缓存失效时间分散开:失效时间增加一个随机值,这样每个缓存的过期时间重复率降低。
21.分布式锁
跨越jvm互斥机制,一把锁能在分布式系统中使用。(共享锁)
三种方案:
- 基于数据库实现分布式锁
- 基于缓存(redis)
- 基于Zookeeper
redis实现分布式锁:
- setnx k v 设置k-v且上上锁(上锁后不能再加新的k-v)
- del k 解锁删除k
- expire 设置key过期时间,解决锁一直不释放问题
- set k v nx ex 12 即上锁又设置过期时间