0 介绍
Redis是一个开源的key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,Redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
Redis官方网址http://Redis.io,中文官方网址http://www.Redis.net.cn/
1 安装
- 下载redis安装包并解压
# tar –zxvf redis-3.2.5.tar.gz
2) 进入解压后的目录,执行make命令
如果执行make报错,则说明未安装gcc。联网状态下可以通过yum install gcc++进行安装;如果未联网则参考我的另一篇文章《离线安装gcc和gcc++》
make执行成功可以看到如下页面:
3) 继续执行 make install
4)查看默认安装目录/usr/local/bin
Redis-benchmark:性能测试工具,可以在自己本地运行,看看自己本地性能如何(服务启动起来后执行)
Redis-check-aof:修复有问题的AOF文件
Redis-check-dump:修复有问题的dump.rdb文件
Redis-sentinel:Redis集群使用
redis-server:Redis服务器启动命令
redis-cli:客户端,操作入口
4) 启动
备份redis.conf:拷贝一份redis.conf到其他目录
修改redis.conf文件将里面的daemonize no 改成 yes(128行),让服务在后台启动
启动命令:执行
redis-server /root/dara/redis/conf/redis.conf
用客户端访问: redis-cli
多个端口可以 redis-cli –p 6379
测试验证: ping
到此redis组件就安装成功且正常启动了
2 常用命令
1)关闭
单实例关闭:
redis-cli shutdown
或在终端直接执行shutdown
多实例关闭,指定端口关闭:redis-cli -p 6379 shutdown
- key
key + list/string/set/hash/zset
相关命令:
keys * 查询数据库当前所有键值
exists <key> 判断某个键是否存在
type <key> 查看键对应的数据类型
del <key> 删除某个键
expire <key> <seconds>为键值设置过期时间,单位秒
dbsize 查看当前数据库的key数量
flushdb清空当前库
3 五大数据类型
3.1 String
String是Redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value。
String类型是二进制安全的。意味着Redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。
String类型是Redis最基本的数据类型,一个Redis中字符串value最多可以是512M
相关命令:
get <key> 查询对应键值
set <key> <value> 添加键值对
append <key> <value> 将给定的<value> 追加到原值的末尾
strlen <key> 获得值的长度
setnx <key> <value> 只有在 key 不存在时设置 key 的值
incr <key> 将 key 中储存的数字值增1; 只能对数字值操作,如果为空,新增值为1
decr <key> 将 key 中储存的数字值减1; 只能对数字值操作,如果为空,新增值为-1
incrby / decrby <key> <步长> 将 key 中储存的数字值增减。自定义步长。
mset <key1> <value1> <key2> <value2> .....
同时设置一个或多个 key-value对
mget <key1> <key2> <key3> .....
同时获取一个或多个 value
msetnx <key1> <value1> <key2> <value2> .....
同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。
getrange <key> <起始位置> <结束位置>
获得值的范围,类似java中的substring
setrange <key> <起始位置> <value>
用 覆写 所储存的字符串值,从<起始位置>开始
setex <key> <过期时间> <value>
设置键值的同时,设置过期时间,单位秒
ttl
在这里插入代码片
查看还有多少秒过期,-1表示永不过期,-2表示已过期
getset <key> <value>
以新换旧,设置了新值同时获得旧值。
3.2 List
单键多值
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
它的底层实际是个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差。
相关命令:
lpush/rpush <key> <value1> <value2> <value3> ....
从左边/右边插入一个或多个值。
lpop/rpop <key>
从左边/右边吐出一个值。
值在键在,值亡键亡。
rpoplpush <key1> <key2>
从列表右边吐出一个值,插到列表左边
lrange <key> <start> <stop>
按照索引下标获得元素(从左到右)
lindex <key> <index>
按照索引下标获得元素(从左到右)
llen <key>
获得列表长度
linsert before
在这里插入代码片
在的前面插入
lrem <key> <n> <value>
从左边删除n个value(从左到右)
3.3 Set
Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动去重,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。
Redis的Set是string类型的无序集合。它底层其实是一个value为null的hash表,所以添加,删除,查找的复杂度都是O(1)。
相关命令:
sadd <key> <value1> <value2> .....
将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略
smembers <key> 取出该集合的所有值
sismember <key> <value>
判断集合是否为含有该值,有返回1,没有返回0
scard <key> 返回该集合的元素个数
srem <key> <value1> <value2> .... 删除集合中的某个元素
spop <key> <n> 随机从该集合中吐出一个或多个值
srandmember <key> <n> 随机从该集合中取出n个值。不会从集合中删除
sinter <key1> <key2> 返回两个集合的交集元素
sunion <key1> <key2> 返回两个集合的并集元素
sdiff <key1> <key2> 返回两个集合的差集元素
3.4 hash
Redis hash 是一个键值对集合。
Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。
类似Java里面的Map<String,Object>
相关命令:
hset <key> <field> <value> 给<key>集合中的 <field>键赋值<value>
hget <key> <field> 从<key>集合<field> 取出 value
hmset <key> <field1> <value1> <field2> <value2>... 批量设置hash的值
hexists key <field> 查看哈希表 key 中,给定域 field 是否存在
hkeys <key> 列出该hash集合的所有field
hvals <key> 列出该hash集合的所有value
hincrby <key> <field> <increment>
为哈希表 key 中的域 field 的值加上增量 increment
hsetnx <key> <field> <value>
将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在
3.5 ZSet
Redis有序集合zset与普通集合set非常相似,是一个没有重复元素的字符串集合。不同之处是有序集合的所有成员都关联了一个评分(score) ,这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的,但是评分可以重复。
因为元素是有序的, 所以可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。访问有序集合的中间元素也是非常快的,因此能够使用有序集合作为一个没有重复成员的智能列表。
相关命令:
zadd <key> <score1> <value1> <score2> <value2>...
将一个或多个 member 元素及其 score 值加入到有序集 key 当中
zrange <key> <start> <stop> [WITHSCORES]
返回有序集 key 中,下标在
带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>
为元素的score加上增量
zrem <key> <value>
删除该集合下,指定值的元素
zcount <key> <min> <max>
统计该集合,分数区间内的元素个数
zrank <key> <value>
返回该值在集合中的排名,从0开始。
4 Java的Redis客户端Jedis
4.1 所需jar包
Commons-pool-1.6.jar
Jedis-2.1.0.jar
4.2 windows环境下连接redis注意事项
禁用Linux的防火墙:
临时禁用:service iptables stop
关闭开机自启:chkconfig iptables off
redis.conf中注释掉bind 127.0.0.1(61行) ,然后 protect-mode(80行)设置为 no。
4.3 测试redis连通性
package com.redis;
import redis.clients.jedis.Jedis;
public class JredisDemo {
public static void main(String[] args) {
Jedis jedis = new Jedis("192.168.199.101", 6379);
System.out.println(jedis.ping());
}
}
输出PONG则表示连接成功
4.4 API:keys
Set<String> keys = jedis.keys("*");
for(Iterator iterator=keys.iterator();iterator.hasNext();){
String key = (String) iterator.next();
System.out.println(key);
}
4.5 API:String
//获取和设置String
System.out.println(jedis.get("a"));
jedis.set("b","1");
System.out.println("-------------------------------");
jedis.mset("c","2","d","3","e","4");
System.out.println(jedis.mget("b","c","d"));
4.6 API: List
List<String> list = jedis.lrange("k1", 0, 3);
for (String s : list) {
System.out.println(s);
}
4.7 API:Set
//向Set中写入数据
jedis.sadd("set1","bb","cc");
Set<String> sets = jedis.smembers("set1");
for (String set : sets) {
System.out.println(set);
}
//判断Set中某个值是否存在
System.out.println(jedis.sismember("set1","d"));
System.out.println(jedis.sismember("set1","x"));
4.8 API:hash
//向hash中插入值
jedis.hset("hash3","name","wangwu");
jedis.hset("hash3","age","26");
//批量传入hash值
Map<String,String> map =new HashMap();
map.put("name","lisi");
map.put("age","26");
map.put("sex","male");
jedis.hmset("hash5",map);
//取出hash中的值
System.out.println(jedis.hget("hash3","name"));
//判断某个值是否存在
System.out.println(jedis.hexists("hash5","name"));
System.out.println(jedis.hexists("hash5","salary"));
4.9 API:ZSet
//获取Zset
Set<String> zsets = jedis.zrange("zset1", 0, 4);
for (String zset : zsets) {
System.out.println(zset);
}
System.out.println("-------------------------------------------------");
//向zset中加入数据
jedis.zadd("zsei1",5.5,"f");
jedis.zadd("zsei1",9.5,"g");
//根据score值取出zset数据并遍历
Set<String> zsets1 = jedis.zrangeByScore("zset1", 5, 9);
for (String s : zsets1) {
System.out.println(s);
}
5 Redis事务
Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断
Redis事务的主要作用就是串联多个命令防止别的命令插队
5.1 Multi、Exec、discard
从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,至到输入Exec后,Redis会将之前的命令队列中的命令依次执行
5.2 事务的错误处理
- 组队中某个命令出现了报告错误,执行时整个的所有队列会都会被取消
2) 如果执行阶段某个命令报出了错误,则只有报错的命令不会被执行,而其他的命令都会执行,不会回滚。
5.3 事务冲突
1) 悲观锁
悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
2) 乐观锁
乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的。
WATCH key [key …]
在执行multi之前,先执行watch key1 [key2],可以监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。(加watch 别人动过,就执行失败)
5.4 三个特性
单独的隔离操作
事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
没有隔离级别的概念
队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行,也就不存在“事务内的查询要看到事务里的更新,在事务外查询不能看到”这个让人万分头痛的问题
不保证原子性
Redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚
6 Redis持久化
Redis 提供了2个不同形式的持久化方式。
• RDB (Redis DataBase)
• AOF (Append Of File)
6.1 RDB
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是Snapshot快照,它恢复时是将快照文件直接读到内存里。
6.1.1备份是如何执行的
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。
在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,Linux中引入了“写时复制技术”,一般情况父进程和子进程会共用同一段物理内存,只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。
6.1.2 RDB保存的文件
在redis.conf中配置文件名称,默认为dump.rdb
rdb文件的保存路径,也可以修改。默认为Redis启动时命令行所在的目录下
rdb的保存策略
手动保存快照
• 命令save: 只管保存,其它不管,全部阻塞
• save vs bgsave
stop-writes-on-bgsave-error yes
• 当Redis无法写入磁盘的话,直接关掉Redis的写操作
rdbcompression yes
• 进行rdb保存时,将文件压缩
rdbchecksum yes
• 在存储快照后,还可以让Redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能
6.1.3 RDB的备份
- 先通过config get dir 查询rdb文件的目录
- 将*.rdb的文件拷贝到别的地方
6.1.4 RDB恢复
- 关闭Redis
- 先把备份的文件拷贝到工作目录下
- 启动Redis, 备份数据会直接加载
6.1.5 RDB优缺点
• 节省磁盘空间
• 恢复速度快
• 虽然Redis在fork时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能。
• 在备份周期在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改。
6.2 AOF
以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,Redis启动之初会读取该文件重新构建数据,换言之,Redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
- AOF默认不开启,需要手动在配置文件中配置
- 可以在redis.conf中配置文件名称,默认为 appendonly.aof
- AOF文件的保存路径,同RDB的路径一致
6.2.1 AOF文件故障备份
1)AOF的备份机制和性能虽然和RDB不同, 但是备份和恢复的操作同RDB一样,都是拷贝备份文件,需要恢复时再拷贝到Redis工作目录下,启动系统即加载
2)AOF和RDB同时开启,系统默认取AOF的数据
6.2.2 AOF文件故障恢复
- AOF文件的保存路径,同RDB的路径一致。
2)如遇到AOF文件损坏,可通过redis-check-aof --fix appendonly.aof 进行恢复
6.2.3 AOF同步频率设置
1) 始终同步,每次Redis的写入都会立刻记入日志
2) 每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失。
3) 把不主动进行同步,把同步时机交给操作系统。
6.2.4 Rewrite
AOF采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集.可以使用命令bgrewriteaof。
6.2.5 AOF重写实现
AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再rename),遍历新进程的内存中数据,每条记录有一条的Set语句。重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。
重写虽然可以节约大量磁盘空间,减少恢复时间。但是每次重写还是有一定的负担的,因此设定Redis要满足一定条件才会进行重写。
系统载入时或者上次重写完毕时,Redis会记录此时AOF大小,设为base_size,如果Redis的AOF当前大小>= base_size +base_size*100% (默认)且当前大小>=64mb(默认)的情况下,Redis会对AOF进行重写。
6.2.6 AOF优缺点
备份机制更稳健,丢失数据概率更低
可读的日志文本,通过操作AOF稳健,可以处理误操作
比起RDB占用更多的磁盘空间。
恢复备份速度要慢。
每次读写都同步的话,有一定的性能压力。
存在个别Bug,造成恢复不能。
6.3 RDB与AOF选择
• 官方推荐两个都启用
• 如果对数据不敏感,可以选单独用RDB。
• 不建议单独用 AOF,因为可能会出现Bug。
• 如果只是做纯内存缓存,可以都不用。
7 Redis主从复制
主从复制,就是主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主
作用:
• 读写分离,性能扩展
• 容灾快速恢复
7.1 复制原理
- 每次从机联通后,都会给主机发送sync指令
- 主机立刻进行存盘操作,发送RDB文件,给从机
- 从机收到RDB文件后,进行全盘加载
- 之后每次主机的写操作,都会立刻发送给从机,从机执行相同的命令
7.2薪火相传
1) 上一个slave可以是下一个slave的Master,slave同样可以接收其他slaves的连接和同步请求,那么该slave作为了链条中下一个的master, 可以有效减轻master的写压力,去中心化降低风险。
2) 用 slaveof
3) 中途变更转向:会清除之前的数据,重新建立拷贝最新的
4) 风险是一旦某个slave宕机,后面的slave都没法备份
7.3 反客为主
当一个master宕机后,后面的slave可以立刻升为master,其后面的slave不用做任何修改。
用 slaveof no one 将从机变为主机。
7.4 哨兵模式(sentinel)
反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库.
7.4.1 配置哨兵
• 调整为一主二仆模式
• 自定义的/myredis目录下新建sentinel.conf文件
• 在配置文件中填写内容:
sentinel monitor mymaster 127.0.0.1 6379 1
• 其中mymaster为监控对象起的服务器名称, 1 为至少有多少个哨兵同意迁移的数量。
启动哨兵
• 执行redis-sentinel /myredis/sentinel.conf
7.5 故障恢复
8 Redis集群
Redis 集群实现了对Redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的1/N。
Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。