Redis
阿里云的这群疯子
- 分库分表+水平拆分+Mysql集群
Nosql
NoSQL,指的是非关系型的数据库。NoSQL有时也称作Not Only SQL的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称。
NoSQL用于超大规模数据的存储。(例如谷歌或Facebook每天为他们的用户收集万亿比特的数据)。这些类型的数据存储不需要固定的模式,无需多余操作就可以横向扩展。
- 方便扩展(数据库之间没有关系,很好扩展)
- 大数据量高性能(Redis 一秒可以写8万次,读取11万次)
- 数据类型是多样型的(不需要实现设计数据库,随取随用)
- 传统的RDBMS
- 结构化组织
- SQL
- 数据和关系都存在单独的表中
- 操作简单,数据定义语言
- 严格的一致性
- 基础的事务…
- Nosql
- 没有固定的查询语言
- 键值对存储、列存储、文档存储、图形数据库(社交关系)
- 最终一致性
- CAP定理二和BASE理论(异地多活)
- 高性能,高可用,高可扩展…
NoSql的四大分类
- KV键值对
- 新浪:Redis、
- 美团:Redis+Tair
- 阿里、百度:Redis+memecache
- 文档型的数据库(bson格式)
- MongDB:基于分布式文件存储的数据库
- C++编写的,主要用来处理大量的文档
- 介于关系型和非关系型数据库的中间产品
- 非关系数据库中功能最丰富,最像关系型数据库的
- 列存储数据库
- HBase
- 分布式文件系统
- 图像关系数据库
- 存的是关系
- Neo4j,infoGrid
Redis基础
- Remote Dictionary Server :远程字典服务
- 是一个开源的ANSI,C语言编写的、支持网络,可基于内存亦可持久化的日志型
- Key-Value数据库,并提供多种语言的API
- 内存存储、持久化(rdb、aof)
- 效率高,可以用于高速缓存
- 发布订阅系统,地图信息分析、计时器、计数器
- 多样的数据类型、持久化、集群、事务
- 默认端口6379
Linux下安装Redis
Redis性能测试(redis-benchmark)
- 参数列表
序号 | 选项 | 描述 | 默认值 |
---|---|---|---|
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 连接并等待。 |
- 例如:redis-benchmark -h localhost -p 6379 -c 100 -n 10000
Redis基本知识
- redis默认有16个数据库:配置文件中:databases 16
- 默认使用第0个数据库
- 切换数据库:select 数字
- 查看数据库大小:desize
- 清空当前数据库:flushdb
- 清空所有数据库:flushall
- 端口号:6379
- Redis是单线程的
- Redis是基于内存操作的
- Redis的性能瓶颈是根据内存,网络带宽,可以使用单线程实现,所以就使用单线程了
- 多线程(CPU上下文切换效率慢),对于内存系统来说,没有CPU上下文切换效率就是最高的
- 多次读写都在一块CPU上
Redis五大数据类型
- Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
Redis-Key
- 查看所有的key:keys *
- 判断是否存在:exit key
- 设置过期时间:exit key time
- 查看当前key的剩余时间:ttl name
- 移除数据:move key
- 查看key数据类型:type key
- 不会的命令去官网查询:命令查询页
String(字符串)
- 设置值:set key value
- 获得值:get key
- 追加字符串:append key value,如果不存在,新建一个字符串
- 值加一:incr key
- 值减一:decr key
- 值加多:incrby key value
- 值减多:decrby key value
- 截取字符串:getrange key 0 -1
- 替换指定位置的字符串:setrange key 2 value
- 设置过期时间:setex key seconds value
- 不存在在设置:setnx key value,(不存在就创建,存在就创建失败)
- 批量设置:mset k1 v1 k2 v2 k3 v3
- 批量获取:mget k1 k2
- msetnx:msetnx k1 v1,原子性操作,要么一起成功,要么一起失败
- 先获取再设置:getset key value,返回原值,设置新值
List(列表/可重复)
- 实际上是一个链表
- 在两边插入或者改动值,效率最高,中间元素相对较低
- 所有的List命令都是以L开头的
- 将值放在列表头部:lpush list v1 v2 v3
- 将值放在列表尾部:rpush list v1 v2 v3
- 查看位置值:lrange list 0 -1
- 左边移除一个值:lpop list
- 右边移除一个值:rpop list
- 通过下表获取列表中的值:Lindex list 3
- 返回列表长度:Llen list
- 移除指定个数的值:Lrem list 1 value
- 通过下表修剪截取列表:Ltrim list 1 2
- 移除列表的最后一个元素,将她移动到新的列表中:rpopLpush list1 list2
- 将列表中指定下标的值替换为另外一个值:lset list index value,如果不存在列表或下表会报错
- 在指定值的前面或后面插入值:Linsert list before|after 指定值 插入值
Set(集合/不能重复)
- 无序不重复集合
- 添加集合元素:sadd myset value
- 返回集合所有值:smembers myset
- 判断元素是否在集合中:sismember myset value
- 获取set集合中内容个数:scard myset
- 移除指定元素:srem myet value
- 随机抽选一个元素:srandmember myset number,number表示抽出几个
- 随机删除一些set元素:spop myset
- 将一个指定的值,移动到另外一个set中:smove myset myset2 value
- 差集:sdiff myset myset2
- 交集:sinter myset myset2
- 并集:sunion myset myset2
Hash(哈希)
- key-map:这个值是一个map集合
- 添加一个值:hset myhash key value
- 查找一个值:hget myhash key
- 添加多个值:hmset myhash k1 v1 k2 v2
- 得到多个值:hmget myhash k1 k2
- 获取所有的值:hgetall myhash
- 删除指定值:hdel myhash key
- 获取hash表的长度:hlen myhash
- 判断指定字段是否存在:hexists myhash key
- 获取所有字段:hkeys myhash
- 获取所有值:hvals myhash
- 指定增量:Hincrby myhash key number
- 如果不存在设置,存在则不设置:hsetnx myhash key value
Zset(有序集合)
- 在set的基础上添加一个值用来排序
- 添加一个值:zadd myset 1 value
- 查看所有值:zrange myset 0 -1
- 从小到大排序:zrangebyscore myset -inf +inf
- 从大到小排序:zrevrange myset 0 -1
- 获取集合中的个数:zcard myset
- 统计区间个数:zcount myset 1 4,统计1到4之间的个数
Redis三种特殊数据类型
geospatial(地理位置)
- 主要用于存储地理位置信息,并对存储的信息进行操作
- geoadd:添加地理位置的坐标。geoadd key 纬度 经度 名称
- geopos:获取地理位置的坐标。geopos key 名称
- geodist:计算两个位置之间的距离。geodist key 名称 名称 单位单位:m,km,英里mi,英尺ft
- georadius:根据用户给定的经纬度坐标来获取指定范围内的地理位置集合。georadius key 纬度 经度 半径大小
- 参数 whithdish:显示到中间距离的位置
- 参数 withcoord:显示他人的定位信息
- 参数 count 1:显示指定数量的结果
- georadiusbymember:根据储存在位置集合里面的某个地点获取指定范围内的地理位置集合。georadiusbymember key 名称 半径大小
- geohash:返回一个或多个位置对象的 geohash 值。
- 底层是zset集合,使用zset命令
hyperloglog(统计数据结构)
- 基数统计算法
- 占用内存是固定的,12KB,有0.81%错误率
- 创建一组元素:PFadd mykey v1 v2 v3
- 统计基数数量:PFcount mykey
- 合并两组:PFmerge 新mykey 原mykey 原mykey
bitmaps(位图数据结构)
- 操作二进制位来记录,只有01
- 记录数据:setbit sign 0 0
- 查看数据:getbit sign 0
- 统计为1个数:bitcount sign
Redis基本事务操作
- Redis单条指令保证原子性,但是事务不保证原子性
- 本质:一组命令的集合,一个事务中的所有命令都会被序列化,在事务执行过程中,按照顺序执行
- 没有隔离级别的概念
- 所有命令在事务中,并没有直接被执行,只有发起执行命令才执行
- 执行过程:
- 开启事务(multi)
- 命令入队
- 执行事务(exec)
- 取消事务(discard)
- 执行完事务就结束
乐观锁
- watch可以当作redis的乐观锁操作:watch key,unwatch key
Jedis
- 官方推荐的java连接开发工具
- 导入依赖
<!--jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
<!--fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
- 远程连接测试:连接远程redis
SpringBoot整合Redis(lettuce)
-
Springboot操作数据:Spring-data jpa jdbc mongodb redis
-
在springBoot2.x原jedis被替换为lettuce
- jedis:采用直连,多个线程操作不安全,要避免不安全的,使用jedis pool连接池。更像BIO模式
- lettuce采用netty,实例可以在多个线程中进行共享,不存在线程不安全的情况,减少线程数据,更像NIO模式
-
SpringBoot所有的配置类都有一个自动配置类:RedisAutoConfiguration
- 带入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 配置文件
spring.redis.host=119.3.226.219
spring.redis.password=123456
spring.redis.port=6379
- 测试连接
@SpringBootTest
class DemoApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
/**
* opsForValue(); 操作字符串
* opsForList(); 操作list
* opsForHash(); 操作hash
* opsForSet(); 操作set
* opsForZSet(); 操作zset
* opsForGeo(); 操作地图
* opsForHyperLogLog();
*/
redisTemplate.opsForValue().set("huang","hhhhh");
Object huang = redisTemplate.opsForValue().get("huang");
System.out.println(huang);
//常用的方法可以通过redisTemplate点出来
//获取连接的数据库对象
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
connection.flushDb();
}
}
Redis.conf详解
-
配置文件对大小写不敏感
-
可以包含多个配置文件
-
网络配置
- 绑定IP,指定哪些ip可以访问 bing 127.0.0.1
- 是否受保护:protected-mode no
- 端口设置:port 6379
-
通用配置
- 是否以守护进程开启,后台运行:daemonize yes
- 配置文件:pidfile /var/run/redis_6379.pid,如果以后台方式进程,就要指定一个pid文件
- 日志级别:loglevel notice
- 生成的日志文件名:logfile “”
- 默认的数据库数量:databases 16
- 是否显示logo:always-show-logo yes
-
快照:SNAPSHOTTING
- 持久化,在规定的时间内执行了多少次操作则会持久化到文件 .rdb .aof文件
- 没有持久化,断电即失去
- 900秒内至少有一个key修改,就进行持久化 save 900 1
- 持久化如果出错,是否继续工作:stop-writes-on-bgsave-error yes
- 是否压缩rdb文件:rdbcompression yes,需要消耗cpu资源
- 保存rdb文件的时候进行校验检查:rdbchecksum yes
- rdb保存文件名和目录:dbfilename dump.rdb dir ./
-
复制:REPLICATION
- 主机地址端口号:replicaof < masterip > < masterport >
- 主机密码:masterauth < master-password >
-
安全:SECURITY
- 设置命令:requirepass 123456
-
客户端:CLIENTS
- 设置最大的客户端连接数:maxclients 10000
- 设置最大的内存容量:maxmemory < bytes >
- 内存到达上限的处理策略:maxmemory-policy noeviction
- maxmemory-policy 六种方式
- volatile-lru:只对设置了过期时间的key进行LRU(默认值)
- allkeys-lru : 删除lru算法的key
- volatile-random:随机删除即将过期key
- allkeys-random:随机删除
- volatile-ttl : 删除即将过期的
- noeviction : 永不过期,返回错误
- maxmemory-policy 六种方式
-
aof配置:APPEND ONLY MODE
- 默认不开启:appendonly no,大部分情况rdb够用
- 持久化文件名字:appendfilename “appendonly.aof”
- 同步时间:appendfsync everysec,
- everysec:每秒同步一次
- always:每次修改都会同步
- no:不执行sync,操作系统自己同步,最快
持久化
RDB
- 在指定时间间隔内将内存中的数据集快照写入磁盘,Snapshot快照,恢复时直接将问价读到内存中
Redis会单独创建一个子进程来持久化,将数据写到一个临时文件中,持久化结束,替换之前持久化的文件,整个过程主进程不进行IO操作,确保了性能,如果需要大规模进行数据恢复,且对数据恢复的完整性不是非常敏感,那么RDB比AOF更加有效,RDB的缺点就是最后一次持久化后数据可能丢失,我们默认的就是RDB,一般情况下不需要修改这个配置
-
rdb保存的文件默认是:dunp.rdb,在
-
触发机制:
- save规则满足
- 执行flushall
- 推出redis
-
恢复RDB文件:将RDB文件放在redis启动目录下就可以了,启动时会自动检测文件恢复数据
AOF(Append Only File)
- 将我们所有的命令都记录下来,恢复的时候把文件全部执行一遍
- 以日志的形式来记录每个写操作,将Redis执行过的所有指令记录下来(写操作),只许追加文件,不可以改写文件,redis启动之初会读取文件重新构建数据
- Aof保存的是appendonly.aof
- 将配置文件中的appendonly 改为yes,重启既可以生效
- 如果aof配置文件有错误,redis启动不了,用 redis-check-aof --fix appendonly.aof,修复即可
Redis集群
- 主从复制,读写分离,
环境配置
- 配置:只配置从库,不用配置主库
- 查看当前库信息:info replication
# Replication
role:master # 角色
connected_slaves:0 # 从机个数
-
从机配置修改内容:(本机测试)
- 日志输出命名:logfile “”
- 备份名称:dbfilename name.rdb
- 端口号
- pid名称
-
默认是主库,只用配置从机就好了
- 从机中命令配置配置:slaveof host port
- 从机中文件配置:
- 主机地址端口号:replicaof < masterip > < masterport >,(重启变回主机)
- 主机密码:masterauth < master-password >
复制原理
-
主机负责写,从机负责读取
-
只要是从机就会马上负责
-
从机启动连接到master后会发送一个sync同步命令
-
Master接到命令,启动后台的存盘进程,同时搜集所有接受到的用于修改数据集命令,将整个文件传到从机,完成一次同步。
- 全量复制:从机接收到数据库文件数据后,将其存盘并加载到内存中
- 增量复制:Master继续将新的所有收集到的修改命令依次传给从机完成同步
-
断开从新连接就会触发全量复制
-
如果主机断开可以手动使用命令是自己成为主机:slaveof no one
哨兵模式
-
主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。
-
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
-
作用:
- 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
- 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。
-
一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。
用文字描述一下故障切换(failover)的过程。假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线。这样对于客户端而言,一切都是透明的。
配置哨兵
- 在Redis安装目录下有一个sentinel.conf文件
sentinel monitor myredis ip 端口号 1
# 1 代表主机挂了,从机投票让谁接替,票数最多成为主机
- 启动哨兵:redis-sentinel myconfig/sentinel.conf
- 如果主节点断开了,这个时候会从从机中按照算法选择一个作为主机
- 如果主机断开后回来,只能归并到新主机下当作从机
配置项 | 参数类型 | 作用 |
---|---|---|
port | 整数 | 启动哨兵进程端口 |
dir | 文件夹目录 | 哨兵进程服务临时文件夹,默认为/tmp,要保证有可写入的权限 |
sentinel down-after-milliseconds | <服务名称><毫秒数(整数)> | 指定哨兵在监控Redis服务时,当Redis服务在一个默认毫秒数内都无法回答时,单个哨兵认为的主观下线时间,默认为30000(30秒) |
sentinel parallel-syncs | <服务名称><服务器数(整数)> | 指定可以有多少个Redis服务同步新的主机,一般而言,这个数字越小同步时间越长,而越大,则对网络资源要求越高 |
sentinel failover-timeout | <服务名称><毫秒数(整数)> | 指定故障切换允许的毫秒数,超过这个时间,就认为故障切换失败,默认为3分钟 |
sentinel notification-script | <服务名称><脚本路径> | 指定sentinel检测到该监控的redis实例指向的实例异常时,调用的报警脚本。该配置项可选,比较常用 |
高并发问题
缓存穿透
用户想要查询一个数据,Redis中没有,会向持久层数据库中查询,发现也没有,查询失败
当有很多用户时,缓存如果都没有命中,就都去请求持久层数据库,会给持久层数据库带来很大压力,这个时候相当于缓存穿透
- 解决方案:
- 布隆过滤器
- 一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,避免对底层存储系统的查询压力
- 缓存空对象
- 当存储层不命中后,即使返回的空对象也将其缓存起来,同时设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护后端资源
- 布隆过滤器
缓存击穿
- 是指一个key非常热点,在不停的扛着高并发,当这个key在失效的瞬间,这个key在失效的瞬间,持续的大并发就会击破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞,或者是也该key在过期的瞬间,有大量的请求并发访问,会同时访问数据库来查询最新数据,并且回写缓存,会导致数据库瞬间压力过大
- 解决方案:
- 设置热点数据永不过期
- 从缓存层面来看,没有设置过期时间,所以不会出现热点key过期后产生的问题
- 加互斥锁
- 分布式锁:使用分布式锁,保证对于每个key同时只有一个线程去查询后端业务,其他线程没有获得分布式锁的权限,因此只需要等待即可,这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大
- 设置热点数据永不过期
雪崩
-
缓存雪崩:某一个时间段,缓存集中过期失效,Redis宕机
-
解决方案:
- redis高可用
- 增加Redis,搭建集群(异地多活)
- 限流降级
- 缓存失效后,通过加锁或队列来控制读数据库写缓存的线程数量,比如对某个key只允许一个线程查询数据和写缓存,其他线程等待
- 数据预热
- 数据加热的含义就是在正式部署之前,把可能的数据预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中,在即将发生大并发访问前手动触发加载缓存不同的key,设置不同过期时间,让缓存失效的时间点尽量均匀
- redis高可用