一、Redis 的 五大数据类型 1、key
查看还有多久过期,-1 表示永不过期,-2 表示已过期
2、String String 是 Redis 最基本的类型。 String 类型是二进制安全的。意味着 Redis 的 string 可以包含任何数据。比如 jpg 图片或者序列化的对象。 String 类型是 Redis 最基本的数据类型,一个 Redis 中字符串 value 最多可以是 512M 常用操作
将 key 中存储的数字值增 1, 只能对数字值的操作,如果为空,新增值为 1。
将 key 中存储的数字值减 1,只能对数字值操作,如果为空,新增值为-1
mset<key1><value1><key2><value2>
msetnx<key1><value1><key2><value2>
同时设置一个或多个 key-value 对,当且仅当所有给定的 key 都不存在
getrange<key><起始位置><结束位置>
获取值的范围,类似 java 中的 substring
setrange<key> <起始位置> <value>
用<vakue>覆盖<key>所存储的字符串值,从<起始位置>开始
incr key 操作的原子性 所谓的原子是指不会被线程的调度机制打断的操作:这种操作一旦开始,就一直运行到结束,中间不会有任何的 context switch (切换到另一个线程)。 在单线程中,能够在单条指令中完成的操作都可以认为是”原子操作”,因为中断只能发生在于指令之间。 在多线程中,不能被其他的进程(线程)打断的操作就叫原子操作。 Redis 单命令的原子性主要得益于 Redis 的单线程 3、List 单键多值 Redis ;列表是简单的字符串列表,按照插入顺序排序。可以添加一个元素列表的头部(左边) 或者尾部 (右边)。 它的底层实际是一个双向链表,对两端的操作行选性能很高,通过索引下标的操作中间的节点性能会差 5. 常用操作
4、Set Redis set 对外提供的功能与 list 类似是一个列表功能,特殊之处在于 set 是可以自动排重的,当你需要存储一个列表数据,又不希望出现一个重复的数据时,set 是一个很好的选择,并且 set 提供了判断某个成员是否存在一个 set 集合内的重要接口,这个也是 list 所不能提供的。 Rdis 的 set 是 String 类型的无序集合。它底层其实是一个 value 为 null 的 hash 表,所以添加,删除,查找的复杂度都是 O(1)。 常用操作
setadd<key><value1><value2>…
将一个或多个 menber 元素加入到集合 key 当中,已经存于集合的 menber 元素将忽略
判断一个集合<key>是否含有该<value>值,有返回值 1,没有返回 0
srem<key><value1><value2>…
5、Hash Redis hash 是一键值对集合 Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特点适用用于存储对象。 类似 java 里面的 Map<String,Object> 分析一个问题:现有一个 JavaBean 对象,在 Redis 中如何存? 第一种方案:用户 ID 为 key,VALUE 为 JavaBean 序列化后的字符串 缺点:每次修改的用户的某个属性需要,先反序列化改好后在序列化回去。开销较大
第二种方案:用户 ID+属性名作为 key 属性值作为 Value. 缺点:用户 ID 数据冗余
第三种方案:通过 key(用户 ID) + filed(属性标签) 就可以操作对应的属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题 常用操作
给<key>集合中的<field>键赋值<value>
从<key1>集合<field> 取出 value
hmset<key1><field1><value1> <field2> <value2>…
查看哈希表 key 中,给定域 field 是否存在
hincrby<key><field><increment>
为哈希表 key 中的域 field 的值加上增量 increment
hsetnx <key> <field><value>
将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在
6、zset(sorted ser) Redis 有序集合 zset 与 普通的 set 非常相似,是一个没有重复元素的字符串集合。不同之处是有序集合的每个成员都关联一个评分(sroce), 这个评分(score) 被用来按照从最低的分到最高的方式排序集合中的成员。集合中的成员是唯一的,但是评分可以是重复了。 因为元素是有序的,所以你也可以很快的根据评分(score) 或者次序(position)来获取一个范围的元素。访问有序集合的中间元素也是非常快的,因此你能够使用有序集合作为一个没有重复的成员的智能列表。 常用操作
zadd <key> <score1> <value1> <score2> <value2>…
将一个或多个 member 元素及其 score 值加入到有序集合 key 当中
zrange<key> <start><stop>[WINHSCORES]
返回有序集 key 中,下标在<start><stop>之间的元素带 WITHSCORES,可以让分数一起和值返回到结果集。
zrangebyscore key min max [withscores] [limit offset count]
返回有序集合 key 中, 所有 score 值介于 min 和 max 之间(包括等于 min 或 max) 的成员。有序集合按 score 值递增(从小到大)次序排列。
zrevrangebyscore key max min [withscores] [limit offset count]
zincrby <key> <increment> <value>
二、Redis 的相关配置 计量单位说明,大小写不敏感 include
类似 jsp 中的 include,多实例的情况可以把公用的配置文件提取出来ip 地址的绑定 bind 默认情况 bind=127.0.0.1 只能接受本机的访问请求 不写的情况下,无限制接受任何 ip 地址的访问 生产环境肯定要写你应用服务器的地址 如果开启了 protected-mode ,那么在没有设定 bind ip 且没有设密码的情况下, Redis 只允许接受本机的相应 tcp-backlog 可以理解是一个请求到达后至到接受进程处理前的队列 backlog 队列总和=未完成三次握手队列 + 已经完成三次握手队列 高并发环境 tcp-backlog 设置值跟超时时限内的 Redis 吞吐量决定 timeout
一个空闲的客户端维持多少秒会关闭, 0 为永不关闭。tcp keepalive
对访问客户端的一种心跳检测,每个 n 秒检测一次,官方推荐设置为 60 秒daemonize
是否为后台进程pidfile
存放 pid 文件的位置,每个实例会产生一个不同的 pid 文件log level
四个级别根据使用阶段来选择,生产环境选择 notice 或者 warninglog level
日志文件名称syslog
是否将 Redis 日志输送到 linux 系统日志服务中syslog-ident
日志的标志syslog-facility
输出日志的设备database
设定库的数量 默认 16security
在命令行中设置密码maxclient
最大客户端连接数maxmemory
设置 Redis 可以使用的内存量。一旦到达内存使用上限, Redis 将会试图移除内部数据,移除规则可以通过 maxmemory-policy 来指定。如果 Redis 无法根据移除规则来移除内存中的数据,或者设置了“不允许移除”,那么 Redis 则会针对那些需要申请内存的指令返回错误信息,比如 SET、 LPUSH 等。Maxmemory-policy volatile-lru: 使用 LRU 算法移除 key, 只对设置了过期时间的键 allkeys-lru:使用 LRU 算法移除 key volatile-random:在过期集合中移除随机的 key,只对设置了过期时间的键 allkeys-random:移除随机的 key volatile-ttl:移除那些 TTL 值最小的 key,即那些最近要过期的 key noeviction:不进行移除。针对写操作,只是返回错误信息 Maxmemory-samples
设置样本数量, LRU 算法和最小 TTL 算法都并非是精确的算法,而是估算值,所以你可以设置样本的大小。一般设置 3 到 7 的数字,数值越小样本越不准确,但是性能消耗也越小。三、Redis 的 Java 客户端 Jedis 1、Jedis 所需要的 jar 包,可以通过 Maven 的依赖引入
2. 使用 Windows 环境下 Idea 连接虚拟机中的 Redis 注意事项
禁用 Linux 的防火墙: Linux(CentOS7)里执行命令 : systemctl stop firewalld.service redis.conf 中注释掉 bind 127.0.0.1 ,然后 protect-mode no。 3. Jedis 测试连通性 :
public class JedisTest {
public static void main(String[] args) {
// 连接本地 Redis 服务
Jedis jedis = new Jedis("192.168.64.129",6379);
// 查看服务是否运行,打出pong 表示OK
System.out.println("connection is OK =====>:" + jedis.ping());
}
}
四、 Redis 事务 1、Redis 中的事务的定义 Redis 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。Redis 事务的主要作用就是串联多个命令防止别的命令插队。
2、multi、exec、discard 从输入 Multi 命令开始,输入的命令都会依次进入执行命令列中,单不会执行,至到输入 Exec 后,Redis 会将之前的命令依次执行。 组队的过程中可以通过 discard 来放弃组队。 3、事务中的错误处理 组队中的某个命令出现了报告错误,执行时整个的所有队列都会别取消。 2 . 如果执行阶段某个命令出现了错误,则只有报错的命令不会被执行,而其他的命令会执行,不会回滚。
4、通过事务解决问题 悲观锁(Pessimistic Lock) ,顾名思义,就是很悲观,每次去拿数据的时候都被认为别人会修改,所以每次在那数据的时候都会上锁,这样别人想拿这个数据就会 block 直接它拿到锁。传统的关系型数据库里边就用到了很多这样的机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。 乐观锁(Optimistic Lock) ,顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下再此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis 就是利用这种 check-and-set 机制实现的事务。 5、Redis 事务的使用 WACH key[key….]
在执行 multi 之前, 先执行 watch key1[key2], 可以监视一个(或多个)key,如果在事务执行之前这个(或这些)Key 被其他命令所改动,那么事务将被打断。 2. unwatch
取消 WATCH 命令对所有 key 监视。如果执行 WATCH 命令之后,EXEC 命令或 DISCAED 命令先被执行了的话,那么就不需要在执行 UNWINAH 了。
3. 三特性
单独的隔离操作
事务中的所有命令都会被序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发来的命令请求所打断。 没有隔离级别的概念
队列中的命令没有被提交之前都不会实际的被执行,因为事务提交前的任何指令都不会被实际执行,也就不存在”事务内的查询要看到事务里的更新,在事务外查询不能看到” 不保证原子性
Redis 同一个事务中如果有一条命令执行失败,其后的命令仍会被执行,没有回滚 五、Redis 持久化 Redis 提供 2 个不用形式的持久化方式 RDB 和 AOF
1、RDB 在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的 Snapshot 快照,它恢复时是将快照文件直接读到内存里。 备份的执行:Redis 会单独创建(fork)一个子进程来进行持久化,会将数据写入到一个临时文件中,待持久化进程都结束,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何的 IO 操作的,这就是确保了极高的性能如果需要进行大规模的数据恢复,且对数据恢复的完整性不是非常敏感,那 RDB 方式要比 AOF 方式更加的高效。RDB 的缺点是最后一次持久化后的数据可能丢失。 fork: 在 Linux 程序中,fork() 会产生一个和父进程完全相同的子进程,但子进程在此后多会 exec 系统调用,出于效率考虑,Linux 中引入”写时复制技术“,一般情况父进程和子进程会共同一段物理内存,只有进程空间的各段的内容要发生变化时,才会父进程的内容复制一份个子进程。 RDB 保存的文件
在 redis.conf 中的配置文件,默认为 dump. rdb 5. RDB 文件的保存路径
默认为 Redis 启动时命令所在的目录下,也可以修改
6. RDB 的保存策略
手动保存快照
save:只管保存,其他不管,全部阻塞
bgsave: 按照保存策略的自动保存 RDB 的相关配置 stop-writes-on-bgsave-error yes
当 Redis 无法写入磁盘的话,直接关闭 Redis 的写入操作。 rdbcompression yes
进行 rdb 保存时,将文件压缩 rdbchecksum yes
在存储的快照后,还可以让 Redis 使用 CRC64 算法来进行数据校验,但是这样做会增加大约 10% 的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能 RDB 的备份 与恢复 备份:先通过 config get dir 查询 rdb 文件的目录 , 将*.rdb 的文件拷贝到别的地方 恢复: 关闭 Redis,把备份的文件拷贝到工作目录下,启动 redis,备份数据会直接加载。 RDB 的优缺点 优点: 节省磁盘空间,恢复速度快. 缺点:虽然 Redis 在 fork 时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能。在备份周期在一定的间隔时间做一次备份,所有如果 Redis 意外 down 掉的话,就会失去最后一个快照后的所有修改。 2、AOF 以日志的形式来记录每个写的操作,将 Redis 执行的过程的所有写的指令记录下来(读操作不记录),只许追加文件但不可以改写文件,Redis 启动之初会读取该文件重新构建数据,换言之,Redis 重启的话就根据日志文件的内容将写的指令从前到后执行一次以完成数据的恢复工作。 AOF 默认不开启,需要手动在配置文件中配置。 可以在 redis.conf 中配置文件名称,默认为 appendonly.aof AOF 文件的保存路径,同 RDB 的路径一致
AOF 文件故障备份 AOF 的备份机制和性能虽然和 RDB 不同,但是备份和恢复的操作 RDB 一样,都是拷贝文件,需要恢复时再拷贝到 Redis 工作目录下,启动系统即加载
AOF 文件故障恢复
如遇到 AOF 文件损坏,可通过 redis-check-aof –fix appendonly.aof 进行恢复 AOF 同步频率设置 始终同步,每次 Redis 的写入都会立刻记入日志 。 每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失。 把不主动进行同步,把同步时机交给操作系统。 Rewrite AOF 采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制,当 AOF 文件的大小超过所设定的阈值时, Redis 就会启动 AOF 文件的内容压缩,只保留可以恢复数据的最小指令集.可以使用命令 bgrewriteaof。 Redis 实现重写:AOF 文件持续增长而过大时,会 fork 出一条新进程来将 3 文件重写(也是先写临时文件最后写 rename),遍历新进程的内存中的数据,每条记录有一条的 Set 语句。重写的 aof 文件的操作,并没有读取旧的 aof 文件,而是将整个内存中的数据库名内容用命令的方式重写了一个新的 aof 文件,这点和快照有点类似。 何时重写: 重写虽然可以节省大量的磁盘空间,减少恢复时间。但是每次重写还是有一定的负担的,因此设定 Redis 要满足一定条件才会进行重写。 系统载入时或者上次重写完毕时,Redis 会记录此时的 AOF 大写,设为 base_size,如果 Redisde AOF 当前大小 >= base_size + base_size*100%(默认)且当前大小>=64mb(默认)的情况下,Redis 会对 AOF 进行重写。
AOF 的优缺点 优点:
备份机制更稳点,丢失数据概率更低。
可读的日志文本,通过操作 AOF 稳健,可以处理误操作。 缺点:
比起 RDB 占用更多的磁盘空间
恢复备份速度要慢
每次读写都同步的话,有一定的性能压力。 3、RDB 和 AOF 用哪个好 官方推荐两个都启用。 如果对数据不敏感,可以选单独用 RDB 不建议单独使用 AOF, 因为可能会出现 Bug。 如果只是做内存缓存,可以都不用 六、Redis 主从复制 主从复制,就是主机数据更新后根据配置和策略,自动同步到备用的 master/slaver 机制。 Master 以写为主, Slave 以读为主。
1、主从复制的目的 读写分离,性能扩展 容灾快速恢复 2、主从配置 原则:配从不配主 步骤: 准备三个 Redis 实例,一主两从 拷贝多个 redis.conf 文件 include 开启 daemonize yes pid 文件名字 pidfile 指定端口 port Log 文件名字 Dump.rdb 名字 dbfilename Appendonly 关闭或换掉名字 include /opt/myRedis/redis.conf
pidfile /var/run/redis6381.pid
port 6381
dbfilename dump6381.rdb
info replication 打印主从复制的相关信息 2. salveof <ip> <port> 成为主从复制的相关信息
3、一主二从模式 复制原理:
每次从机联通之后,都会给主机发送 sync 指令 主机立即进行存盘操作,发送 RDB 文件,给从机 从机收到 RDB 文件后,进行全盘加载 之后每次主机的写操作,都会立刻发送给从机,从机执行相同的命令 4、薪火相传模式演示 上一个 slave 可以是下一个 slave 的 Master,slave 同样可以接收其他的 slaves 的接收和同步请求,那么该 slave 作为链条中下一个的 master, 可以有效减轻 master 的写压力,去同步化降低风险。
中途变更转向:会清除之前的数据,重新建立拷贝最新的
风险是一旦某个 slave 宕机,后面的 slave 都没法备份 2. 反客为主(小弟上位)
当一个 master 宕机后,后面的 slave 可以立即升为 master, 其后面的 slave 不用做任何修改。用 slave no one 将从机边为主机。
3. 哨兵模式 sentine(推荐大哥)
反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。
配置哨兵
调整为一主二从模式 自定义的/myRedis 目录下新建 sentine.conf 文件 在配置文件中填写内容
==sentinel monitor mymaster 127.0.0.1 6379 1 ==
其中 mymaster 为监控对象起的服务器名称(随意起), 1 为 至少有多少个哨兵同意迁移的数量。 启动哨兵
执行 redis-sentinel /myRedis/sentinel.conf 5、故障恢复 七、Redis 集群 1、集群 Redis 集群 实现了对 Redis 的水平扩容,即启动 N 个 redis 节点,将整个数据库分布式存储在这个 N 个节点中, 每个节点存储总数据的 1/N。 Redis 集群通过分区(partiton)来提供一定程度的可用性(availability ): 即使集群中有部分节点失效或者无法进行通信,集群也可以继续处理命令请求。 2、安装 ruby 环境 ① 能上网
执行yum install ruby 执行 yum install rubygems ② 不能上网
cd /run/media/root/CentOS 7 x86_64/Packages 获取如下 rpm 包
==注意 ==:
因为 redis 集群需要使用 2.3.0 及以上版本,而 Linux 系统自带的是 2.0.0,需要更新一下,才能启动
方法:换 yum 源安装
yum install centos-release-scl-rh //会在/etc/yum.repos.d/目录下多出一个CentOS-SCLo-scl-rh.repo源
yum install rh-ruby23 -y //直接yum安装即可
scl enable rh-ruby23 bash //必要一步
ruby -v //查看安装版本
使用:gem install redis
3、准备 6 个 Redis 实例 准备 6 个实例 6379,6380,6381,6389,6390,6391 拷贝多个 redis.conf 文件
开启 daemonize yes
Pid 文件名字
指定端口
Log 文件名字
Dump.rdb 名字
Appendonly 关掉或者换名字
再加入如下配置
cluster-enabled yes 打开集群模式
cluster-config-file nodes-端口号.conf 设定节点配置文件名
cluster-node-timeout 15000 设定节点失联时间,超过该时间(毫秒),集群自动进行
主从切换
include /opt/myRedis/redis.conf
pidfile /var/run/redis6379.pid
port 6379
dbfilename dump6379.rdb
cluster-enabled yes
cluster-config-file nodes-6379.conf
cluster-node-timeout 15000
4、合体 将 6 个实例全部启动, nodes-端口号.conf 文件都生成正常 2.合体
进入到 cd /opt/redis-3.2.5/src 执行 ./redis-trib.rb create –replicas 1 192.168.64.129:6379 192.168.64.129:6380 192.168.64.129:6381 192.168.64.129:6389 192.168.64.129:6390 192.168.64.129:6391
==注意: IP 地址修改为当前服务器的地址,端口号为每个 Redis 实例对应的端口号==
5、集群操作 以集群的方式进入客户端 通过 cluster nodes 命令查看集群信息 redis cluster 如何分配这六个节点。一个集群至少要有三个主节点。选项 –replicas 1 表示我们希望为集群中的每个主节点创建一个从节点。分配原则尽量保证每个主数据库运行在不同的 IP 地址,每个从库和主库不在一个 IP 地址上。slots :一个 Redis 集群包含 16384 个插槽( hash slot), 数据库中的每个键都属于这 16384 个插槽的其中一个, 集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽, 其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。集群中的每个节点负责处理一部分插槽。 举个例子, 如果一个集群可以有主节点,
其中:节点 A 负责处理 0 号至 5500 号插槽。 节点 B 负责处理 5501 号至 11000 号插槽。 节点 C 负责处理 11001 号至 16383 号插槽 在集群中录入值 在 redis-cli 每次录入、查询键值, redis 都会计算出该 key 应该送往的插槽,如果不是该客户端对应服务器的插槽, redis 会报错,并告知应前往的 redis 实例地址和端口. redis-cli 客户端提供了 – c 参数实现自动重定向。如 redis-cli -c – p 6379 登入后,再录入、查询键值对可以自动重定向。 不在一个 slot 下的键值,是不能使用 mget,mset 等多键操作。 可以通过{}来定义组的概念,从而使 key 中{}内相同内容的键值对放到一个 slot 中去 。 不在一个 slot 下的键值,是不能使用 mget,mset 等多键操作。 可以通过{}来定义组的概念,从而使 key 中{}内相同内容的键值对放到一个 slot 中去 。 查询集群中的值 CLUSTER KEYSLOT <key> 计算键 key 应该被放置在哪个槽上。 CLUSTER COUNTKEYSINSLOT <slot> 返回槽 slot 目前包含的键值对数量 CLUSTER GETKEYSINSLOT <slot> <count> 返回 count 个 slot 槽中的键 6、集群的 Jedis 开发 public class JedisClusterTest {
public static void main(String[] args) {
Set<HostAndPort> set =new HashSet<HostAndPort>();
set.add(new HostAndPort("192.168.64.129",6379));
JedisCluster jedisCluster=new JedisCluster(set);
jedisCluster.set("k1", "v1");
System.out.println(jedisCluster.get("k1"));
}
}
7、Redis 集群的优缺点 实现扩容 分摊压力 无中心配置相对简单 多键操作是不被支持的 多键的 Redis 事务是不被支持的。 lua 脚本不被支持。 由于集群方案出现较晚,很多公司已经采用了其他的集群方案,而代理或者客户端分片 的方案想要迁移至 redis cluster,需要整体迁移而不是逐步过渡,复杂度较大。