技术选型
什么是 NoSQL?
- Not Only Sql.
- 传统项目使用纯数据库
- 为互联网和大数据而生
- 水平(横向)扩展方便高效
- 高性能读取
- 高可用
- 存数据,做缓存
NoSql 常见分类
- 键值对数据库 - Redis, Memcache
- 列存储数据库 - Hbase, Cassandra
- 文档型数据库 - MongoDB, CouchDB
- 图形数据库 - Neo4j, FlockDB
什么是分布式缓存?
- 提升读取速度性能
- 分布式计算领域
- 为数据库降低查询压力
- 跨服务器缓存
- 内存式缓存
什么是 Redis?
NoSql
- 分布式缓存中间件
key-value
存储- 提供海量数据存储访问
- 数据存储在内存里,读取更快
缓存方案对比 - Memcache
优点:
- 简单的
key-value
存储 - 内存使用率比较高
- 多核处理,多线程
缺点:
- 无法容灾
- 无法持久化
缓存方案对比 - Redis
优点:
- 丰富的数据结构
- 持久化
- 主从同步、故障转移
- 内存数据库
缺点:
- 单线程
- 单核
Redis 概知
Redis 官网
Redis 安装
二进制包安装方式
二进制包:redis-5.0.5.tar.gz
安装前依赖准备
使用二进制方式安装Redis
,需要先执行yum install -y gcc-c++
命令去安装c
的执行环境。
然后执行make
命令和make install
命令,或者可以直接合并为一条命令去执行make && make install
。
Redis 配置
Redis 配置文件
redis.conf:
- 启用后台运行 - daemonize yes
- 工作空间 - dir /usr/local/redis/working
- 远程访问 - bind 0.0.0.0 如果要限制连接IP地址的话,就填写具体的IP即可
- 连接密码 - requirepass password
- 端口号 - REDISPORT 默认6379
Redis 命令
- 启动 - ./redis_init_script start
Redis 实战
Redis 命令
redis-cli -a password shutdown
:关闭redis./redis_init_script stop
:关闭redisredis-cli
:进入到redis客户端auth pwd
:输入密码set key value
:设置缓存get key
:获得缓存del key
:删除缓存redis-cli -a password ping
:查看是否存活。如果存活着的话,会返回一个PONG
。
Redis
的数据类型 - string
string
字符串
string: 最简单的字符串类型键值对缓存,也是最基本的
key
相关
keys *: 查看所有的key(不建议在生产上使用,有性能影响)
type key: key的类型
string
类型
get/set/del: 查询/设置/删除
set rekey data: 设置已经存在的key,会覆盖
setnx rekey data: 设置已经存在的key,不会覆盖
set key value ex time: 设置带过期时间的数据
expire key: 设置过期时间
ttl: 查看剩余时间,-1永不过期,-2过期
append key: 合并字符串
strlen key: 字符串长度
incr key: 累加1
decr key: 累减1
incrby key num: 累加给定数值
decrby key num: 累减给定数值
getrange key start end: 截取数据,end=-1代表到最后
setrange key start newdata: 从start位置开始替换数据
mset: 连续设值
mget: 连续取值
msetnx: 连续设值,如果存在则不设值
- 其他
select index: 切换数据库,总共默认16个
flushdb: 删除当前下边db中的数据
flushall: 删除所有db中的数据
Redis
的数据类型 - hash
hash
hash: 类似map,存储结构化数据结构,比如存储一个对象(不能有嵌套对象)
- 使用
hset key property value:
-> hset user name imooc
-> 创建一个user对象,这个对象中包含name属性,name值为imooc
hget username: 获得用户对象中name的值
hmset: 设置对象中的多个键值对
-> hset user age 18 phone 13888888888
hmsetnx: 设置对象中的多个键值对,存在则不添加
-> hset user age 18 phone 13888888888
hmget: 获得对象中的多个属性
-> hmget user age phone
hgetall user: 获得整个对象的内容
hincrby user age 2: 累加属性
hincrbyfloat user age 2.2: 累加属性
hlen user: 有多少个属性
hexists user age: 判断属性是否存在
hkeys user: 获得所有属性
hvals user: 获得所有值
hdel user: 删除对象
Redis
的数据类型 - list
- list
list: 列表,[a, b, c, d, ……]
- 使用
lpush userList 1 2 3 4 5: 构建一个list,从左边开始存入数据
rpush userList 1 2 3 4 5: 构建一个list,从右边开始存入数据
lrange list start end: 获得数据
lpop: 从左侧开始拿出一个数据
rpop: 从右侧开始拿出一个数据
pig cow sheep chicken duck
llen list: list长度
lindex list index: 获取list下标的值
lset list index value: 把某个下标的值替换
linsert list before/after value: 插入一个新的值
lrem list num value: 删除几个相同数据
ltrim list start end: 截取值,替换原来的list
Redis
的数据类型 - zset
sorted set
sorted set: 排序的set,可以去重可以排序,比如可以根据用户积分做排名,积分作为set的一个数值,根据数值可以做排序。set中的每一个member都带有一个分数
- 使用
zadd zset 10 value1 20 value2 30 value3: 设置member和对应的分数
zrange zset 0 -1: 查看所有zset中的内容
zrange zset 0 -1 withscores: 带有分数
zrank zset value: 获得对应的下标
zscore zset value: 获得对应的分数
zcard zset: 统计个数
zcount zset 分数1 分数2: 统计个数
zrangebyscore zset 分数1 分数2: 查询分数之间的member(包含分数1 分数2)
zrangebyscore zset(分数1(分数2:查询分数之间的member(不包含分数1和分数2)
zrangebyscore zset 分数1 分数2 limit start end: 查询分数之间的member(包含分数1 分数2),获得的结果集再次根据下标区间做查询
zrem zset value: 删除member
Redis
的持久化
RDB
- `Redis DataBaseAOF
- `Append Only File
Redis
的持久化机制 - RDB
- 什么是RDB
RDB
: 每个一段时间,把内存中的数据写入磁盘的临时文件,作为快照,恢复的时候把快照文件读进内存。如果宕机重启,那么内存里的数据肯定会没有的,那么在此启动redis后,则会恢复。
- 备份与恢复
- 内存备份 --> 磁盘临时文件
- 临时文件 --> 恢复到内存
RDB
优劣势
- 优势
- 每隔一段时间备份,全量备份
- 灾备简单,可以远程传输
- 子进程备份的时候,主进程不会有任何io操作(不会有写入修改或删除),保证备份数据的完整性
- 相对
AOF
来说,当有更大文件的时候可以快速重启恢复
- 劣势
- 发生故障时,有可能会丢失最后异常的备份数据
- 子进程所占用的内存比会和父进程一模一样,如会造成
CPU
负担 - 由于定时全量备份是重量级操作,所以对于实时备份,就无法处理了
RDB
的配置
- 保存位置,可以在
redis.conf
自定义:/usr/local/redis/working/dump.rdb
- 保存机制:
save 900 1
save 300 10
save 60 10000
save 10 3
* 如果1个缓存更新,则15分钟后备份
* 如果10个缓存更新,则5分钟后备份
* 如果10000个缓存更新,则1分钟后备份
* 演示:更新3个缓存,10秒后备份
* 演示:备份dump.rdb,删除重启
stop-writes-on-bgsave-error
yes
: 如果save
过程出错,则停止写操作no
: 可能造成数据不一致
rdbcompression
yes
: 开启rdb
压缩模式no
: 关闭,会节约cpu
损耗,但是文件会大,道理同nginx
rdbchecksum
yes
: 使用CRC64
算法校验对rdb
进行数据校验,有10%性能损耗no
: 不校验
Redis
的持久化机制 - AOF
AOF
特点
- 以日志的形式来记录用户请求的写操作。读操作不会记录,因为写操作才会存存储。
- 文件以追加的形式而不是修改的形式。
redis
的ao
恢复其实就是把追加的文件从开始到结尾读取执行写操作。
优势
AOF
更加耐用,可以以秒级别为单位备份,如果发生问题,也只会丢失最后一秒的数据,大大增加了可靠性和数据完整性。所以AOF
可以每秒备份一次,使用fsync
操作。- 以
log
日志形式操作,如果磁盘满了,会执行redis-check-aof
工具。 - 当书记太大的时候,
redis
可以在后台自动重写aof
。当redis
继续把日志追加到老的文件中去时,重写也是非常安全的,不会影响客户端的读写操作。 AOF
日志包含的所有写操作,会更加便于redis
的解析恢复。
劣势
- 相同的数据,同一份数据,
AOF
比RDB
大。 - 针对不同的同步机制,
AOF
会比RDB
慢,因为AOF
每秒都会备份做些操作,这样相对与RDB
来说就略低。每秒备份fsync
没毛病,但是如果客户端的每次写入就做一次备份fsync
的话,那么redis
的性能就会下降。 AOF
发生过bug
,就是数据恢复的时候数据不完整,这样显得AOF
会比较脆弱,容易出现bug
,因为AOF
没有RDB
那么简单,但是呢为了防止bug
的产生,AOF
就不会根据旧的指令去重构,而是根据当时缓存中存在的数据指令去做重构,这样就更加健壮和可靠了。
AOF
的配置
# AOF 默认关闭,yes 可以开启
appendonly no
# AOF 的文件名
appendfilename "appendonly.aof"
# no:不同步
# everysec:每秒备份,推荐使用
# always:每次操作都会备份,安全并且数据完整,但是慢性能差
appendfsync everysec
# 重写的时候是否要同步,no可以保证数据安全
no-appendfsync-on-rewrite no
# 重写机制:避免文件越来越大,自动优化压缩指令,会fork一个新的进程去完成重写动作,新进程里的内存数据会被重写,此时旧的aof文件不会被读取使用,类似rdb
# 当前AOF文件的大小是上次AOF大小的100% 并且文件体积达到64m,满足两者则触发重写
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
Redis 缓存过期处理与内存淘汰机制
引子
计算机内存有限,越大越贵,Redis
的高并发高性能都是基于内存的,用硬盘的话GG。
已过期的key
如何处理?
设置了expire
的key
缓存过期了,但是服务器的内存还是会被占用,这是因为redis
所基于的两种删除策略
redis
有两种策略
- (主动)定时删除
- 定时随机的检查过期的
key
,如果过期则清理删除。(每秒检查次数在redis.conf
中的hz
配置)
- (被动)惰性删除
- 当客户端请求一个已经过期的
key
的时候,那么redis
会检查这个key
是否过期,如果过期了,则删除,然后返回一个nil
。这种策略对cpu
比较友好,不会有太多的损耗,但是内存占用会比较高。
如果内存被 Redis 缓存占用满了怎么办?
内存占满了,可以使用硬盘来保存。但是没意义,因为硬盘没有内存快,回影响redis
性能。
所以,当内存占用满了以后,redis
提供了一套缓存淘汰机制:MEMORY MANAGEMENT
* noeviction: 旧缓存永不过期,新缓存设置不了,返回错误
* allkeys-lru: 清除最少用的就缓存,然后保存新的缓存(推荐使用)
* allkeys-random: 在所有的缓存中随机删除(不推荐)
* volatile-lru: 在那些设置了expire过期时间的缓存中,清除最少用的旧缓存,然后保存新的缓存
* volatile-random: 在那些设置了expire过期时间的缓存中,随机删除缓存
* volatile-ttl: 在那些设置了expire过期时间的缓存中,删除即将过期的
Redis 哨兵模式
引子
Master
挂了,如何保证可用性,实现继续读写
什么是哨兵
Sentinel
(哨兵)是用于监控Redis
集群中Master
状态的工具,是Redis
高可用解决方案,哨兵可以监视一个或多个redis master
服务,以及这些master
服务的所有从服务;当某个master
服务宕机后,会把这个master
下的某从服务升级为master
来替代已宕机的master
继续工作。
配置哨兵监控Master
创建并配置
sentinel.conf
- 普通配置
port 26379
pidfile "/usr/local/redis/sentinel/redis-sentinel.pid"
dir "/usr/local/redis/sentinel"
daemonize yes
protected-mode no
logfile "/usr/local/redis/sentinel/redis-sentinel.log"
- 核心配置
# 配置哨兵
sentinel monitor mymaster 127.0.0.1 6379 2
# 密码
sentinel auth-pass <master-name> <password>
# master被sentinel认定为失效的间隔时间
sentinel down-after-milliseconds mymaster 30000
# 剩余的slaves重新和新的master做同步的并行个数
sentinel parallel-syncs mymaster 1
# 主备切换的超时时间,哨兵要去做故障转移,这个时候哨兵也是一个进程,如果他没有去执行,超过这个时间后,会由其他的哨兵来处理
sentinel failover-timeout mymaster 180000
启动哨兵 x3
redis-sentinel sentinel.conf
测试
master
挂了,看slave
是否成为master
master
恢复,观察slave
状态
结论
master
挂了以后,由于哨兵监控,剩余slave
会进行选举,选举后其中一个成为master
,当原来的master
恢复后,他会成为slave
。
解决原Master恢复后不同步问题
一般master
数据无法同步给slave
的方案检查如下:
- 网络通信问题,要保证互相
ping
通,内网互通。 - 关闭防火墙,对应的端口开放(虚拟机汇总建议永久关闭防火墙,云服务器的话需要保证内网互通)。
- 统一所有的密码,通过逐台检查机器以防某个节点被遗漏。