Redis Cluter

Posted _枝桠。

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis Cluter相关的知识,希望对你有一定的参考价值。

Redis简介

Redis是一款开源的,ANSI C语言编写的,高级键值(key-value)缓存和支持永久存储NoSQL数据库产品。
Redis采用内存(In-Memory)数据集(DataSet) 。
支持多种数据类型。
运行于大多数POSIX系统,如Linux、*BSD、OS X等。
作者: Salvatore Sanfilippo

Redis.io 官网
Download/redis.io 下载
Redisdoc.com 文档
redis.cn 中文网

核心特性

  1. 高速读写
  2. 数据类型丰富:常见5种
  3. 支持持久化
  4. 多种内存分配及回收策略
  5. 支持事务-弱事务
  6. 消息队列、消息订阅
  7. 支持高可用
  8. 支持分布式分片集群

和Memcache的区别:支持事务、数据类型丰富、支持高可用、支持分布式分片集群

优点

优势:
    透明性:分布式系统对用户来说是透明的,一个分布式系统在用户面前的表现就像一个传统的单处理机分时系统,可让用户不必了解内部结构就可以使用。
    扩展性:分布式系统的最大特点就是可扩展性,他可以根据需求的增加而扩展,可以通过横向扩展使集群的整体性能得到线性提升,也可以通过纵向扩展单台服务器的性能使服务器集群的性能得到提升。
    可靠性:分布式系统不允许单点失效的问题存在,它的基本思想是:如果一台服务器坏了,其他服务器接替它的工作,具有持续服务的特性
    高性能:高性能是人们设计分布式系统的一个初衷,如果建立了一个透明,灵活,可靠的分布式系统,但他运行起来像蜗牛一样慢,那这个系统就是失败的。
分布式系统的缺点:
      1.在节点通信部分的开销比较大,线程安全也变得复杂,需要保证在完整数据的同时兼顾性能。
      2.过分依赖网络,网络信息的丢失或饱和将会抵消分布式的大部分优势。
      3.有潜在的数据安全和网络安全问题。

企业缓存数据库解决方案对比

Memcached:
    优点:高性能读写、单一数据类型、支持客户端式分布式集群、一致性hash
      多核结构、多线程读写性能高。
    缺点:无持久化、节点故障可能出现缓存穿透、分布式需要客户端实现、跨机房数据同步困难、
      架构扩容复杂度高 Redis: 优点:高性能读写、多数据类型支持、数据持久化、高可用架构、支持自定义虚拟内存、
      支持分布式分片集群、单线程读写性能极高 缺点:多线程读写较Memcached慢 Tair: 优点:高性能读写、支持三种存储引擎(ddb、rdb、ldb)、支持高可用、支持分布式分片集群、
      支撑了几乎所有淘宝业务的缓存。 缺点:单机情况下,读写性能较其他两种产品较慢

对比测试

总结:

对三款产品的单用户和多用户的操作的性能的对比,差异

Memcache是多核的处理模式,可以支持更多的并发能力

Redis支持少量的并发,并且发起多次修改或者读取的能力

在单台主机上装单个redis,并不具备优势!可以在单台机器上装多实例实现更高的并发!!

redis应用场景

数据高速缓存
web会话缓存(Session Cache)
排行榜应用
消息队列
发布订阅
等

Redis安装及基本配置

安装

有两个要求:1、去官网下载;2、下载6-12月的版本

下载:
wget http://download.redis.io/releases/redis-3.2.12.tar.gz
解压: 上传至 /data 或者你喜欢的目录 tar xzf redis-3.2.12.tar.gz mv redis-3.2.12 redis 安装: cd redis make 修改环境变量,将src加入到文件中 vim /etc/profile export PATH=/application/mysql/bin/:/data/redis/src:$PATH
使生效 source
/etc/profile
启动和简易连接: redis
-server & redis-cli

配置文件

