Redis 初 级 精 讲
原创者:文思
I:Redis前转
一、软件系统的演变
1998-2008:单机mysql时代(MyISUM引擎[表锁]-->InnoDB引擎[行锁])—>Memached+mysql+垂直拆分—>mysql主从分离à分表分库+集群
瓶颈:
1、数据总量大小,当一台机器放不下时
2、 数据索引(B+Tree),当一台机器内存放不下时
3、 访问量(读写混合或分离),当一个实例不能承受时
MySQL数据库也经常存储一些大文本字段,导致数据库表非常的大,在做数据库恢复的时候就导致非常的慢,不容易快速恢复数据库。比如1000万4KB大小的文本就接近40GB的大小,如果能把这些数据从MySQL省去,MySQL将变得非常的小。关系数据库很强大,但是它并不能很好的应付所有的应用场景。MySQL的扩展性差(需要复杂的技术来实现),大数据下IO压力大,表结构更改困难,正是当前使用MySQL的开发人员面临的问题。
2008年以后互联网飞速发展的时代,其应用的特点是3V+3高:
海量volume,多样variety,实时velocity
高并发,高可扩,高性能
互联网公司的应用架构越来越微服务化:
互联网时代背景下,大数据量、高并发,频发读写数据库压力大,多数据源,多数据类型。而常见关系型数据库难以满足需求,所以NoSql的兴起。
以淘宝女装为例:
1、商品基本信息。(传统关系型数据库类)
2、商品描述、详情、评价信息、多文字类(1000万条基本上很多个G了,一般用MongDB)
3、商品图片。(静态服务器)
4、商品的关键字。(Isearch)
5、 商品波段性的热点高频词汇信息。(内存数据库如:Redis)
6、 商品的交易、价格计算、积分累计。(支付宝及第三方支付接口)
系统中针对多数据源、多数据类型、数据源改造而数据服务平台不需要大面积重构这种需求成为主要应用场景。
解决:UDSL统一数据平台。在此不作详细描述。
为解决以上需求,NoSql数据模型兴起,已成为大中型互联网公司的标配架构之一。
二、NoSql数据模型的兴起
当前模式:sql + nosql
Not Only Sql非关系型数据库。
NoSql如何设计:
{
"customer":{
"id":1136,
"name":"zhangsan",
"billingAddress":[{"city":"BeiJing"}],
"orders":[
{"id":"123","name":"巧克力"},
{"id":"456","name":"牛肉"},
{"id":"789","name":"黄金"}
],
......
}
}
大并发场景下传统sql层设计原则:
传统关系型数据库高并发的操作强烈不建议有关联查询;
适当允许冗余数据来避免关联查询;
分布式事务支持不了太多并发。
如果按BSON这种数据格式存储的进行查询,就能避免如上问题,如根据id=1136就可避免数据库关联查询,而查出数据格式为BSON格式的所有信息字符串。
NoSql的聚合模型:
1、K-V:代表redis
2、文档型数据库(数据模型是BSON):代表MongDB。BSON是类似json的二进制存储格式。
3、列存储数据库(列族):代表Hbase。按列存储数据,即纵向的K-V,列纵向扩展。如:
name:”zhangsan”
billingAddress:….
4、图形:放的不是图形,是关系,专注于构建关系图谱
NoSql特点:
1、 去关系型,数据之间无关系,所以易扩展。
2、数据模型灵活多样,无需事先为要存储的数据建立字段,可随意增删字段。
3、强调最终一致性,而非ACID
4、 CAP定理(Consistency强一致性,Availability可用性,Partition tolerance分区容错性)
CAP:一个分布式系统不可能同时满足CAP三种需求,最多只能同时满足两个。
三种组合CA、CP、AP哪个最重要呢?
常理都是可用性第一(A),其次分析C(一致性)和P(分区容错性)哪个重要,NoSql时代一般是分布式应用,分区容错性是必须实现的,所以AP是互联网场景下大多数网站架构的选择,而CA代表着以前传统数据库的思想选择。而CP是Redis、MongDB的思想选择。
BASE:为解决关系数据库强一致性引起的可用性降低问题而提出的解决方案。
BasicallyAvailable基本可用
SoftState软状态
EventuallyConsistent最终一致性
互联网应用架构的设计思想AP+BASE:通过让系统放松对某一时刻数据一致性的要求来换取系统整体伸缩性和性能上的改观。
II:Redis初级
一、Redis基础
1、安装
Remote Dictionary Server远程字典服务器,支持将内存数据异步存储到硬盘,有类似httpsession设置定时过期功能,定时器计数器,发布订阅消息系统。
redis.io官网
怎么玩(以下是此文章学习梳理知识点的顺序方向):
1、数据类型、基本操作和配置
2、持久化和复制,RDB/AOF
3、事务的控制
4、复制(哨兵模式)
安装……略,redis.conf配置文件(重要)
启动:redis-servier /redis-3.2.1/redis.conf
启动客户端:redis-cli –p 6379
Ping得到pong说明连接成功:
set k1 hello
get k1
得到hello说明功能ok:
Redis是单进程的。
2、Redis.conf详讲
port 6379是配置启动端口的地方,默认是6379:
默认创建16个数据库,并且可以选择,database:
通过select命令切换数据库,分库的作用主要用来业务分片:
dbsize查看当前数据库的key的数量:
flushdb清除当前库所有key(当前库):
flushall清除所有库中的key,慎用。Redis的索引都是从0开始。
3、Redis五大数据类型:
1、String
2、 Hash
3、 List
4、 Set
5、Sorted set
String是redis中基本的数据类型,是二进制安全的,所以可以包含任何数据,比如图片或者序列化对象,一个字符串value最多可以是512M
Hash哈希是一个键值对集合,是一个String类型的fieldhevalue的映射表,特别适合用于存储对象。类似java里面的Map
List列表是简单的字符串列表,按照插入顺序排序。
Set集合是String类型的无序集合,它通过HashTablelai实现的
Sorted set有序集合,和set不同的是每个元素都会关联一个double类型的分数,通过分数值来为成员进行从小到大的排序。
4、常用命令:
1)和key相关的常用命令:set、get、exists key
set、get略
exists
key判断一个key是否存在:
根据命令可以推出jedis的api参数及返回值
move移动key从当前库到目标库,move keyname targetBD:
以上演示了:0号库有k1,k2,k3三个key,通过move移动k3到1号库。
expire key设置key的过期时间。ttl key代表查看还有多少秒过期,-1代表永不过期,-2代表已过期:
使用expire设置k1过期时间为10秒后然后ttl查看:
10秒后k1就不存在了,已过期就自动从内存系统中移除。
type key查看key的类型:
2)和String相关的常用命令:
set/get/del/append/strlen
set设置相同的key,前一个会被后一个覆盖:
其它命令略。
Incr/decr/incrby/decrby,一定要是数字才能进行加减:
Incr每次递增1
Incrby可以指定递增的数值
decrby递减,略。
setex(set with expire)设置值并加过期时间setex 过期时间值:
setnx(set if not exist),key不存在时才进行set:
批量操作mset/mget/msetnx:
3)和List相关的常用命令:
Lpush/rpush/lrang
lpush是后进先出排序方式,rpush是先进先出排序方式。
lpip/rpop是一个一个移出:
lindex按下标取元素:
lrem删除:
ltrim截取list:ltrim key startindex endindex,演示略。
pop与push的组合应用:
linsert命令:linsert key before/after 值1 值2
总结:如果值全部移除,对应的键也就消失了,链表从头插入效率高,中间效率很慢。
3)set常用命令:
sadd/smembers/sismember
上图可以体现出set有序但不允许重复值。
scard获取集合里元素个数,srem key value删除集合中元素。
sdiff差集/sinter交集/sunion并集:
3)hash常用命令:
hset/hget/hmset/hmget/hgetall/hdel
Hexists key在key里面的某个值的key是否存在:
Hkeys/hvals里面的全部key/value:
Hincrby/hincrbyfloat:
5)zset常用命令:
先回忆一下zset定义: Sorted set有序集合,和set不同的是每个元素都会关联一个double类型的分数,通过分数值来为成员进行从小到大的排序。
zadd/zrang,通过定义来推测可能要比set命令多一个double分数类型的参数:
zcard/zcount key score/zrank key values等命令。
5、解析Redis配置文件redis.conf:
1)单元:单位定义,大小写不敏感
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
2)包含,支持配置文件外置化,incolud包含进来外置化的配置
# include
.\path\to\local.conf
# include
c:\path\to\other.conf
3)GENERAL标准化配置
daemonize:
出厂默认是no,改成daemonize yes
Redis默认不是以守护进程的方式运行的,修改此参数,启用守护进行运行。
pidfile:
进程管道pip文件,当运行起来时并没有指定其它路径时,将在此路径下产生。
port是启动端口,就不用细说。
tcp-backlog:
tcp-backlog 511
设置tcp的backlog,backlog是一个连接队列,在高并发环境下需要一个高的backlog值来避免慢客户端连接问题。
bind:
绑定的一些端口设备:
# bind 192.168.1.100 10.0.0.1
# bind 127.0.0.1
timeout:
# Close the connection after a client isidle for N seconds (0 to disable)连接超时设置:
timeout 0
代表客户端闲置多久后关闭连接,0代表一直不关闭
tcp-keepalive:
# TCP keepalive.是否进行keepalive心跳检测,建议设置成60秒:
tcp-keepalive 0
loglevel:
# Specify the server verbosity level.
# This can be one of:日志级别及选择
# debug (a lot of information, useful fordevelopment/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / criticalmessages are logged)
loglevel notice
syslog-enabled:
是否把日志输出到syslog中
syslog-ident:
指定syslog里的日志标志
Redis默认的数据库个数:
# Set the number of databases. The defaultdatabase is DB 0, you can select
# a different one on a per-connection basisusing SELECT where
# dbid is a number between 0 and'databases'-1
databases 16
4)SNAPSHOTTING快照
dbfilename:指定本地数据库文件名
dbfilename dump.rdb
dir:指定本地数据库存放目录
dir ./
5)SECURITY安全
redis默认空密码,使用config get命令查看
使用config get命令查看启动路径
同理,config set 是一些常用配置的设置,比如设置密码:config set requirepass “123456”或者requirepass “123456”。配制后客户端连接redis时需要通过AUTH命令提供密码。
6)LIMIT限制
Maxclients:最大连接数限制
# Set the max number of connected clientsat the same time. By default
# this limit is set to 10000 clients当没有设置时会默认10000
Maxmemory:最大内存。vm-max-memory设置为0时(默认值0),Key存放在内存,value存放在swap区。
Maxmemory-policy:达到最大内促后,缓存过期清洁策略
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached. You can select among fivebehaviors:
# (根据lru算法先进先出,只对设置了过期时间的键有效)volatile-lru:remove the key with an expire set using an LRU algorithm
# (根据lru算法最近最少使用)allkeys-lru:remove any key according to the LRU algorithm
# (在过期集合中移除随机key,只对设置了过期时间的键有效)volatile-random:remove a random key with an expire set
# (随机)allkeys-random :remove a random key, any key
#(有限时间内,移除那些TTL值最小的key,ttl最小代表最近要过期)volatile-ttl -> remove the key with the nearest expire time (minor TTL)
# (永不过期)noeviction:don't expire at all, just return an error on write operations
# The default is:
# maxmemory-policy volatile-lru
生产环境一定不要设置成永不过期
Maxmemory-samples:设置样本数量,LRU算法和最小TTL算法都并非十分精确,而是估值,所以可以设置样本大小,redis默认会从样本数量中选择进行测试出估值。
二、Redis持久化
RDB(RedisDataBase)、AOF(Append Only File)
如果大规模数据恢复,且对数据恢复的完整性不敏感,rdb要比aof高效,但rdb缺点是最后一次的持久化数据可能丢失。
RDB:在指定的时间间隔内将内存中的数据集快照(snapshot快照)写入磁盘,它恢复时是将快照文件直接读到内存中。
Redis会创建一个子进程来进行持久化,整个过程中主进程不进行任何IO操作。
内部原理:fork的作用是复制一个与当前进程一样的进程,新进程的所有数据数都和原进程一致,此进程作为原进程的子进程。隐患是当内存数据很大很慢时会更慢,需考虑内存空间。
SNAPSHOTTING中:
# Will save the DB if both the given number ofseconds and the given
# number of write operations against the DBoccurred.
# In the example below the behaviour will beto save:
# after 900 sec (15 min) if at least 1 keychanged(900秒内只要有1个改变)
# after 300 sec (5 min) if at least 10 keyschanged(300秒内只要有10个改变)
# after 60 sec if at least 10000 keys changed(00秒内只要有10000个改变)
save 900 1
save 300 10
save 60 10000
满足上述三种条件之一当停止服务时就会触发reids持久化。用法:save <秒> <写操作次数>
# The filename
where to dump the DB
dbfilename
dump.rdb备份文件所在地
模拟生产运维操作:现在进行三分钟内(300秒)内变更10次以上:
这时可以看到磁盘下生成了dump.rdb文件
将dump.rdb重命名为dump_bak.rdb,并删除dump.rdb(模拟生产运维流程)。因为配置中dump.rdb是备份文件,所以再启动时无法恢复。
再次启动并清除所有key:
将dump_bak.rdb重命名恢复成dum.rdb,并且重启redis:
看到所有的key都已经恢复。
或者执行save命令也可以立刻执行快照持久化,但此操作会在持久化时把其它操作全部阻塞,慎用或者夜晚用户低谷时使用。
bgsave:后台异步进行快照持久化,同时快照还可以执行其它操作。
Stop –writes-on-bgsave-error命令:当进行持久化save出错时前端禁止写入数据。
Stop –writes-on-bgsave-error
yes,如果配置成no表示你不在乎数据不一致或者有其它手段发现和控制。
Rdbcompression:对于存储到磁盘中的快照,是否采用lzf算法进行压缩,压缩消费cpu但节省空间。使用原则:一般能用钱解决的事情不要去消耗性能,所以建议设置成no。
Rdbchecksum:存储快照后是否采用crc64算法进行数据校验,但这样会增加大约10%的性能消耗,建议关闭此功能。
AOF:
rdb存在的前提下为何要存在aof,以此思考rdb劣势。
aof以日志的形式来记录每一个写操作,将redis执行过的所有写指令记录下来,只许追加文件,但不可改写文件。Redis启动之初会读取该文件重新构建数据。
查看配置文件中APPEND ONLY MODE:
此配置默认是关appendonly no
现在改成yes:
既然appendonly.aof记录的是执行命令,那服务器突然断电的时候,很可能记录的命令不完整。那么再启动时是否可以启动成功?没有启动成功。
而且将dump_bak.rdb恢复成dum.rdb再次启动还没有成功,说明当rdb与aof同时存在使用时,优先使用加载aof。
运行redis-check-aof:
再次启动成功。
redis-check-aof:解决网络闪断、延时、病毒、丢包等引起的aof不完整问题。具体用法请百度。
aof相关策略:
Always:同步持久化,每次发生数据变更立刻记录到磁盘,性能较差。
Everysec:出厂默认推荐,异步操作,每秒记录,会丢失一秒内数据。
No:不进行
总结异常恢复步骤:
1、启动:设置yes
2、 备份被写坏的aof文件
3、修复:redis-check-aof–fix进行修复
4、恢复:重启redis然后重新加载
Rewrite:aof采用文件追加方式,文件会越来越大,为避免此情况,新增了重写机制,当aof文件的大小超过所设定的阀值时,redis会启动aof内容压缩,只保留可以恢复数据的最小指令集,可以使用命令bgrewriteaof
触发机制:
# Specify a
percentage of zero in order to disable the automatic AOF
# rewrite feature.
auto-aof-rewrite-percentage100 重写的基准值
auto-aof-rewrite-min-size 64mb重写的基准值
redis会记录上次文件重写时的aof大小,默认配置是当aof文件大小是上次rewrite后大小的一倍且文件大于64M时触发。但是高速发展的互联网公司里3G是起步配置,重写策略一定要配。
aof优势:略
劣势:相同数据集数据aof文件远大于rdb,恢复速度慢;aof运行效率慢于rdb。
客户端--命令à服务器—网络协议命令内容àaof文件
总结:
官网建议如果对数据敏感和完整性要求不高使用rdb。只做缓存的话可以不使用任何持久化方式。也可以同时开启两种持久化建议。
性能建议:
1、因为rgb文件只用作后备用途,建议在slave上持久化rdb文件,而且只要15分钟备份一次就够了,只保留save 900 1这一条规则即可。
2、如果开了AOF,好处是在最恶劣的情况下也只会丢失不超过两秒的数据,启动脚本简单。但代价一是带来的持续的IO,二是AOF REWRITE的最后将rewrite过程中产生的新数据写到文件造成的阻塞不可避免,只要硬盘许可,应尽量减少AOF rewrite的频率,同时默认的64M太小,可以设置到3或5G以上,默认超过原大小100%时重写可以改到适当的数据。
3、如果没有开启AOF,仅靠Master-Slave Replicaiton实现可用也是好的方案,这样能省掉大笔IO开销和rewrite时带来的波动。M-S同时倒掉会丢失世纪分钟数据而已。
三、Redis事务:可以一次执行多个命令,本质是一组命令的集合。
五大案例分类:正常执行、放弃事务、全体连坐、冤头债主、watch监控
相关命令:discard\exec\multi\unwatch\watch key[key…],根据英文意思推测命令含义
正常执行:
放弃事务:
全体连坐:
冤头债主:
大家比较全体连坐与冤头债主,命令都一样,为何结果不一样?是否类似java中的编译一场与运行时异常?得出结论:redis对事物的支持是部分支持,不保证原子性。
WATCH监控:
悲观锁/乐观锁/CAS(check and set)
举例:初始化信用卡可用余额(100000)和欠款(1000)
以上例子保证两笔金额在一个事物内。但中间有篡改的情况下:
篡改后失败了。
watch指令类似乐观锁,事务提交时,如果key的值已经被别的客户端改变,整个事务队列都不会被执行。通过watch命令在事务执行前监控多个key,倘若watch之后有任何key的改变,事务将不被执行。
事务的执行过程:开启-〉入队-〉执行。
四、Redis消息订阅发布机制:
进程间的一种消息通信模式:发送者pub发送消息,订阅者sub接收消息。
下图展示了频道 channel1 ,以及订阅这个频道的三个客户端 ——client2 、 client5 和 client1 之间的关系:
当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:
发布命令:publish <管道> <内容>
订阅命令:subscribe<管道>
先订阅后发布才能收到消息,可以一次订阅多个,比如subscibe c1 c2 c3
通配符形式定义多个用*:psubscribe<*>
示例:
企业中一般不会使用redis的消息订阅机制,所以这里不做详讲。
五、Redis主从复制
配置:slaveof 主库IP 主库端口
可通过以上命令进行配置,但每次与master端口后都需要重新连接,如果写到redis.conf文件里则不需要断开后重新手工连接。
常用三招:一主二仆、薪火相传、反客为主、哨兵模式
一主二仆示例:
1、模拟3台redis环境,redis主库使用原6379端口,两台从库分别使用6380、6381端口
2、一主二仆,一台主机,两天备机。启动三台机器并使用info replication查看信息
主机set一些key
在6380和6381上设置从库,使用slave 主库ip 主库端口
slaveof 127.0.0.1 6379
只要设置好从机,从机会第一次启动会将主机中所有的key都同步过来。
再使用info replication看6379:
role:master
connected_slave:2
slave0:ip=127.0.0.1,port=6380,state=online
slave2:ip=127.0.0.1,port=6381,state=online
模拟异常:
3、如果在master和slave上都执行set操作会怎样?
6379:set k1 1
6380: set k1 11
6381: set k1 111
6380和6381上无法set:readonly
you cant’t write against a read only slave.
即只有主机才可以写。
4、shutdown主机,从机会怎样?
将6379shutdown,info replication6380和6381:
6380与6381都仍然是slave,master_link_status=down
重启6379后一切恢复。
结论:6380与6379在主机关闭后会原地待命
5、shutdown从机会怎样?
将6380shutdown,然后再启动后用info replication6380:
Role:master
在6379 set k1 t1后,6380 get k1没有任何值
结论:从机配置如没有配置进redis.conf中,从机每次重启都需要重新配置,否则就是一个独立的进程应用。
如果master下的slave越来越多,master会越来越累,所以根据业务场景如果需要可去中心化,这就需要薪火相传。
薪火相传示例:
上一个slave可以是下一个slave的master,slave同样可以接收其它salve的连接和同步请求,那么该slave作为了链条中下一个的master。
slaveof 新主库ip 新主库端口
即:6379(m)->6380(s-m)->6381(s)
在6379:set k1 t1
在6380:slaveof 127.0.0.1 6379
在6381:slaveof 127.0.0.1 6380
中途变更转向:会清除之前的数据,重新建立拷贝最新的。
如果主机挂了,怎么办,这就需要此情况下反客为主。
slaveof no one使当前数据库停止与其它数据库的同步,转成主库。
反客为主示例:
6379:shutdown
6380:slaveof 127.0.0.1 6379
6380:slaveof no one
6381:slaveof 127.0.0.1 6379
再次启动6379,则6379是独立的体系了。
自动化反客为主:哨兵模式
当master挂掉后,从slave中投票选择出新master
1、首先设置成一主二仆
6380:slaveof 127.0.0.1 6379
6381:slaveof127.0.0.1 6379
2、6379、6380、6381下建立sentinel.conf文件
sentinel monitor host6379 127.0.0.1 6379 1
以上配置中最后一个数字1表示主机挂掉后slave投票看让谁接替成为主机。
3、6379:shutdown
4、稍候6380:info replicaiton查看6380信息
role:master,已经说明6380被选举成为了主机。
5、当6379又重启后info replicaiton查看6379信息
role:slave,已经说明6379重启回来后只能做从机了。