做者:王爷科技java
https://www.toutiao.com/i6713520017595433485redis
Redis 简介 & 优点算法
Redis 数据类型数据库
发布订阅缓存
订阅者的客户端显示以下安全
事务服务器
持久化网络
复制数据结构
哨兵app
分片
Redis 是彻底开源免费的,遵照 BSD 协议,是一个高性能的 key - value 数据库
Redis 与 其余 key - value 缓存产品有如下三个特色:
Redis 支持数据持久化,能够将内存中的数据保存在磁盘中,重启的时候能够再次加载进行使用。
Redis 不只仅支持简单的 key - value 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储
Redis 支持数据的备份,即 master - slave 模式的数据备份
性能极高 – Redis 读的速度是 110000 次 /s, 写的速度是 81000 次 /s 。
丰富的数据类型 - Redis 支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操做。
原子性 - Redis 的全部操做都是原子性的,意思就是要么成功执行要么失败彻底不执行。单个操做是原子性的。多个操做也支持事务,即原子性,经过 MULTI 和 EXEC 指令包起来。
其余特性 - Redis 还支持 publish/subscribe 通知,key 过时等特性。
Redis 支持 5 中数据类型:string(字符串),hash(哈希),list(列表),set(集合),zset(sorted set:有序集合)
string
string 是 redis 最基本的数据类型。一个 key 对应一个 value。
string 是二进制安全的。也就是说 redis 的 string 能够包含任何数据。好比 jpg 图片或者序列化的对象。
string 类型是 redis 最基本的数据类型,string 类型的值最大能存储 512 MB。
理解:string 就像是 java 中的 map 同样,一个 key 对应一个 value
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> get hello
"world"
hash
Redis hash 是一个键值对(key - value)集合。
Redis hash 是一个 string 类型的 key 和 value 的映射表,hash 特别适合用于存储对象。
理解:能够将 hash 当作一个 key - value 的集合。也能够将其想成一个 hash 对应着多个 string。
与 string 区别:string 是 一个 key - value 键值对,而 hash 是多个 key - value 键值对。
// hash-key 能够当作是一个键值对集合的名字,在这里分别为其添加了 sub-key1 : value一、
sub-key2 : value二、sub-key3 : value3 这三个键值对
127.0.0.1:6379> hset hash-key sub-key1 value1
(integer) 1
127.0.0.1:6379> hset hash-key sub-key2 value2
(integer) 1
127.0.0.1:6379> hset hash-key sub-key3 value3
(integer) 1
// 获取 hash-key 这个 hash 里面的全部键值对
127.0.0.1:6379> hgetall hash-key
1) "sub-key1"
2) "value1"
3) "sub-key2"
4) "value2"
5) "sub-key3"
6) "value3"
// 删除 hash-key 这个 hash 里面的 sub-key2 键值对
127.0.0.1:6379> hdel hash-key sub-key2
(integer) 1
127.0.0.1:6379> hget hash-key sub-key2
(nil)
127.0.0.1:6379> hget hash-key sub-key1
"value1"
127.0.0.1:6379> hgetall hash-key
1) "sub-key1"
2) "value1"
3) "sub-key3"
4) "value3"
list
Redis 列表是简单的字符串列表,按照插入顺序排序。咱们能够网列表的左边或者右边添加元素。
127.0.0.1:6379> rpush list-key v1
(integer) 1
127.0.0.1:6379> rpush list-key v2
(integer) 2
127.0.0.1:6379> rpush list-key v1
(integer) 3
127.0.0.1:6379> lrange list-key 0 -1
1) "v1"
2) "v2"
3) "v1"
127.0.0.1:6379> lindex list-key 1
"v2"
127.0.0.1:6379> lpop list
(nil)
127.0.0.1:6379> lpop list-key
"v1"
127.0.0.1:6379> lrange list-key 0 -1
1) "v2"
2) "v1"
咱们能够看出 list 就是一个简单的字符串集合,和 Java 中的 list 相差不大,区别就是这里的 list 存放的是字符串。list 内的元素是可重复的。
set
redis 的 set 是字符串类型的无序集合。集合是经过哈希表实现的,所以添加、删除、查找的复杂度都是 O(1)
127.0.0.1:6379> sadd k1 v1
(integer) 1
127.0.0.1:6379> sadd k1 v2
(integer) 1
127.0.0.1:6379> sadd k1 v3
(integer) 1
127.0.0.1:6379> sadd k1 v1
(integer) 0
127.0.0.1:6379> smembers k1
1) "v3"
2) "v2"
3) "v1"
127.0.0.1:6379>
127.0.0.1:6379> sismember k1 k4
(integer) 0
127.0.0.1:6379> sismember k1 v1
(integer) 1
127.0.0.1:6379> srem k1 v2
(integer) 1
127.0.0.1:6379> srem k1 v2
(integer) 0
127.0.0.1:6379> smembers k1
1) "v3"
2) "v1"
redis 的 set 与 java 中的 set 仍是有点区别的。
redis 的 set 是一个 key 对应着 多个字符串类型的 value,也是一个字符串类型的集合
可是和 redis 的 list 不一样的是 set 中的字符串集合元素不能重复,可是 list 能够。
Zset
redis zset 和 set 同样都是 字符串类型元素的集合,而且集合内的元素不能重复。
不一样的是,zset 每一个元素都会关联一个 double 类型的分数。redis 经过分数来为集合中的成员进行从小到大的排序。
zset 的元素是惟一的,可是分数(score)却能够重复。
127.0.0.1:6379> zadd zset-key 728 member1
(integer) 1
127.0.0.1:6379> zadd zset-key 982 member0
(integer) 1
127.0.0.1:6379> zadd zset-key 982 member0
(integer) 0
127.0.0.1:6379> zrange zset-key 0 -1 withscores
1) "member1"
2) "728"
3) "member0"
4) "982"
127.0.0.1:6379> zrangebyscore zset-key 0 800 withscores
1) "member1"
2) "728"
127.0.0.1:6379> zrem zset-key member1
(integer) 1
127.0.0.1:6379> zrem zset-key member1
(integer) 0
127.0.0.1:6379> zrange zset-key 0 -1 withscores
1) "member0"
2) "982"
zset 是按照分数的大小来排序的。
通常不用 Redis 作消息发布订阅。
简介
Redis 发布订阅 (pub/sub) 是一种消息通讯模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。
Redis 客户端能够订阅任意数量的频道。
下图展现了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:
学Redis这篇就够了
当有新消息经过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:
学Redis这篇就够了
实例
如下实例演示了发布订阅是如何工做的。在咱们实例中咱们建立了订阅频道名为 redisChat:
127.0.0.1:6379> SUBsCRIBE redisChat
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisChat"
如今,咱们先从新开启个 redis 客户端,而后在同一个频道 redisChat 发布两次消息,订阅者就能接收到消息。
127.0.0.1:6379> PUBLISH redisChat "send message"
(integer) 1
127.0.0.1:6379> PUBLISH redisChat "hello world"
(integer) 1
# 订阅者的客户端显示以下
1) "message"
2) "redisChat"
3) "send message"
1) "message"
2) "redisChat"
3) "hello world"
发布订阅经常使用命令
自行查阅
redis 事务一次能够执行多条命令,服务器在执行命令期间,不会去执行其余客户端的命令请求。
事务中的多条命令被一次性发送给服务器,而不是一条一条地发送,这种方式被称为流水线,它能够减小客户端与服务器之间的网络通讯次数从而提高性能。
Redis 最简单的事务实现方式是使用 MULTI 和 EXEC 命令将事务操做包围起来。
批量操做在发送 EXEC 命令前被放入队列缓存。
收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其他命令依然被执行。也就是说 Redis 事务不保证原子性。
在事务执行过程当中,其余客户端提交的命令请求不会插入到事务执行命令序列中。
一个事务从开始到执行会经历如下三个阶段:
开始事务。
命令入队。
执行事务。
实例
如下是一个事务的例子, 它先以 MULTI 开始一个事务, 而后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的全部命令:
redis 127.0.0.1:6379> MULTI
OK
redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days"
QUEUED
redis 127.0.0.1:6379> GET book-name
QUEUED
redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series"
QUEUED
redis 127.0.0.1:6379> SMEMBERS tag
QUEUED
redis 127.0.0.1:6379> EXEC
1) OK
2) "Mastering C++ in 21 days"
3) (integer) 3
4) 1) "Mastering Series"
2) "C++"
3) "Programming"
单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增长任何维持原子性的机制,因此 Redis 事务的执行并非原子性的。
事务能够理解为一个打包的批量执行脚本,但批量指令并不是原子化的操做,中间某条指令的失败不会致使前面已作指令的回滚,也不会形成后续的指令不作。
这是官网上的说明 From redis docs on transactions:
It's important to note that even when a command fails, all the other commands in the queue are processed – Redis will not stop the processing of commands.
好比:
redis 127.0.0.1:7000> multi
OK
redis 127.0.0.1:7000> set a aaa
QUEUED
redis 127.0.0.1:7000> set b bbb
QUEUED
redis 127.0.0.1:7000> set c ccc
QUEUED
redis 127.0.0.1:7000> exec
1) OK
2) OK
3) OK
若是在 set b bbb 处失败,set a 已成功不会回滚,set c 还会继续执行。
Redis 事务命令
下表列出了 redis 事务的相关命令:
序号命令及描述:
1DISCARD 取消事务,放弃执行事务块内的全部命令。
2EXEC 执行全部事务块内的命令。
3MULTI 标记一个事务块的开始。
4UNWATCH 取消 WATCH 命令对全部 key 的监视。
5WATCH key [key …]监视一个 (或多个) key ,若是在事务执行以前这个 (或这些) key 被其余命令所改动,那么事务将被打断。
Redis 是内存型数据库,为了保证数据在断电后不会丢失,须要将内存中的数据持久化到硬盘上。
RDB 持久化
将某个时间点的全部数据都存放到硬盘上。
能够将快照复制到其余服务器从而建立具备相同数据的服务器副本。
若是系统发生故障,将会丢失最后一次建立快照以后的数据。
若是数据量大,保存快照的时间会很长。
AOF 持久化
将写命令添加到 AOF 文件(append only file)末尾。
使用 AOF 持久化须要设置同步选项,从而确保写命令同步到磁盘文件上的时机。
这是由于对文件进行写入并不会立刻将内容同步到磁盘上,而是先存储到缓冲区,而后由操做系统决定何时同步到磁盘。
选项同步频率always每一个写命令都同步eyerysec每秒同步一次no让操做系统来决定什么时候同步
always 选项会严重减低服务器的性能
everysec 选项比较合适,能够保证系统崩溃时只会丢失一秒左右的数据,而且 Redis 每秒执行一次同步对服务器几乎没有任何影响。
no 选项并不能给服务器性能带来多大的提高,并且会增长系统崩溃时数据丢失的数量。
随着服务器写请求的增多,AOF 文件会愈来愈大。Redis 提供了一种将 AOF 重写的特性,可以去除 AOF 文件中的冗余写命令。
经过使用 slaveof host port 命令来让一个服务器成为另外一个服务器的从服务器。
一个从服务器只能有一个主服务器,而且不支持主主复制。
链接过程
主服务器建立快照文件,即 RDB 文件,发送给从服务器,并在发送期间使用缓冲区记录执行的写命令。
快照文件发送完毕以后,开始像从服务器发送存储在缓冲区的写命令。
从服务器丢弃全部旧数据,载入主服务器发来的快照文件,以后从服务器开始接受主服务器发来的写命令。
主服务器每执行一次写命令,就向从服务器发送相同的写命令。
主从链
随着负载不断上升,主服务器没法很快的更新全部从服务器,或者从新链接和从新同步从服务器将致使系统超载。
为了解决这个问题,能够建立一个中间层来分担主服务器的复制工做。中间层的服务器是最上层服务器的从服务器,又是最下层服务器的主服务器。
Sentinel(哨兵)能够监听集群中的服务器,并在主服务器进入下线状态时,自动从从服务器中选举处新的主服务器。
分片是将数据划分为多个部分的方法,能够将数据存储到多台机器里面,这种方法在解决某些问题时能够得到线性级别的性能提高。
假设有 4 个 Redis 实例 R0, R1, R2, R3, 还有不少表示用户的键 user:1, user:2, … , 有不一样的方式来选择一个指定的键存储在哪一个实例中。
最简单的是范围分片,例如用户 id 从 0 ~ 1000 的存储到实例 R0 中,用户 id 从 1001 ~ 2000 的存储到实例 R1中,等等。可是这样须要维护一张映射范围表,维护操做代价高。
还有一种是哈希分片。使用 CRC32 哈希函数将键转换为一个数字,再对实例数量求模就能知道存储的实例。
根据执行分片的位置,能够分为三种分片方式:
客户端分片:客户端使用一致性哈希等算法决定应当分布到哪一个节点。
代理分片:将客户端的请求发送到代理上,由代理转发到正确的节点上。
服务器分片:Redis Cluster。