手动创建一个目录,创建配置文件
mkdir
/data/6379 vim /data/6379/redis.conf daemonize yes port 6379 logfile /data/6379/redis.log dir /data/6379 dbfilename dump.rdb 127.0.0.1:6379> shutdown 关闭 not connected> exit [root@db01 6379]# redis-server /data/6379/redis.conf 重启,带上配置文件 [root@db03 ~]# netstat -lnp|grep 63 redis-cli shutdown 在命令行关闭 +++++++++++配置文件说明++++++++++++++ redis.conf 是否后台运行: daemonize yes 默认端口: port 6379 日志文件位置 logfile /var/log/redis.log 持久化文件存储位置 dir /data/6379 RDB持久化数据文件: dbfilename dump.rdb +++++++++++++++++++++++++ ------------------------- redis安全配置 (1)redis没有用户概念,redis只有密码 (2)redis默认工作在保护模式下。不允许远程任何用户登录的(protected-mode) protected-mode yes/no (保护模式,是否只允许本地访问,远程可以登录,但是无法操作) ---------------------- DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients. In this mode connections are only accepted from the loopback interface. If you want to connect from external computers to Redis you may adopt one of the following solutions: 1)Just disable protected mode sending the command \'CONFIG SET protected-mode no\' from the loopback interface by connecting to Redis from the same host the server is running, however MAKE SURE Redis is not publicly accessible from internet if you do so. Use CONFIG REWRITE to make this change permanent. 2) Alternatively you can just disable the protected mode by editing the Redis configuration file, and setting the protected mode option to \'no\', and then restarting the server. 3) If you started the server manually just for testing, restart it with the \'--protected-mode no\' option. 4) Setup a bind address or an authentication password. NOTE: You only need to do one of the above things in order for the server to start accepting connections from the outside. ---------------------- (1)Bind :指定IP进行监听 vim /data/6379/redis.conf bind 10.0.0.52 127.0.0.1 (2)增加requirepass {password} vim /data/6379/redis.conf requirepass root 重启生效: redis-cli shutdown 关闭 redis-server /data/6379/redis.conf 带上配置文件启动

[root@db01 ~]# redis-cli -h 10.0.0.52 -a root

----------验证---------
方法一:
[root@db03 ~]# redis-cli -a root
127.0.0.1:6379> set name zhangsan 
OK
127.0.0.1:6379> exit
方法二:
[root@db03 ~]# redis-cli
127.0.0.1:6379> auth root
OK
127.0.0.1:6379> set a b

------------------------
在线查看和修改配置
CONFIG GET *  获取所有配置信息 ,两行是一个参数
在先修改,不用重启redis,下次登录就生效,但也不是所有的参数都能在线修改
CONFIG GET requirepass
CONFIG SET requirepass 123456 
------------------

Redis持久化

RDB 持久化
    可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot)。
    优点:速度快,适合于用做备份,主从复制也是基于RDB持久化功能实现的。
    缺点:会有数据丢失 AOF 持久化 记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。
   AOF 文件中的命令全部以 Redis 协议的格式来保存,新命令会被追加到文件的末尾。
      优点:可以最大程度保证数据不丢
      缺点:日志记录量级比较大

RDB的优点

RDB是一种表示某个即时点的Redis数据的紧凑文件
RDB文件适合用于备份
例如,你可能想要每小时归档最近24小时的RDB文件,每天保存近30天的RDB快照
这允许你很容易的恢复不同版本的数据集以容灾。 RDB非常适合于灾难恢复,作为一个紧凑的单一文件,可以被传输到远程的数据中心。 RDB最大化了Redis的性能,因为Redis父进程持久化时唯一需要做的是启动(fork)一个子进程,
由子进程完成所有剩余工作。父进程实例不需要执行像磁盘IO这样的操作。 RDB在重启保存了大数据集的实例时比AOF要快。

RDB缺点

当你需要在Redis停止工作(例如停电)时最小化数据丢失,RDB可能不太好。
你可以配置不同的保存点(save point)来保存RDB文件(例如,至少5分钟和对数据集100次写之后,
但是你可以有多个保存点)。然而,你通常每隔5分钟或更久创建一个RDB快照,
所以一旦Redis因为任何原因没有正确关闭而停止工作,你就得做好最近几分钟数据丢失的准备了。 RDB需要经常调用fork()子进程来持久化到磁盘。
如果数据集很大的话,fork()比较耗时,
结果就是,当数据集非常大并且CPU性能不够强大的话,Redis会停止服务客户端几毫秒甚至一秒。
AOF也需要fork(),但是你可以调整多久频率重写日志而不会有损(trade
-off)持久性(durability)。

RDB普通配置参数

vim /data/6379/redis.conf
dir /data/6379
dbfilename dump.rdb
save 900 1
save 300 10
save 60 10000

配置分别表示:
900秒(15分钟)内有1个更改
300秒(5分钟)内有10个更改
60秒内有10000个更改

如果不设置上面的条件配置,什么时候使用save命令,什么时候才会持久化

RDB持久化高级配置

stop-writes-on-bgsave-error yes
rdbcompression yes 
rdbchecksum yes
dbfilename dump.rdb
dir ./ 
以上配置分别表示: 后台备份进程出错时,主进程停不停止写入
? 主进程不停止容易造成数据不一致 导出的rdb文件是否压缩 如果rdb的大小很大的话建议这么做 导入rbd恢复时数据时,要不要检验rdb的完整性 验证版本是不是一致 导出来的rdb文件名 rdb的放置路径

AOF持久化配置

appendonly yes
appendfsync always
appendfsync everysec
appendfsync no

