文章目录
1.NoSQL
缓存数据库(非关系型数据库),减少io的读操作。
打破了传统关系型数据库以业务逻辑为依据的存储模式,而针对不同数据结构类型改为以性能为最优先的存储方式。
1.1 NoSQL概述
- NoSQL,即“不仅仅是SQL”,泛指非关系型数据库
- NoSQL不依赖业务逻辑方式存储,而以简单的key-value模式存储。因此大大的增加了数据库的扩展能力
- 不遵循SQL标准
- 不支持ACID
- 远超于SQL的性能(关系型数据库将数据存储在磁盘,而非关系型数据库将数据存储在内存中)
1.2 场景
适用场景
- 对数据高并发读写
- 海量数据读写
- 对数据高可扩展性的
不适用场景
- 需要事务支持
- 基于SQL的结构化查询存储,处理复杂的关系,需要条件查询
1.3 常用NoSQL数据库
- Memcached
- Redis
- MongoDB
2. Redis(REmote DIctionary Server,远程字典服务器)
Redis是一个开源的key-value存储系统。与Memcached类似,它支持存储的value类型相对更多,包括String、list、set、zset(sorted set——有序集合)和hash类型。这些数据类型都支持push/pop、add/remove以及交集并集和差集及更丰富的操作,且这些操作都是原子性的。在此基础上,Redis支持各种不同方式的排序。与Memcached一样,为了保证效率,数据都是缓存在内存中。区别是Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
2.1 Redis与其他key-value缓存数据库区别
- 支持数据持久化,可以将内存中的数据保持在磁盘中,重启时可以再次加载进行使用
- Redis不仅仅支持简单的key-value类型的数据,同时还提供list、set、zset、hash等数据结构的存储
- Redis支持数据的备份,即master-slave模式的数据备份
2.2 Redis应用
- 内存存储和持久化
- 取最新N个数据的操作,如:可以将最新的10条评论的ID放在Redis的List集合中
- 模拟类似于HttpSession这种需要设定过期时间的功能
- 发布、订阅消息系统
- 定时器、计数器
2.3 Redis应用场景
- 配合关系型数据库做高速缓存
- 高频词,热门访问的数据,降低数据库IO
- 分布式架构,做session共享
- 由于其持久化能力,利用其多样的数据结构存储特定的数据
2.4 Redis 单线程+多路IO复用技术
多路复用是指使用一个线程来检查多个文件描述符(Socket)的就绪状态,比如调用select和poll函数,传入多个文件描述符,如果有一个文件描述符就绪,则返回,否则阻塞直到超时。得到就绪状态后进行真正的操作可以在同一个线程里执行,也可以启动线程执行(比如使用线程池)
3.Redis数据类型
注意,Redis数据类型指的是key-value中value的数据类型
key 关键字
常用命令
- keys * :查询当前库的所有键
- exists key的名字,判断某个key是否存在
- move key db —>当前库就没有了,被移除了
- expire key 秒钟:为给定的key设置过期时间
- ttl key 查看还有多少秒过期,-1表示永不过期,-2表示已过期
- type key :查看你的key是什么类型
- dbsize :查看当前数据库的key的数量
- flushdb :清空当前库
命令 | 描述 |
---|---|
DEL key | 该命令用于在 key 存在时删除 key。 |
DUMP key | 序列化给定 key ,并返回被序列化的值。 |
EXISTS key | 检查给定 key 是否存在。 |
EXPIRE key seconds | 为给定 key 设置过期时间,以秒计。 |
EXPIREAT key timestamp | EXPIREAT 的作用和 EXPIRE 类似,都用于为 key 设置过期时间。 不同在于 EXPIREAT 命令接受的时间参数是 UNIX 时间戳(unix timestamp)。 |
PEXPIRE key milliseconds | 设置 key 的过期时间以毫秒计。 |
PEXPIREAT key milliseconds-timestamp | 设置 key 过期时间的时间戳(unix timestamp) 以毫秒计 |
KEYS pattern | 查找所有符合给定模式( pattern)的 key 。 |
MOVE key db | 将当前数据库的 key 移动到给定的数据库 db 当中。 |
PERSIST key | 移除 key 的过期时间,key 将持久保持。 |
PTTL key | 以毫秒为单位返回 key 的剩余的过期时间。 |
TTL key | 以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)。 |
RANDOMKEY | 从当前数据库中随机返回一个 key 。 |
RENAME key newkey | 修改 key 的名称 |
RENAMENX key newkey | 仅当 newkey 不存在时,将 key 改名为 newkey 。 |
SCAN cursor [MATCH pattern] [COUNT count] | 迭代数据库中的数据库键。 |
TYPE key | 返回 key 所储存的值的类型。 |
3.1 String
字符串特点
- string是redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value。
- string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
- string类型是Redis最基本的数据类型,一个redis中字符串value最多可以是512M
String应用场景
- 因为String是二进制安全的,所以可以把保密要求高的图片文件内容作为字符串来存储
- 计数器:常规Key-Value缓存应用,如计算站点访问量、当前在线人数等,以及微博数、粉丝数。INCR本身就具有原子性特性,所以不会有线程安全问题。
操作字符串的指令
get key
set key value
append key value
strlen key
setnx key value:只有在key不存在时,才设置key的值
incr key:将key中存储的数字值增1;只能对数字值操作,若为空,新增值为1
decr key
incrby/decrby key 步长:将key中存储的数字值增减自定义的步长量
getrange key start stop获取key值的范围,类似substring
setrange key start value从start位置开始用value覆写key所存储的字符串值
setex key 过期时间 value :设置键值的同时,设计过期时间,单位为秒
getset key value:以旧换新,设置新值的同时获得旧值
原子性:
不会被线程调度机制打断的操作。
Redis单命令的原子性主要得益于Redis的单线程.
(1)在单线程中,能够在单条指令中完成的操作都可以认为是“原子操作”,因为中断只能发生于指令之间;
(2)在多线程中,不能被其它进程(线程)打断的操作就叫原子操作。
3.2 List
List特点
- 单键多值
- Redis列表是简单的字符串列表,按照插入顺序排序,可以添加一个元素到列表的头部或者尾部
- 它底层实际是个双向链表,对两端的操作性能很高,通过索引下标操作中间的节点性能会较差。
List应用场景
- (1)最新消息排行榜,或最新N个数据(通过List实现按自然时间排序的数据)
- (2)消息队列,以完成多程序之间的消息交换。可以用push操作将任务存在list中(生产者),然后线程在用pop操作将任务取出进行执行。(消费者)
- (3)rpoplpush list1 list2 用此命令可以实现订单下单流程、用户系统登录注册短信等。
操作List的指令
lpush/rpush key v1 v2 v3.. :从左边/右边插入一个或多个值
lpop/rpop key:从左边或右边弹出一个值;值在键在,值光键亡
rpoplpush key1 key2:从key1的右边弹出一个值,并插入到key2的左边
lrange key start stop:按照索引下标获得元素(从头到尾查时start设置为0,stop设置为-1)
lindex key index:按照索引下标获得元素(从左到右)
llen key:获得列表长度
linsert key before value newvalue:在key的value前面插入newvalue
lrem key n value:从左边删除n个value(n为正表示从左到右,n为负表示从右往左,n为0表示删除全部)
3.3 Set
Set特点
- 单键多值,不可重复
- set提供了判断某个成员是否在一个set集合内的重要接口(可以用于秒杀时判断某用户是否已经秒杀成功过)
- Redis的Set是String类型的无序集合,它底层是一个value为null的hash表,所以添加、删除、查找的复杂度都是O(1)
Set应用场景
- 去除大量数据中的重复数据
- 在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis还提供了诸如collection、union和differences等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能。对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存储到一个新的集合中。
- 利用唯一性,可以统计访问网站的所有独立 IP
操作Set的指令
sadd key v1 v2...
smembers key:取出键为key对应的集合的所有值
sismember key value:判断key是否包含元素value,有返回1,没有返回0
scard key:集合元素个数
srem key v1 v2:删除集合元素
spop key:随机从集合中吐出一个值(**可以用于抽奖**);该元素从集合中删除
srandmember key n:随机从该集合中取出n个值;不会从元素中删除
sinter key1 key2:返回两集合交集
sunion key1 key2:返回两集合并集
sdiff key1 key2:返回两集合差集;集合有顺序之分
3.4 hash
hash特点
- 键值对集合
- String类型的filed和value的映射表,适合存储对象
- 类似Java中的Map<String,String>
hash应用场景
- 存储部分更改数据,如用户信息、会话共享。
操作hash指令
hset key field value:给key集合中的field键赋值value
hget key field:从key集合中取出field的值value
hmset key1 field1 value1 field2 value12...:批量设置hash值
hexists key field:查看哈希表key中,给定域field是否存在
hkeys key:列出该hash集合的所有field
hvals key:列出该hash集合所有value
hgetall key:列出该集合所有field和value
3.5 zset(sorted set)
zset特点
- 有序集合,无重复元素,与set相似,不同之处在于有序集合的每个成员都关联了一个评分(score),这个评分被用来按照从最低分到最高分的方式排序集合中的成员,集合成员唯一,但评分可重复。
- 元素有序,因此可以快速根据评分(score)或者次序(position)来获取一个范围的元素。访问有序集合中间元素效率页很高,因此可以使用有序集合作为一个没有重复成员的智能列表。
zset应用场景
- 主要用于排行榜,根据得分列出topN的用户
- 存储成绩,其集合value可以是同学的学号,而score就可以是成绩。
- 实现文章访问量的排序
- 推特可以以发表时间作为score来存储
- 用zset来做带权重的队列,让重要的任务先执行
操作zset指令
zadd key score1 value1 score2 value2...:给key批量增加元素
zrange key start stop (WITHSCORES):按从小到大返回有序集key中,下标在start和stop之间的value(;带WITHSCORES,可让分数一起和值返回
zrevrange key start stop (WITHSCORES):按分数从大到小输出有序集key范围元素
zrangebyscore key min max (WITHSCORES):返回有序集key中,所有score值介于min和max之间(包括等于min或max)的成员value
zrevrangebyscore key max min(WITHSCORES):改为从大到小排列
zrank key value:返回value在key集中的排名,从0开始
4.Redis事务
4.1 Redis事务概述
- Redis事务是一个单独的隔离操作:事务中的所有操作都会序列化、按顺序的执行。事务在执行过程中,不会被其他客户端发送来的命令请求所打断。
- Redis事务的主要作用就是串联多个命令防止别的命令插队。
4.2 Redis事务实现
从输入Multi命令开始,输入的指令会依次进入命令队列中,但不会执行,直到输入Exec指令后,Redis会将之前的命令队列中的命令依次执行。
组队的过程中,可以通过discard来放弃组队
4.3 事务的错误处理
- 组队中某个命令如果出现了报告错误,执行时整个的命令队列都会被取消。
- 如果执行阶段某个命令报错,则只有报错的命令不会被执行,而其他命令都会正常执行,不会回滚。
4.4 Redis实现与乐观锁
悲观锁,每次拿数据时都认为别人会修改,所以每次在拿数据时会上锁,这样比尔想拿这个数据就会block直到它拿到锁。传统的关系型数据库就用到了很多这种锁机制,比如行锁,表锁,读锁,写锁等,都是在做操作之前先上锁。
乐观锁,每次拿数据时都认为别人不会修改,所以不会上锁,但是在更新时会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的。
4.5 WATCH key1 [key2…]
在执行multi之前,先执行watch key1 [key2…]可以监视一个或多个key,若在事务执行之前这个(或这些)key被其他命令所改动,那么事务将被打断,队列中的所有命令取消,不再执行。
unwatch 取消watch命令对所有key的监视(若exec或discard命令已经执行,就不需要再执行unwatch)
4.6 Redis事务的三特性
- 单独的隔离操作
事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。 - 没有隔离级别的概念
队列中的命令在没有提交之前都不会实际的被执行,因此也不存在“事务内的查询要看到事务里的更新,在事务外查询不能看到”的问题。 - 不保证原子性
Redis同一事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚。
4.7Redis事务——秒杀案例
ab工具模拟并发
ab -n 请求数 -c 并发数 -p 指定请求数据文件 -T “application/x-www-form-unlencoded” 测试的请求
超卖问题
超时连接
链接池
- 节省每次Redis连接带来的消耗,把连接好的实例反复利用
- 通过参数管理连接行为
库存遗留问题
5.Redis持久化——RDB、AOF
5.1 RDB(Redis database)
- 在指定时间间隔内将内存中的数据集快照写入磁盘,也就是Snapshot快照,它恢复时是将快照文件直接读到内存中。
- 备份如何执行?
- Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能,如果需要大规模数据的恢复,且对于数据恢复的完整性要求不是非常敏感,则RDB方式要比AOF方式更加高效。RDB缺点是最后一次持久化后的数据可能会丢失。
- rdb保存策略
- 满足保存条件
- Redis正常关闭
- RDB优点
- 节省磁盘空间
- 恢复速度快
- RDB缺点
- 虽然Redis在fork时使用了写时复制技术,但是如果数据庞大时还是比较消耗性能
- 在备份周期做一次备份,所以如果Redis意外宕机的话,就会丢失最后一次快照后的所有修改
5.2 AOF(append of file)
- AOF以日志形式记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,Redis启动之初会读取该文件重新构建数据,换言之,Redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
- AOF文件故障备份
- AOF的备份机制和性能虽然和RDB不同,但是备份和恢复的操作同RDB一样,都是拷贝备份文件,需要恢复时再拷贝到Redis工作目录下,启动系统即加载。
- Rewrite
- AOF采用文件追加方式,文件会越来越大,为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集,可以使用命令bgrewriteaof。
- AOF优点
- 备份机制更稳健,丢失数据概率更低
- 可读的日志文件,通过操作AOF文件,可以处理误操作
- AOF缺点
- 比起RDB占用更多磁盘空间
- 恢复备份速度更慢
- 每次读写都同步的话,有一定的性能压力
- 存在个别Bug,造成恢复不能
AOF和RDB同时开启时,Redis听AOF的
5.3 RDB与AOF选择
- 官方推荐两个都用
- 如果对数据不敏感,可以单独选用RDB
- 不建议单独使用AOF,因为可能会出现bug
- 如果只是做纯内存缓存,可以两个都不用
6. Redis主从复制
6.1 主从复制
主机数据更新后根据配置和策略,自动同步到备机的master/slave机制,master以写为主,slave以读为主
用途:
- 读写分离,性能扩展
- 容灾快速恢复
主从复制配置策略
配从(服务器)不配主(服务器)
常用命令
命令 | 作用 |
---|---|
slaveof 主库ip 主库端口 | 配置从库 |
info replication | 查看redis主从复制的相关信息 |
slaveof no one | 使当前服务器停止与其他服务器的同步,转成主服务器 |
sentinel monitor 被监控数据库名字(自己起名字) 127.0.0.1 6379 n | 配置哨兵,监视master |
redis-sentinel /myredis/sentinel.conf | 以哨兵模式启动redis |
6.2 主从复制模式
6.2.1 一主二仆模式
- 从服务器始终要与主服务器保持一致
- 主机宕机后,从机原地待命,等着主服务器重新上线
- 主服务器可读可写,但一般只用于写;从服务器只能读
复制原理
- 每次从机连通后,都会给主机发送sync指令;
- 主机立刻进行存盘操作,发送RDB文件给从机;
- 从机收到RDB文件后,进行全盘加载;
- 之后每次主机的写操作,都会立刻发送给从机,从机执行相同的指令。
6.2.2 薪火相传
- 上一个slave可以是下一个slave的master,slave同样可以接收其他slaves的连接和同步请求,那么该slave作为链条中下一个的master,可以有效减轻master的写压力,去中心化降低风险;
- 中途变更转向:会清除之前的数据,重新建立拷贝最新的
- 风险:一旦某个slave宕机,后面的slave都没法备份
6.2.3 反客为主
当一个master宕机后,后面的slave可以立刻升为master,其后面的slave不用做任何修改
- 用slaveof no one 将从机变为主机
6.2.4 哨兵模式(sentinel)
反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。(投票机制或留言机制)
配置哨兵
- 调整为一主二仆模式
- 自定义的/myredis目录下新建sentinel.conf文件
- 在配置文件中填写内容 sentinel monitor mymaster 127.0.0.1 6379 n
- 其中,mymaster为监控对象起的别名
- 127.0.0.1 6379为监控对象地址和端口号
- n表示主服务器迁移至少要有n个哨兵同意迁移
启动哨兵
- 执行redis-sentinel /myredis/sentinel.conf
哨兵模式故障恢复
7. Redis集群
问题引入:
- 容量不够,Redis如何进行扩容?
- 并发写操作,Redis如何分摊?
7.1 Redis集群
- Redis集群实现了对Redis的水平扩容,即启动N个Redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的1/N。
- Redis集群通过分区来提供一定程度的可用性:即使集群中有一部分节点失效或者无法进行通讯,集群也可以继续处理命令请求。
节点
-
一个集群至少要有三个主节点。
-
分配原则尽量保证每个主数据库运行在不同的ip地址,每个从库和主库不在一个ip地址。
-
当主节点崩了,从节点能自动升为主节点;当主节点再次恢复时,主节点变为slave(参考哨兵模式)。
SLOTS
-
一个Redis 集群包含16384个插槽(hash slot), 数据库中的每个键都属于这16384个插槽的其中一个。
-
集群中的每个节点负责处理一部分插槽。 举个例子, 如果一个集群可以有主节点。其中:
- 节点A负责处理0号至5500号插槽
- 节点B负责处理5501号至11000号插槽
- 节点C负责处理11001号至16383号插槽
(注意:每个节点分配的插槽具体数字可能不同,当然可以通过一个小脚本来指定)
集群中录入值
-
redis-cli客户端提供-c参数实现自动重定向
redis-cli -c -p 6379
-
不在一个slot下的键值,是不能使用mget,mset等多键操作
-
可以通过{}来定义
组的概念
,从而使key中{}内相同内容的键值对放到一个slot中去。
集群故障恢复
- 当主节点下线,从节点能自动升为主节点;当主节点再次恢复时,主节点变为slave(参考哨兵模式)
- 若某一段插槽中的主从节点都下线,该段插槽不可用,Redis集群不能再对外提供服务(只有16384个插槽都正常时才能对外提供服务)
- redis.conf有个参数cluster-require-full-coverage,默认情况下,集群全部的slot有节点负责,集群状态才为ok,才能提供服务。设置为no,可以在slot没有全部分配的时候提供服务(不建议打开该配置)
Redis集群优缺点
优点:
- 实现扩容
- 分摊压力
- 无中心配置,相对简单
缺点:
- 不支持多键操作
- 不支持Redis事务,lua脚本不支持