是否打开aof日志功能
每1个命令,都立即同步到aof 
每秒写1次
写入工作交给操作系统,由操作系统判断缓冲区大小,统一写入到aof.

vim /data/6379/redis.conf
appendonly yes
appendfsync everysec

AOF持久化高级配置

no-appendfsync-on-rewrite yes/no
auto-aof-rewrite-percentage 100 
auto-aof-rewrite-min-size 64mb
配置分别表示:
正在导出rdb快照的过程中,要不要停止同步aof
aof文件大小比起上次重写时的大小,增长率100%时重写,缺点:业务开始的时候,会重复重写多次。
aof文件,至少超过64M时,重写

RDB到AOF的切换

在 Redis 2.2 或以上版本,可以在不重启的情况下,从 RDB 切换到 AOF :
1、为最新的 dump.rdb 文件创建一个备份。
2、将备份放到一个安全的地方。
3、执行以下两条命令:
redis-cli> CONFIG SET appendonly yes 
redis-cli> CONFIG SET save  ""
4、确保命令执行之后,数据库的键的数量没有改变。
5、确保写命令会被正确地追加到 AOF 文件的末尾

步骤 3 执行的第一条命令开启了 AOF 功能,Redis 会阻塞直到初始 AOF 文件创建完成为止, 之后 Redis 会继续处理命令请求,并开始将写入命令追加到 AOF 文件末尾。
步骤 3 执行的第二条命令用于关闭 RDB 功能。这一步是可选的,如果你愿意的话,也可以同时使用 RDB 和 AOF 这两种持久化功能。
注意:别忘了在 redis.conf 中打开 AOF 功能! 否则的话, 服务器重启之后, 之前通过 CONFIG SET 设置的配置就会被遗忘, 程序会按原来的配置来启动服务器。

面试题

redis 持久化方式有哪些?有什么区别?
  rdb:基于快照的持久化,速度更快,一般用作备份,主从复制也是依赖于rdb持久化功能
  aof:以追加的方式记录redis操作日志的文件。可以最大程度的保证redis数据安全,
    类似于mysql的binlog

Redis多数据类型开发(了解)

数据类型(value)种类

  1. String
  2. Hash
  3. List
  4. Set
  5. Sorted set

常用的key(自主定制名字)操作

KEYS *            查看KEY支持通配符
DEL               删除给定的一个或多个key
EXISTS            检查是否存在
TYPE              返回键所存储值的类型
EXPIRE\\ PEXPIRE   以秒\\毫秒设定生存时间
TTL\\ PTTL         以秒\\毫秒为单位返回生存时间
PERSIST           取消生存实现设置


flushdb             清空当前选择的数据库
del mykey mykey2         删除了两个 Keys

move mysetkey 1         将当前数据库中的 mysetkey 键移入到 ID 为 1 的数据库中
rename mykey mykey1     将 mykey 改名为 mykey1
renamenx oldkey newkey     如果 newkey 已经存在,则无效
expire mykey 100         将该键的超时设置为 100 秒
persist mykey         将该 Key 的超时去掉,变成持久化的键

keys my*             获取当前数据库中所有以my开头的key
exists mykey         若不存在,返回0;存在返回1
select 0             打开 ID 为 0 的数据库
ttl mykey             查看存货时间还剩下多少秒
type mykey             返回mykey对应的值的类型
randomkey             返回数据库中的任意键


string(字符串)

形式比喻:name   “张山”
定义:
  string是redis最基本的类型,一个key对应一个value。
  一个键最大能存储512MB。 String类型有如下基本操作(官网):   set   get   incr 计数+1   incrby 可以指定数量,一次性添加   decr 计数-1   decrby 指定数量   mset 同时设置多个键值对   mget   append   getset   setex   setnx   del   setrange   strlen   getrange 应用场景
  1.基本的键值对存储
2.计数器
互联网当中,点击量,访问量,关注量等   常规计数:微博数,粉丝数等
  游戏应用,如血量、魔法值


set mykey "test"               为键设置新值,并覆盖原有值
getset mycounter 0               设置值,取值同时进行
setex mykey 10 "hello"          设置指定 Key 的过期时间为10秒,在存活时间可以获取value
setnx mykey "hello"               若该键不存在,则为键设置新值
mset key3  "zyx"  key4 "xyz"      批量设置键


del mykey                      删除已有键


append mykey "hello"              若该键并不存在,返回当前 Value 的长度
                              该键已经存在,返回追加后 Value的长度
incr mykey                   值增加1,若该key不存在,创建key,初始值设为0,增加后结果为1
decrby  mykey  5               值减少5
setrange mykey 20 dd              把第21和22个字节,替换为dd, 超过value长度,自动补0

查  
exists mykey                     判断该键是否存在,存在返回 1,否则返回0
get mykey                    获取Key对应的value
strlen mykey                  获取指定 Key 的字符长度
ttl mykey                     查看一下指定 Key 的剩余存活时间(秒数)
getrange mykey 1 20              获取第2到第20个字节,若20超过value长度,则截取第2个和后面所有的
mget key3 key4                   批量获取键

Hash(字典)

最接近MySQL表结构的数据类型

形式比喻:stu:{id:101,name:zhangsan}
定义:
  我们可以将Redis中的Hashes类型看成具有String Key和String Value的map容器。   所以该类型非常适合于存储值对象的信息。如Username、Password和Age等。
  如果Hash中包含很少的字段,那么该类型的数据也将仅占用很少的磁盘空间。
  每一个Hash可以存储4294967295个键值对。 hash类型有如下操作:   hset   hsetnx   hmset   hdel   del   hincrby   hget   hmget   hlen   hexists   hgetall   hkeys   hvals 应用场景:   存储部分变更的数据,如用户信息等。
127.0.0.1:6379> hset zhangsan name zs 插入
(integer) 1
127.0.0.1:6379> hmset student id 101 name zs age 20 gender male 插入多个
OK
127.0.0.1:6379> hmset stu id 102 name lisi age 21 gender male
OK
127.0.0.1:6379> hmget stu id name age gender 获取指定值
1) "102"
2) "lisi"
3) "21"
4) "male"
127.0.0.1:6379> hgetall stu 获取全部键值对
1) "id"
2) "102"
3) "name"
4) "lisi"
5) "age"
6) "21"
7) "gender"
8) "male"

作业:
  MySQL 中 city表中前10行数据,灌入到redis
mysql> select concat("hmset stu_",name," id ",id," name ",name) from t1;
+---------------------------------------------------+
| concat("hmset stu_",name," id ",id," name ",name) |
+---------------------------------------------------+
| hmset stu_zhang3 id 1 name zhang3                 |
| hmset stu_li4 id 2 name li4                       |
| hmset stu_wang5 id 3 name wang5                   |
+---------------------------------------------------+


hset myhash field1 "s"    
若字段field1不存在,创建该键及与其关联的Hashes, Hashes中,key为field1 ,并设value为s ,若存在会覆盖原value
hsetnx myhash field1 s    
若字段field1不存在,创建该键及与其关联的Hashes, Hashes中,key为field1 ,并设value为s, 若字段field1存在,则无效
hmset myhash field1 "hello" field2 "world       一次性设置多个字段

hdel myhash field1                       删除 myhash 键中字段名为 field1 的字段
del myhash                          删除键
改  
hincrby myhash field 1                  给field的值加1


hget myhash field1                       获取键值为 myhash,字段为 field1 的值
hlen myhash                           获取myhash键的字段数量
hexists myhash field1                     判断 myhash 键中是否存在字段名为 field1 的字段
hmget myhash field1 field2 field3              一次性获取多个字段
hgetall myhash                           返回 myhash 键的所有字段及其值
hkeys myhash                          获取myhash 键中所有字段的名字
hvals myhash                           获取 myhash 键中所有字段的值

LIST(列表)

形式比喻:webchat [m1,m2,m3]
定义:
  List类型是按照插入顺序排序的字符串链表。
  和数据结构中的普通链表一样,我们可以在其头部(left)和尾部(right)添加新的元素。
  在插入时,如果该键并不存在,Redis将为该键创建一个新的链表。
  与此相反,如果链表中所有的元素均被移除,那么该键也将会被从数据库中删除。
  List中可以包含的最大元素数量是4294967295。
常用操作:   lpush   lpushx   linsert   rpush   rpushx   rpoplpush   del   lrem   ltrim   lset   rpoplpush   lrange   lpop   lindex 应用场景
  微信朋友圈
    首先看到的是最新的   消息队列系统     比如sina微博:  在Redis中我们的最新微博ID使用了常驻缓存,这是一直更新的。
  但是做了限制不能超过5000个ID,因此获取ID的函数会一直询问Redis。
  只有在start
/count参数超出了这个范围的时候,才需要去访问数据库。 
  系统不会像传统方式那样“刷新”缓存,Redis实例中的信息永远是一致的。
  SQL数据库(或是硬盘上的其他类型数据库)只是在用户需要获取“很远”的数据时才会被触发,
  而主页或第一个评论页是不会麻烦到硬盘上的数据库了。
127.0.0.1:6379> LPUSH wechat "today is 1"
(integer) 1
127.0.0.1:6379> LPUSH wechat "today is 2"
(integer) 2
127.0.0.1:6379> LPUSH wechat "today is 3"
(integer) 3
127.0.0.1:6379> LPUSH wechat "today is 4"
(integer) 4
127.0.0.1:6379> LPUSH wechat "today is 5"

127.0.0.1:6379> LRANGE wechat 0 -1 第一个到最后一个
1) "today is 5"
2) "today is 4"
3) "today is 3"
4) "today is 2"
5) "today is 1"


lpush mykey a b              若key不存在,创建该键及与其关联的List,依次插入a ,b, 若List类型的key存在,则插入value中
lpushx mykey2 e              若key不存在,此命令无效, 若key存在,则插入value中
linsert mykey before a a1      在 a 的前面插入新元素 a1
linsert mykey after e e2       在e 的后面插入新元素 e2
rpush mykey a b             在链表尾部先插入b,在插入a
rpushx mykey e              若key存在,在尾部插入e, 若key不存在,则无效
rpoplpush mykey mykey2       将mykey的尾部元素弹出,再插入到mykey2 的头部(原子性的操作)

del mykey                  删除已有键
lrem mykey 2 a               从头部开始找,按先后顺序,值为a的元素,删除数量为2个,若存在第3个,则不删除
ltrim mykey 0 2              从头开始,索引为0,1,2的3个元素,其余全部删除

lset mykey 1 e               从头开始, 将索引为1的元素值,设置为新值 e,若索引越界,则返回错误信息
rpoplpush mykey mykey          将 mykey 中的尾部元素移到其头部

lrange mykey 0 -1          取链表中的全部元素,其中0表示第一个元素,-1表示最后一个元素。
lrange mykey 0 2            从头开始,取索引为0,1,2的元素
lrange mykey 0 0            从头开始,取第一个元素,从第0个开始,到第0个结束
lpop mykey                  获取头部元素,并且弹出头部元素,出栈
lindex mykey 6              从头开始,获取索引为6的元素 若下标越界,则返回nil

SET(集合)

形式比喻:weibo (m1,m2,m3)
定义:
  Set类型看作为没有排序的字符集合。
  Set可包含的最大元素数量是4294967295。
  如果多次添加相同元素,Set中将仅保留该元素的一份拷贝。 常用操作:   sadd 创建   spop   srem   smove   sismember   smembers   scard   srandmember   sdiff 差集   sdiffstore   sinter 交集   sinterstore   sunion 并集   sunionstore 应用场景:   案例:  在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。
  Redis还为集合提供了求交集、并集、差集等操作,
  可以非常方便的实现如共同关注、共同喜好、二度好友等功能,
  对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存集到一个新的集合中。

127.0.0.1:6379> sadd lxl pg1 pg2 baoqiang masu marong
(integer) 5
127.0.0.1:6379> sadd jnl baoqiang yufan baobeier zhouxingchi
(integer) 4
127.0.0.1:6379> SUNION lxl jnl 并集
1) "zhouxingchi"
2) "baobeier"
3) "pg2"
4) "yufan"
5) "masu"
6) "baoqiang"
7) "pg1"
8) "marong"
127.0.0.1:6379>
127.0.0.1:6379> SINTER lxl jnl 交集
1) "baoqiang"
127.0.0.1:6379> SDIFF lxl jnl 差集
1) "masu"
2) "pg1"
3) "marong"
4) "pg2"
127.0.0.1:6379> SDIFF jnl lxl 差集
1) "yufan"
2) "zhouxingchi"
3) "baobeier"


sadd myset a b c  
若key不存在,创建该键及与其关联的set,依次插入a ,b,若key存在,则插入value中,若a 在myset中已经存在,则插入了 d 和 e 两个新成员。

spop myset              尾部的b被移出,事实上b并不是之前插入的第一个或最后一个成员
srem myset a d f          若f不存在, 移出 a、d ,并返回2

smove myset myset2 a        将a从 myset 移到 myset2,

sismember myset a            判断 a 是否已经存在,返回值为 1 表示存在。
smembers myset            查看set中的内容
scard myset                获取Set 集合中元素的数量
srandmember myset          随机的返回某一成员
sdiff myset1 myset2 myset3      1和2得到一个结果,拿这个集合和3比较,获得每个独有的值
sdiffstore diffkey myset myset2 myset3      3个集和比较,获取独有的元素,并存入diffkey 关联的Set中
sinter myset myset2 myset3               获得3个集合中都有的元素
sinterstore interkey myset myset2 myset3  把交集存入interkey 关联的Set中
sunion myset myset2 myset3               获取3个集合中的成员的并集
sunionstore unionkey myset myset2 myset3  把并集存入unionkey 关联的Set中

SortedSet(有序集合)

形式比喻:weibo (socre m1,score m2,score m3)
定义:  
  Sorted-Sets中的每一个成员都会有一个分数(score)与之关联,
  Redis正是通过分数来为集合中的成员进行从小到大的排序。
  成员是唯一的,但是分数(score)却是可以重复的。 常用操作   zadd   zrem   zincrby   zrange   zrank   zcard   zcount   zscore   zrangebyscore   zremrangebyscore   zremrangebyrank   zrevrange   zrevrangebyscore   zrevrangebyscore 应用场景:   排行榜应用,
    取TOP N操作  这个需求与上面需求的不同之处在于,前面操作以时间为权重,
  这个是以某个条件为权重,比如按顶的次数排序,这时候就需要我们的sorted set出马了,
  将你要排序的值设置成sorted set的score,将具体的数据设置成相应的value,
  每次只需要执行一条ZADD命令即可。
127.0.0.1:6379> zadd music 0 fskl 0 fshkl 0 lzlsfs 0 tm 0 ass
(integer) 5
127.0.0.1:6379> ZINCRBY music 1000 fskl
"1000"
127.0.0.1:6379> ZINCRBY music 10000  fshkl
"10000"
127.0.0.1:6379> ZINCRBY music 100000 lzlsfs
"100000"
127.0.0.1:6379> ZINCRBY music 1000000 tm
"1000000"
127.0.0.1:6379> ZINCRBY music 100 ass
"100"
127.0.0.1:6379> ZREVRANGE music 0 -1 withscores
 1) "tm"
 2) "1000000"
 3) "lzlsfs"
 4) "100000"
 5) "fshkl"
 6) "10000"
 7) "fskl"
 8) "1000"
 9) "ass"
10) "100"
127.0.0.1:6379> ZREVRANGE music 0 -1
1) "tm"
2) "lzlsfs"
3) "fshkl"
4) "fskl"
5) "ass"


zadd myzset 2 "two" 3 "three"           添加两个分数分别是 2 和 3 的两个成员

zrem myzset one two                  删除多个成员变量,返回删除的数量

zincrby myzset 2 one                  将成员 one 的分数增加 2,并返回该成员更新后的分数

zrange myzset 0 -1 WITHSCORES          返回所有成员和分数,不加WITHSCORES,只返回成员
zrank myzset one                   获取成员one在Sorted-Set中的位置索引值。0表示第一个位置
zcard myzset                        获取 myzset 键中成员的数量
zcount myzset 1 2                   获取分数满足表达式 1 <= score <= 2 的成员的数量
zscore myzset three                  获取成员 three 的分数
zrangebyscore myzset  1 2               获取分数满足表达式 1 < score <= 2 的成员
#-inf 表示第一个成员,+inf最后一个成员
#limit限制关键字
#2  3  是索引号
zrangebyscore myzset -inf +inf limit 2 3  返回索引是2和3的成员
zremrangebyscore myzset 1 2           删除分数 1<= score <= 2 的成员,并返回实际删除的数量
zremrangebyrank myzset 0 1              删除位置索引满足表达式 0 <= rank <= 1 的成员
zrevrange myzset 0 -1 WITHSCORES           按位置索引从高到低,获取所有成员和分数
#原始成员:位置索引从小到大
      one  0  
      two  1
#执行顺序:把索引反转
      位置索引:从大到小
      one 1
      two 0
#输出结果: two  
       one
zrevrange myzset 1 3                  获取位置索引,为1,2,3的成员
#相反的顺序:从高到低的顺序
zrevrangebyscore myzset 3 0              获取分数 3>=score>=0的成员并以相反的顺序输出
zrevrangebyscore myzset 4 0 limit 1 2      获取索引是1和2的成员,并反转位置索引

生产消费者模型

消息模式

Redis发布消息通常有两种模式:
  队列模式(queuing)
  发布-订阅模式(publish-subscribe)

任务队列的好处:
  松耦合。
  易于扩展。

Redis 发布--订阅

其实从Pub/Sub的机制来看,它更像是一个广播系统,多个Subscriber可以订阅多个Channel,
多个Publisher可以往多个Channel中发布消息。可以这么简单的理解:   Subscriber:收音机,可以收到多个频道,并以队列方式显示   Publisher:电台,可以往不同的FM频道中发消息   Channel:不同频率的FM频道

一个Publisher,多个Subscriber模型

如下图所示,可以作为消息队列或者消息管道。

主要应用:通知、公告

多个Publisher,一个Subscriber模型

可以将PubSub做成独立的HTTP接口,各应用程序作为Publisher向Channel中发送消息,
Subscriber端收到消息后执行相应的业务逻辑,比如写数据库,显示等等。 主要应用:排行榜、投票、计数。

多个Publisher,多个Subscriber模型

故名思议,就是可以向不同的Channel中发送消息,由不同的Subscriber接收。
主要应用:群聊、聊天。

发布订阅实践

PUBLISH channel msg
    将信息 message 发送到指定的频道 channel 
SUBSCRIBE channel [channel ...]
    订阅频道,可以同时订阅多个频道
UNSUBSCRIBE [channel ...]
    取消订阅指定的频道, 如果不指定频道,则会取消订阅所有频道
PSUBSCRIBE pattern [pattern ...]
    订阅一个或多个符合给定模式的频道,每个模式以 * 作为匹配符,
  比如 it* 匹配所 有以 it 开头的频道( it.news 、 it.blog 、 it.tweets 等等),
  news.* 匹配所有 以 news. 开头的频道( news.it 、 news.global.today 等等),
  诸如此类 PUNSUBSCRIBE [pattern [pattern ...]] 退订指定的规则, 如果没有参数则会退订所有规则 PUBSUB subcommand [argument [argument ...]] 查看订阅与发布系统状态 注意:使用发布订阅模式实现的消息队列,当有客户端订阅channel后只能收到后续发布到该频道的消息,
之前发送的不会缓存,必须Provider和Consumer同时在线。

发布订阅例子:
窗口1:
127.0.0.1:6379> SUBSCRIBE baodi

窗口2:
127.0.0.1:6379> PUBLISH baodi "jin tian zhen kaixin!"

订阅多频道:

窗口1:
127.0.0.1:6379> PSUBSCRIBE wang*

窗口2:

127.0.0.1:6379> PUBLISH wangbaoqiang "jintian zhennanshou

消息队列系统对比

客户端在执行订阅命令之后进入了订阅状态
只能接收 SUBSCRIBE 、PSUBSCRIBE、 UNSUBSCRIBE 、PUNSUBSCRIBE 四个命令。
开启的订阅客户端,无法收到该频道之前的消息,因为 Redis 不会对发布的消息进行持久化。
和很多专业的消息队列系统(例如Kafka、RocketMQ)相比,Redis的发布订阅略显粗糙
例如:无法实现消息堆积和回溯。
但胜在足够简单,如果当前场景可以容忍的这些缺点,也不失为一个不错的选择。

redis事务管理

redis中的事务跟关系型数据库中的事务是一个相似的概念,但是有不同之处。
关系型数据库事务执行失败后面的sql语句不在执行,而redis中的一条命令执行失败,其余的命令照常执行 redis中开启一个事务是使用multi,相当于begin\\start transaction,
exec提交事务,discard取消队列命令(非回滚操作)。

事务命令

DISCARD 
  取消事务,放弃执行事务块内的所有命令。
EXEC 
  执行所有事务块内的命令。
MULTI 
  标记一个事务块的开始。
UNWATCH 
  取消 WATCH 命令对所有 key 的监视。
WATCH key [key ...] -- 乐观锁
  监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,
  那么事务将被打断。

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set a 1
QUEUED 放入了队列中,并没有执行
127.0.0.1:6379> set b 2
QUEUED
127.0.0.1:6379> set c 3
QUEUED
127.0.0.1:6379> exec 提交才回执行
1) OK
2) OK
3) OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set a 10
QUEUED
127.0.0.1:6379> set b 20
QUEUED
127.0.0.1:6379> set c 30
QUEUED
127.0.0.1:6379> DISCARD 取消后,就不执行了
OK

开启事务功能时(multi)
multi 
command1      
command2
command3
command4

4条语句作为一个组,并没有真正执行,而是被放入同一队列中。
如果,这是执行discard,会直接丢弃队列中所有的命令,而不是做回滚。

exec
当执行exec时,对列中所有操作,要么全成功要么全失败


----------
127.0.0.1:6379> set a b
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set a b
QUEUED
127.0.0.1:6379> set c d
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK

--------------------
8、redis乐观锁实现(模拟买票)
发布一张票
set ticket 1

窗口1:
watch ticket
multi
set ticket 0       1---->0

窗口2:
multi 
set ticket 0 
exec 

窗口1:
exec

redis事务中的锁机制

举例:我正在买票
Ticket -1 , money -100
而票只有1张, 如果在我multi之后,和exec之前, 票被别人买了,即ticket变成0了.
我该如何观察这种情景,并不再提交
悲观的想法: 
世界充满危险,肯定有人和我抢, 给ticket上锁, 只有我能操作. [悲观锁]
乐观的想法:
没有那么多人和我抢,因此,我只需要注意,有没有人更改ticket的值就可以了 [乐观锁]
redis就是乐观锁的机制

服务器管理命令

Info  数据库状态信息
  重点关注Memory和Replication Clinet list Client
kill ip:port config get * CONFIG GET/SET 动态修改 Dbsize 查看当前库key的个数 FLUSHALL 清空所有数据 包括已经持久化的信息 select 1 根据索引进入相应的库 redis中有16个库, FLUSHDB 清空当前库 MONITOR 监控实时指令 SHUTDOWN 关闭服务器 save将当前数据保存 SLAVEOF host port 主从配置 SLAVEOF NO ONE SYNC 主从同步 ROLE返回主从角色

Redis复制及高可用

使用异步复制。
一个主服务器可以有多个从服务器。
从服务器也可以有自己的从服务器。
复制功能不会阻塞主服务器。
可以通过复制功能来让主服务器免于执行持久化操作,由从服务器去执行持久化操作即可。
但还是建议打开,防止雪崩灾难导致数据丢失

当配置Redis复制功能时,强烈建议打开主服务器的持久化功能
否则的话,由于延迟等问题,部署的服务应该要避免自动拉起。 为了帮助理解主服务器关闭持久化时自动拉起的危险性,参考以下会导致主从服务器数据全部丢失的例子:
1. 假设节点A为主服务器,并且关闭了持久化。 并且节点B和节点C从节点A复制数据 2. 节点A崩溃,然后由自动拉起服务重启了节点A. 由于节点A的持久化被关闭了,
  所以重启之后没有任何数据
3. 节点B和节点C将从节点A复制数据,但是A的数据是空的, 于是就把自身保存的数据副本删除。 在关闭主服务器上的持久化,
并同时开启自动拉起进程的情况下,即便使用Sentinel来实现Redis的高可用性,也是非常危险的。
因为主服务器可能拉起得非常快,
以至于Sentinel在配置的心跳时间间隔内没有检测到主服务器已被重启,
然后还是会执行上面的数据丢失的流程。 无论何时,数据安全都是极其重要的,所以应该禁止主服务器关闭持久化的同时自动拉起

以下是关于 Redis 复制功能的几个重要方面:
Redis 使用异步复制。 从 Redis 2.8 开始, 从服务器会以每秒一次的频率向主服务器报告复制流(replication stream)的处理进度。
一个主服务器可以有多个从服务器。
不仅主服务器可以有从服务器, 从服务器也可以有自己的从服务器, 多个从服务器之间可以构成一个图状结构。
复制功能不会阻塞主服务器: 即使有一个或多个从服务器正在进行初次同步, 主服务器也可以继续处理命令请求。
复制功能也不会阻塞从服务器: 只要在 redis.conf 文件中进行了相应的设置, 即使从服务器正在进行初次同步,
服务器也可以使用旧版本的数据集来处理命令查询。
不过, 在从服务器删除旧版本数据集并载入新版本数据集的那段时间内, 连接请求会被阻塞。
你还可以配置从服务器, 让它在与主服务器之间的连接断开时, 向客户端发送一个错误。
复制功能可以单纯地用于数据冗余(data redundancy), 也可以通过让多个从服务器处理只读命令请求来提升扩展性(scalability):
比如说, 繁重的 SORT 命令可以交给附属节点去运行。
可以通过复制功能来让主服务器免于执行持久化操作: 只要关闭主服务器的持久化功能, 然后由从服务器去执行持久化操作即可。

主从复制原理

1. 从服务器向主服务器发送 SYNC 命令。
2. 接到 SYNC 命令的主服务器会调用BGSAVE 命令,创建一个 RDB 文件,并使用缓冲区记录接下来执行的所有写命令。
3. 当主服务器执行完 BGSAVE 命令时,它会向从服务器发送 RDB 文件,而从服务器则会接收并载入这个文件。
4. 主服务器将缓冲区储存的所有写命令发送给从服务器执行。

1、在开启主从复制的时候,使用的是RDB方式的,同步主从数据的
2、同步开始之后,通过主库命令传播的方式,主动的复制方式实现
32.8以后实现PSYNC的机制,实现断线重连

SYNC命令执行过程

在主从服务器完成同步之后,
主服务器每执行一个写命令,它都会将被执行的写命令发送给从服务器执行,
这个操作被称为“命令传播”(command propagate)。 命令传播是一个持续的过程:只要复制仍在继续,命令传播就会一直进行,使得主从服务器的状态可以一直保持一致。

复制中的SYNC和PSYNC

在 Redis 2.8 版本之前, 
断线之后重连的从服务器总要执行一次完整重同步(full resynchronization)操作。 从 Redis
2.8 开始,Redis 使用 PSYNC命令代替 SYNC 命令。 PSYNC 比起 SYNC 的最大改进在于 PSYNC 实现了部分重同步(partial resync)
特性:在主从服务器断线并且重新连接的时候,只要条件允许,PSYNC 可以让主服务器只向从服务器同步断线期间缺失的数据,
而不用重新向从服务器同步整个数据库。

SYNC处理断线重连方式

 PSYNC处理断线重连

复制的一致性的问题

为了实现主从复制,读写分离的结构,同时,也产生了数据一致性的问题

当然,不止是redis,mysql也会有这个问题