redis的安装及使用 - wtdig/study GitHub Wiki
1、下载redis到指定服务器目录 redis官网
wget http://download.redis.io/releases/redis-4.0.9.tar.gz
2、到下载的目录,进行解压
tar -zxvf 指定redis文件
3、到指定目录下进行make操作
输入make回车即可
4、进行安装
make PREFIX=/uer/local/redis install 其中PREFIX=/uer/local/redis为指定的安装路径
5、进行安装目录的redis文件中,这时,只有一个bin目录,在源码中负责一份redis.conf的文件,将该配置文件里面的daemonize no改成 daemonize yes即可(可以进行后台运行)
6、redis服务端的启动
到安装的redis目录下 输入 ./bin/redis-server ./redis.conf 即可启动
7、redis客户端的启动
到安装的redis目录下 输入 ./bin/redis.cli 即可,指定端口启动: ./bin/redis.cli -p 端口号
8、redis服务的关闭
pkill -9 redis
二、redis的指令 参考指令
注意:redis默认有16个数据库,从0-15进行编号,默认使用0号数据库
可以使用select 指定序号进行切换 select 1
可以将0号库的key移动到1号库 move 指定的key 指定的数据库 ,比如: move age 1,0号库的age没有了,跑到1号库了
1、 查询存储的所有key
keys * keys * 之间有个空格
2、返回key的类型
type 指定的key,返回key存储的类型
3、是否存在key
exists 指定的key,存在返回1,不存在返回0
4、删除一个key
del 指定的key,删除成功返回1
5、将一个key改成另一个名字
remane 指定的旧的key 指定新的key,成功返回ok
renamex 指定的旧的key 指定新的key
区别:带x的,如果新的key存在,不会进行修改
6、设置一个key
set 指定key的名称 key的值
7、获取key的值
get 指定的key
8、查询key的生命周期
ttl 指定的key,已过期的key/不过期的key,都返回-1,不存在的key,返回-2
9、设置key的生命周期
expire 指定的key 指定过期的秒数 expire age 10
10秒之后该key就不存在了
如果需要设置毫秒 pexpire进行设置,使用pttl进行查询
10、设置key永久有效
persist 指定key
11、 randomkey
随机返回一个key
12、flushdb 清空数据
1、set key value [ex 秒数] / [px 毫秒数] [nx] /[xx]
如: set a 1 ex 10 , 10秒有效
Set a 1 px 9000 , 9秒有效
注: 如果ex,px同时写,以后面的有效期为准
如 set a 1 ex 100 px 9000, 实际有效期是9000毫秒
nx: 表示key不存在时,执行操作
xx: 表示key存在时,执行操作
2、mset multi set , 一次性设置多个键值
例: mset key1 v1 key2 v2 ....
get key
作用:获取key的值
3、mget key1 key2 ..keyn
作用:获取多个key的值
4、setrange key offset value
作用:把字符串的offset偏移字节,改成value
redis 127.0.0.1:6379> set greet hello
OK
redis 127.0.0.1:6379> setrange greet 2 x
(integer) 5
redis 127.0.0.1:6379> get greet
"hexlo"
注意: 如果偏移量>字符长度, 该字符自动补0x00
redis 127.0.0.1:6379> setrange greet 6 !
(integer) 7
redis 127.0.0.1:6379> get greet
"heyyo\x00!"
5、append key value
作用: 把value追加到key的原值上
6、getrange key start stop
作用: 是获取字符串中 [start, stop]范围的值
注意: 对于字符串的下标,左数从0开始,右数从-1开始
redis 127.0.0.1:6379> set title 'chinese'
OK
redis 127.0.0.1:6379> getrange title 0 3
"chin"
redis 127.0.0.1:6379> getrange title 1 -2
"hines"
注意:
1: start>=length, 则返回空字符串
2: stop>=length,则截取至字符结尾
3: 如果start 所处位置在stop右边, 返回空字符串
7、getset key newvalue
作用: 获取并返回旧值,设置新值
redis 127.0.0.1:6379> set cnt 0
OK
redis 127.0.0.1:6379> getset cnt 1
"0"
redis 127.0.0.1:6379> getset cnt 2
"1"
8、incr key
作用: 指定的key的值加1,并返回加1后的值
注意:
1:不存在的key当成0,再incr操作
2: 范围为64有符号
9、incrby key number
redis 127.0.0.1:6379> incrby age 90
(integer) 92
10、incrbyfloat key floatnumber
redis 127.0.0.1:6379> incrbyfloat age 3.5
"95.5"
11、decr key
redis 127.0.0.1:6379> set age 20
OK
redis 127.0.0.1:6379> decr age
(integer) 19
12、decrby key number
redis 127.0.0.1:6379> decrby age 3
(integer) 16
13、getbit key offset
作用:获取值的二进制表示,对应位上的值(从左,从0编号)
redis 127.0.0.1:6379> set char A
OK
redis 127.0.0.1:6379> getbit char 1
(integer) 1
redis 127.0.0.1:6379> getbit char 2
(integer) 0
redis 127.0.0.1:6379> getbit char 7
(integer) 1
14、setbit key offset value
设置offset对应二进制位上的值
返回: 该位上的旧值
注意:
1:如果offset过大,则会在中间填充0,
2: offset最大大到多少
3:offset最大2^32-1,可推出最大的的字符串为512M
15、bitop operation destkey key1 [key2 ...]
对key1,key2..keyN作operation,并将结果保存到 destkey 上。
operation 可以是 AND 、 OR 、 NOT 、 XOR
redis 127.0.0.1:6379> setbit lower 7 0
(integer) 0
redis 127.0.0.1:6379> setbit lower 2 1
(integer) 0
redis 127.0.0.1:6379> get lower
" "
redis 127.0.0.1:6379> set char Q
OK
redis 127.0.0.1:6379> get char
"Q"
redis 127.0.0.1:6379> bitop or char char lower
(integer) 1
redis 127.0.0.1:6379> get char
"q"
注意: 对于NOT操作, key不能多个
1、 lpush key value
作用: 把值插入到链接头部
2、 rpop key
作用: 返回并删除链表尾元素
rpush,lpop: 不解释
3、lrange key start stop
作用: 返回链表中[start ,stop]中的元素 查看全部 lrange key 0 -1
规律: 左数从0开始,右数从-1开始
4、lrem key count value
作用: 从key链表中删除 value值
注: 删除count的绝对值个value后结束
Count>0 从表头删除
Count<0 从表尾删除
5、ltrim key start stop
作用: 剪切key对应的链接,切[start,stop]一段,并把该段重新赋给key
6、lindex key index
作用: 返回index索引上的值,
如 lindex key 2
7、llen key
作用:计算链接表的元素个数
redis 127.0.0.1:6379> llen task
(integer) 3
redis 127.0.0.1:6379>
8、linsert key after|before search value
作用: 在key链表中寻找’search’,并在search值之前|之后,.插入value
注: 一旦找到一个search后,命令就结束了,因此不会插入多个value
9、rpoplpush source dest
作用: 把source的尾部拿出,放在dest的头部,
并返回 该单元值
场景: task + bak 双链表完成安全队列
Task列表 bak列表
业务逻辑:
1:Rpoplpush task bak
2:接收返回值,并做业务处理
3:如果成功,rpop bak 清除任务. 如不成功,下次从bak表里取任务
10、brpop ,blpop key timeout
作用:等待弹出key的尾/头元素,
Timeout为等待超时时间
如果timeout为0,则一直等待
场景: 长轮询Ajax,在线聊天时,能够用到
集合的性质: 唯一性,无序性,确定性
注: 在string和link的命令中,可以通过range 来访问string中的某几个字符或某几个元素 但,因为集合的无序性,无法通过下标或范围来访问部分元素.
因此想看元素,要么随机先一个,要么全选
1、sadd key value1 value2
作用: 往集合key中增加元素
2、srem value1 value2
作用: 删除集合中集为 value1 value2的元素
返回值: 忽略不存在的元素后,真正删除掉的元素的个数
3、spop key
作用: 返回并删除集合中key中1个随机元素
随机--体现了无序性
4、srandmember key
作用: 返回集合key中,随机的1个元素.
5、sismember key value
作用: 判断value是否在key集合中
是返回1,否返回0
6、smembers key
作用: 返回集中中所有的元素
7、scard key
作用: 返回集合中元素的个数
8、smove source dest value
作用:把source中的value删除,并添加到dest集合中
9、sinter key1 key2 key3
作用: 求出key1 key2 key3 三个集合中的交集,并返回
redis 127.0.0.1:6379> sadd s1 0 2 4 6
(integer) 4
redis 127.0.0.1:6379> sadd s2 1 2 3 4
(integer) 4
redis 127.0.0.1:6379> sadd s3 4 8 9 12
(integer) 4
redis 127.0.0.1:6379> sinter s1 s2 s3
1) "4"
redis 127.0.0.1:6379> sinter s3 s1 s2
1)"4"
10、sinterstore dest key1 key2 key3
作用: 求出key1 key2 key3 三个集合中的交集,并赋给dest
11、sunion key1 key2.. Keyn
作用: 求出key1 key2 keyn的并集,并返回
12、sdiff key1 key2 key3
作用: 求出key1与key2 key3的差集
即key1-key2-key3
1、zadd key score1 value1 score2 value2 ..
添加元素
redis 127.0.0.1:6379> zadd stu 18 lily 19 hmm 20 lilei 21 lilei (integer) 3
2、zrem key value1 value2 ..
作用: 删除集合中的元素
3、zremrangebyscore key min max
作用: 按照socre来删除元素,删除score在[min,max]之间的
redis 127.0.0.1:6379> zremrangebyscore stu 4 10
(integer) 2
redis 127.0.0.1:6379> zrange stu 0 -1
1) "f"
4、zremrangebyrank key start end
作用: 按排名删除元素,删除名次在[start,end]之间的
redis 127.0.0.1:6379> zremrangebyrank stu 0 1
(integer) 2
redis 127.0.0.1:6379> zrange stu 0 -1
1) "c"
2) "e"
3) "f"
4) "g"
5、zrank key member
查询member的排名(升续 0名开始)
6、zrevrank key memeber
查询 member的排名(降续 0名开始)
7、zrange key start stop [WITHSCORES]
把集合排序后,返回名次[start,stop]的元素
默认是升续排列
Withscores 是把score也打印出来
8、zrevrange key start stop
作用:把集合降序排列,取名字[start,stop]之间的元素
9、zrangebyscore key min max [withscores] limit offset N
作用: 集合(升续)排序后,取score在[min,max]内的元素, 并跳过 offset个, 取出N个
redis 127.0.0.1:6379> zadd stu 1 a 3 b 4 c 9 e 12 f 15 g
(integer) 6
redis 127.0.0.1:6379> zrangebyscore stu 3 12 limit 1 2 withscores
1) "c"
2) "4"
3) "e"
4) "9"
10、zcard key
返回元素个数
11、zcount key min max
返回[min,max] 区间内元素的数量
12、zinterstore destination numkeys key1 [key2 ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
求key1,key2的交集,key1,key2的权重分别是 weight1,weight2
聚合方法用: sum |min|max
聚合的结果,保存在dest集合内
注意: weights ,aggregate如何理解?
答: 如果有交集, 交集元素又有socre,score怎么处理?
Aggregate sum->score相加 , min 求最小score, max 最大score
另: 可以通过weigth设置不同key的权重, 交集时,socre * weights
详见下例
redis 127.0.0.1:6379> zadd z1 2 a 3 b 4 c
(integer) 3
redis 127.0.0.1:6379> zadd z2 2.5 a 1 b 8 d
(integer) 3
redis 127.0.0.1:6379> zinterstore tmp 2 z1 z2
(integer) 2
redis 127.0.0.1:6379> zrange tmp 0 -1
1) "b"
2) "a"
redis 127.0.0.1:6379> zrange tmp 0 -1 withscores
1) "b"
2) "4"
3) "a"
4) "4.5"
redis 127.0.0.1:6379> zinterstore tmp 2 z1 z2 aggregate sum
(integer) 2
redis 127.0.0.1:6379> zrange tmp 0 -1 withscores
1) "b"
2) "4"
3) "a"
4) "4.5"
redis 127.0.0.1:6379> zinterstore tmp 2 z1 z2 aggregate min
(integer) 2
redis 127.0.0.1:6379> zrange tmp 0 -1 withscores
1) "b"
2) "1"
3) "a"
4) "2"
redis 127.0.0.1:6379> zinterstore tmp 2 z1 z2 weights 1 2
(integer) 2
redis 127.0.0.1:6379> zrange tmp 0 -1 withscores
1) "b"
2) "5"
3) "a"
4) "7"
1、hset key field value
作用: 把key中 filed域的值设为value
注:如果没有field域,直接添加,如果有,则覆盖原field域的值
2、hmset key field1 value1 [field2 value2 field3 value3 ......fieldn valuen]
作用: 设置field1->N 个域, 对应的值是value1->N
(对应PHP理解为 $key = array(file1=>value1, field2=>value2 ....fieldN=>valueN))
3、hget key field
作用: 返回key中field域的值
4、hmget key field1 field2 fieldN
作用: 返回key中field1 field2 fieldN域的值
5、hgetall key
作用:返回key中,所有域与其值
6、hdel key field
作用: 删除key中 field域
7、hlen key
作用: 返回key中元素的数量
8、hexists key field
作用: 判断key中有没有field域
9、hinrby key field value
作用: 是把key中的field域的值增长整型值value
10、hinrbyfloat key field value
作用: 是把key中的field域的值增长浮点值value
11、hkeys key
作用: 返回key中所有的field
12、hvals key
作用: 返回key中所有的value
Redis与 mysql事务的对比
Mysql Redis
开启 start transaction multi
语句 普通sql 普通命令
失败 rollback 回滚 discard 取消
成功 commit exec
注: rollback与discard 的区别
如果已经成功执行了2条语句, 第3条语句出错.
Rollback后,前2条的语句影响消失.
Discard只是结束本次事务,前2条语句造成的影响仍然还在
注:
在multi后面的语句中, 语句出错可能有2种情况
1: 语法就有问题,
这种,exec时,报错, 所有语句得不到执行
2: 语法本身没错,但适用对象有问题. 比如 zadd 操作list对象
Exec之后,会执行正确的语句,并跳过有不适当的语句.
(如果zadd操作list这种事怎么避免? 这一点,由程序员负责)
思考:
我正在买票
Ticket -1 , money -100
而票只有1张, 如果在我multi之后,和exec之前, 票被别人买了---即ticket变成0了.
我该如何观察这种情景,并不再提交
悲观的想法:
世界充满危险,肯定有人和我抢, 给 ticket上锁, 只有我能操作. [悲观锁]
乐观的想法:
没有那么人和我抢,因此,我只需要注意,
--有没有人更改ticket的值就可以了 [乐观锁]
Redis的事务中,启用的是乐观锁,只负责监测key没有被改动.
具体的命令---- watch命令
例:
redis 127.0.0.1:6379> watch ticket
OK
redis 127.0.0.1:6379> multi
OK
redis 127.0.0.1:6379> decr ticket
QUEUED
redis 127.0.0.1:6379> decrby money 100
QUEUED
redis 127.0.0.1:6379> exec
(nil) // 返回nil,说明监视的ticket已经改变了,事务就取消了.
redis 127.0.0.1:6379> get ticket
"0"
redis 127.0.0.1:6379> get money
"200"
watch key1 key2 ... keyN
作用:监听key1 key2..keyN有没有变化,如果有变, 则事务取消
unwatch
作用: 取消所有watch监听
使用办法:
订阅端: subscribe 频道名称 ; psubscribe 频道名称的正则,订阅配置该频道名称正则的所有频道;例如:psubscribe new*
发布端: publish 频道名称 发布内容
客户端例子:
redis 127.0.0.1:6379> subscribe news
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "news"
3) (integer) 1
1) "message"
2) "news"
3) "good good study"
1) "message"
2) "news"
3) "day day up"
服务端例子:
redis 127.0.0.1:6379> publish news 'good good study'
(integer) 1
redis 127.0.0.1:6379> publish news 'day day up'
(integer) 1
Redis的持久化有2种方式 1快照 2是日志
Rdb快照的配置选项
save 900 1 // 900内,有1条写入,则产生快照
save 300 1000 // 如果300秒内有1000次写入,则产生快照
save 60 10000 // 如果60秒内有10000次写入,则产生快照
(这3个选项都屏蔽,则rdb禁用)
stop-writes-on-bgsave-error yes // 后台备份进程出错时,主进程停不停止写入?
rdbcompression yes // 导出的rdb文件是否压缩
Rdbchecksum yes // 导入rbd恢复时数据时,要不要检验rdb的完整性
dbfilename dump.rdb //导出来的rdb文件名
dir ./ //rdb的放置路径
Aof 的配置
appendonly no # 是否打开 aof日志功能
appendfsync always # 每1个命令,都立即同步到aof. 安全,速度慢
appendfsync everysec # 折衷方案,每秒写1次
appendfsync no # 写入工作交给操作系统,由操作系统判断缓冲区大小,统一写入到aof. 同步频率低,速度快,
no-appendfsync-on-rewrite yes: # 正在导出rdb快照的过程中,要不要停止同步aof
auto-aof-rewrite-percentage 100 #aof文件大小比起上次重写时的大小,增长率100%时,重写
auto-aof-rewrite-min-size 64mb #aof文件,至少超过64M时,重写
注意:修改配置文件后,要先杀死redis: pkill -9 redis ,在启动服务,不然appendonly.aof文件可能没有
注: 在dump rdb过程中,aof如果停止同步,会不会丢失?
答: 不会,所有的操作缓存在内存的队列里, dump完成后,统一操作.
注: aof重写是指什么?
答: aof重写是指把内存中的数据,逆化成命令,写入到.aof日志里.
以解决 aof日志过大的问题.
问: 如果rdb文件,和aof文件都存在,优先用谁来恢复数据?
答: aof
问: 2种是否可以同时用?
答: 可以,而且推荐这么做
问: 恢复时rdb和aof哪个恢复的快
答: rdb快,因为其是数据的内存映射,直接载入到内存,而aof是命令,需要逐条执行
主从备份:
redis进行主从备份时,主服务器发现有数据变化,将rdb的数据同步到从服务器,在此期间,如果还有新的数据变化,先存储在aof文件中,等待rdb数据同步完成,再将aof的数据同步到从服务器。
主从的配置修改:
port 6380 端口修改成指定端口;
pidfile /var/run/redis_6380.pid pid修改成指定pid;
slaveof 45.78.9.159 6379 指定主服务器的地址和端口号;
masterauth root 需要设置密码,可以设定指定密码;
启动客户端可以通过-p指定端口启动
redis 服务器端命令
redis 127.0.0.1:6380> time ,显示服务器时间 , 时间戳(秒), 微秒数
1) "1375270361"
2) "504511"
redis 127.0.0.1:6380> dbsize // 当前数据库的key的数量
(integer) 2
redis 127.0.0.1:6380> select 2
OK
redis 127.0.0.1:6380[2]> dbsize
(integer) 0
redis 127.0.0.1:6380[2]>
BGREWRITEAOF 后台进程重写AOF
BGSAVE 后台保存rdb快照
SAVE 保存rdb快照
LASTSAVE 上次保存时间
Slaveof master-Host port , 把当前实例设为master的slave
Flushall 清空所有库所有键
Flushdb 清空当前库所有键
Showdown [save/nosave]
注: 如果不小心运行了flushall, 立即 shutdown nosave ,关闭服务器
然后 手工编辑aof文件, 去掉文件中的 “flushall ”相关行, 然后开启服务器,就可以导入回原来数据.
如果,flushall之后,系统恰好bgrewriteaof了,那么aof就清空了,数据丢失.
Slowlog 显示慢查询
注:多慢才叫慢?
答: 由slowlog-log-slower-than 10000 ,来指定,(单位是微秒)
服务器储存多少条慢查询的记录?
答: 由 slowlog-max-len 128 ,来做限制
Info [Replication/CPU/Memory..]
查看redis服务器的信息
Config get 配置项
Config set 配置项 值 (特殊的选项,不允许用此命令设置,如slave-of, 需要用单独的slaveof命令来设置)
Redis运维时需要注意的参数
1: 内存
# Memory
used_memory:859192 数据结构的空间
used_memory_rss:7634944 实占空间
mem_fragmentation_ratio:8.89 前2者的比例,1.N为佳,如果此值过大,说明redis的内存的碎片化严重,可以导出再导入一次.
2: 主从复制
# Replication
role:slave
master_host:192.168.1.128
master_port:6379
master_link_status:up
3:持久化
# Persistence
rdb_changes_since_last_save:0
rdb_last_save_time:1375224063
4: fork耗时
#Status
latest_fork_usec:936 上次导出rdb快照,持久化花费微秒
注意: 如果某实例有10G内容,导出需要2分钟,
每分钟写入10000次,导致不断的rdb导出,磁盘始处于高IO状态.
5: 慢日志
config get/set slowlog-log-slower-than
CONFIG get/SET slowlog-max-len
slowlog get N 获取慢日志
运行时更改master-slave
修改一台slave(设为A)为new master
1)命令该服务不做其他redis服务的slave
命令: slaveof no one
2)修改其readonly为yes
其他的slave再指向new master A
1)命令该服务为new master A的slave
命令格式 slaveof IP port
1、redis 3.0之前采用主从同步加上哨兵模式,哨兵就是一个脚本,监控redis的节点是否不可用,然后采取替代的措施;
使用案例:6379为master主机,6380、6381为slave为从机
复制一个redis.conf文件,改成redis6380.conf;修改内容如下:
主从的配置修改:
port 6380 端口修改成指定端口;
pidfile /var/run/redis_6380.pid pid修改成指定pid;
slaveof 45.78.9.159 6379 指定主服务器的地址和端口号;
在复制一个redis6381.conf
启动master: ./redis-server redis.conf
启动slave: ./redis-server redis6380.conf
./redis-server redis6381.conf
在redis安装的源目录中复制sentinel.conf到启动的redis中
修改如下文件:
port 26379
dir /tmp
sentinel monitor mymaster 45.78.9.159 6379 1 45.78.9.159监视的主机ip 6379端口 1主要一票就可以选举
sentinel down-after-milliseconds master001 30000
sentinel parallel-syncs master001 1
sentinel failover-timeout master001 180000
配置文件说明:
1. port :当前Sentinel服务运行的端口
2. dir : Sentinel服务运行时使用的临时文件夹
3.sentinel monitor mymaster 45.78.9.159 6379 1:Sentinel去监视一个名为mymaster 的主redis实例,这个主实例的IP地址为本机地址45.78.9.159,端口号为6379,而将这个主实例判断为失效至少需要1个 Sentinel进程的同意,只要同意Sentinel的数量不达标,自动failover就不会执行
4.sentinel down-after-milliseconds mymaster 30000:指定了Sentinel认为Redis实例已经失效所需的毫秒数。当实例超过该时间没有返回PING,或者直接返回错误,那么Sentinel将这个实例标记为主观下线。只有一个 Sentinel进程将实例标记为主观下线并不一定会引起实例的自动故障迁移:只有在足够数量的Sentinel都将一个实例标记为主观下线之后,实例才会被标记为客观下线,这时自动故障迁移才会执行
5.sentinel parallel-syncs mymaster 1:指定了在执行故障转移时,最多可以有多少个从Redis实例在同步新的主实例,在从Redis实例较多的情况下这个数字越小,同步的时间越长,完成故障转移所需的时间就越长
6.sentinel failover-timeout mymaster 180000:如果在该时间(ms)内未能完成failover操作,则认为该failover失败
7.sentinel notification-script <master-name> <script-path>:指定sentinel检测到该监控的redis实例指向的实例异常时,调用的报警脚本。该配置项可选,但是很常用
启动哨兵: ./redis-sentinel sentinel.conf
测试:停掉主机的服务,在主机的服务器上使用: ./redis-cli shutdown
观察哨兵的情况
连接指定端口客户端: ./redis-cli -p 6379
2、redis 3.0之后,提出一个全新的集群方案,去中心化,采用哈希槽进行集群分布式,一般有16384个slot。集群一般需要3台或者以上的主节点,下面的案例是采用3主3从的模式。
1、添加配置文件redis.conf
新建6个文件夹,6380-6385,将redis.conf的配置文件复制进去,进行修改;
port 6380 //端口6380-6385
bind 45.78.9.159 //默认ip为127.0.0.1 需要改为其他节点机器可访问的ip 否则创建集群时无法访问对应的端口,无法创建集群
daemonize yes //redis后台运行
pidfile ./redis_6380.pid //pidfile文件对应6380-6385
cluster-enabled yes //开启集群 把注释#去掉
cluster-config-file nodes_6380.conf //集群的配置 配置文件首次启动自动生成 6380-6385
cluster-node-timeout 15000 //请求超时 默认15秒,可自行设置
appendonly yes
aof的文件名称和rdb的文件名称为了区分也可以进行相应的修改
2、启动着6个redis
./redis-server redis6380.conf
3、安装ruby,redis的集群需要使用ruby
yum -y install ruby ruby-devel rubygems rpm-build
gem install redis
如果ruby版本太低,需要升级:
curl -L get.rvm.io | bash -s stable
source /usr/local/rvm/scripts/rvm
rvm install 2.4.1
再次安装
gem install redis
yum install -y rubygems
Ruby和运行redis-trib.rb需要的环境安装完成了。
4、启动集群
到redis的安装包目录下,一般在src下有redis-trib.rb命令
执行该命令
./redis-trib.rb create --replicas 1 45.78.9.159:6380 45.78.9.159:6381 45.78.9.159:6382 45.78.9.159:6383 45.78.9.159:6384 45.78.9.159:6385
其中1的意思是主从比例1,主3:从3=1
5、连接集群的redis客户端
./redis-cli -c -h 45.78.9.159 -p 6380
6、查看集群信息
连接到客户端之后,
输入cluster info,查看集群信息
输入cluster nodes,查看节点信息
7、redis集群原理
redis cluster在设计的时候,就考虑到了去中心化,去中间件,也就是说,集群中的每个节点都是平等的关系,都是对等的,每个节点都保存各自的数据和整个集群的状态。每个节点都和其他所有节点连接,而且这些连接保持活跃,这样就保证了我们只需要连接集群中的任意一个节点,就可以获取到其他节点的数据。
Redis集群没有并使用传统的一致性哈希来分配数据,而是采用另外一种叫做哈希槽(hash slot)的方式来分配的,一致性哈希对向集群中新增和删除实例的支持很好,但是哈希槽对向集群新增实例或者删除实例的话,需要额外的操作,需要手动的将slot重新平均的分配到新集群的实例中。
redis cluster 默认分配了 16384 个slot,当我们set一个key时,会用CRC16算法来取模得到所属的slot,然后将这个key分到哈希槽区间的节点上,具体算法就是:CRC16(key)%16384。
Redis 集群会把数据存在一个master节点,然后在这个master和其对应的salve之间进行数据同步。当读取数据时,也根据一致性哈希算法到对应的master节点获取数据。只有当一个master 挂掉之后,才会启动一个对应的salve节点,充当master。
需要注意的是:必须要3个或以上的主节点,否则在创建集群时会失败,并且当存活的主节点数小于总节点数的一半时,整个集群就无法提供服务了。
一、原始集群(6节点 3主3从):
(1)启动集群:
[root@bhz004 ~]# /usr/local/redis/bin/redis-server /usr/local/redis-cluster/7001/redis.conf
[root@bhz004 ~]# /usr/local/redis/bin/redis-server /usr/local/redis-cluster/7002/redis.conf
[root@bhz004 ~]# /usr/local/redis/bin/redis-server /usr/local/redis-cluster/7003/redis.conf
[root@bhz004 ~]# /usr/local/redis/bin/redis-server /usr/local/redis-cluster/7004/redis.conf
[root@bhz004 ~]# /usr/local/redis/bin/redis-server /usr/local/redis-cluster/7005/redis.conf
[root@bhz004 ~]# /usr/local/redis/bin/redis-server /usr/local/redis-cluster/7006/redis.conf
(2)查看服务器运行状态:
[root@bhz004 ~]# ps -el | grep redis
5 S 0 1999 1 0 80 0 - 34359 ep_pol ? 00:00:00 redis-server
5 S 0 2003 1 0 80 0 - 34359 ep_pol ? 00:00:00 redis-server
5 S 0 2007 1 0 80 0 - 34359 ep_pol ? 00:00:00 redis-server
5 S 0 2011 1 0 80 0 - 34359 ep_pol ? 00:00:00 redis-server
5 S 0 2017 1 0 80 0 - 34359 ep_pol ? 00:00:00 redis-server
5 S 0 2023 1 0 80 0 - 34359 ep_pol ? 00:00:00 redis-server
(3)查看集群状态
二、新集群操作
(4)我们新建俩个服务,按照之前搭建的集群方式新增俩个节点:(一主一从 master、slave)
Master:7007 Slave:7008
步骤一:创建7007/7008文件夹。拷贝redis.conf文件到对于的7007,7008目录下 要 进行修改配置文件。
[root@bhz004 redis-cluster]# mkdir 7007
[root@bhz004 redis-cluster]# mkdir 7008
[root@bhz004 redis-cluster]# cd 7001
[root@bhz004 7001]# cp redis.conf /usr/local/redis-cluster/7007/
[root@bhz004 7001]# cp redis.conf /usr/local/redis-cluster/7008/
[root@bhz004 7001]# vim /usr/local/redis-cluster/7007/redis.conf
修改内容如下:
port:7007
dir /usr/local/redis-cluster/7007/
cluster-config-file nodes7007.conf
[root@bhz004 7001]# vim /usr/local/redis-cluster/7008/redis.conf
修改内容如下:
port:7008
dir /usr/local/redis-cluster/7008/
cluster-config-file nodes7008.conf
步骤二:启动7007和7008俩个服务并查看服务状态。
[root@bhz004 7001]# /usr/local/redis/bin/redis-server /usr/local/redis-cluster/7007/redis.conf
[root@bhz004 7001]# /usr/local/redis/bin/redis-server /usr/local/redis-cluster/7008/redis.conf
[root@bhz004 7001]# ps -el | grep redis
(5)学习redis-trib命令使用:
[root@bhz004 local]# cd /usr/local/redis3.0/src
[root@bhz004 src]# redis-trib.rb
1 create:创建一个集群环境host1:port1 ... hostN:portN(集群中的主从节点比例)
2 call:可以执行redis命令
3 add-node:将一个节点添加到集群里,第一个参数为新节点的ip:port,第二个参数为集群中任意一个已经存在的节点的ip:port
4 del-node:移除一个节点
5 reshard:重新分片
6 check:检查集群状态
(6)新增一个主节点7007(master)
步骤一:使用add-node命令:绿色为新增节点,红色为已知存在节点
[root@bhz004 7001]#
/usr/local/redis3.0/src/redis-trib.rb add-node 192.168.1.171:7007 192.168.1.171:7001
输出如下:
>>> Adding node 192.168.1.171:7007 to cluster 192.168.1.171:7001
Connecting to node 192.168.1.171:7001: OK
Connecting to node 192.168.1.171:7006: OK
Connecting to node 192.168.1.171:7005: OK
Connecting to node 192.168.1.171:7004: OK
Connecting to node 192.168.1.171:7002: OK
Connecting to node 192.168.1.171:7003: OK
>>> Performing Cluster Check (using node 192.168.1.171:7001)
M: 614d0def75663f2620b6402a017014b57c912dad 192.168.1.171:7001
slots:0-5460 (5461 slots) master
1 additional replica(s)
S: fa299e41c173fa807ba04684c2f5e5e185d5f7d0 192.168.1.171:7006
slots: (0 slots) slave
replicates 83df08875c7707878756364039df0a4c8658f272
S: adb99506ddccad332e09258565f2e5f4f456a150 192.168.1.171:7005
slots: (0 slots) slave
replicates 8aac82b63d42a1989528cd3906579863a5774e77
S: a69b98937844c6050ee5885266ccccb185a3f36a 192.168.1.171:7004
slots: (0 slots) slave
replicates 614d0def75663f2620b6402a017014b57c912dad
M: 8aac82b63d42a1989528cd3906579863a5774e77 192.168.1.171:7002
slots:5461-10922 (5462 slots) master
1 additional replica(s)
M: 83df08875c7707878756364039df0a4c8658f272 192.168.1.171:7003
slots:10923-16383 (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
Connecting to node 192.168.1.171:7007: OK
>>> Send CLUSTER MEET to node 192.168.1.171:7007 to make it join the cluster.
[OK] New node added correctly.
步骤二:查看集群状态:
[root@bhz004 src]# /usr/local/redis/bin/redis-cli -c -h 192.168.1.171 -p 7001
192.168.1.171:7001> cluster nodes
注意:当添加节点成功以后,新增的节点不会有任何数据,因为它没有分配任何的slot(hash槽)。我们需要为新节点手工分配slot。
(7)为7007分配slot槽。
步骤一:使用redis-trib命令,找到集群中的任意一个主节点(红色位置表现集群中的任意一个主节点),对其进行重新分片工作。
[root@bhz004 7001]# /usr/local/redis3.0/src/redis-trib.rb reshard 192.168.1.171:7001
输出如下:
>>> Performing Cluster Check (using node 192.168.1.171:7001)
M: 614d0def75663f2620b6402a017014b57c912dad 192.168.1.171:7001
slots:0-5460 (5461 slots) master
1 additional replica(s)
S: fa299e41c173fa807ba04684c2f5e5e185d5f7d0 192.168.1.171:7006
slots: (0 slots) slave
replicates 83df08875c7707878756364039df0a4c8658f272
S: adb99506ddccad332e09258565f2e5f4f456a150 192.168.1.171:7005
slots: (0 slots) slave
replicates 8aac82b63d42a1989528cd3906579863a5774e77
M: 382634a4025778c040b7213453fd42a709f79e28 192.168.1.171:7007
slots: (0 slots) master
0 additional replica(s)
S: a69b98937844c6050ee5885266ccccb185a3f36a 192.168.1.171:7004
slots: (0 slots) slave
replicates 614d0def75663f2620b6402a017014b57c912dad
M: 8aac82b63d42a1989528cd3906579863a5774e77 192.168.1.171:7002
slots:5461-10922 (5462 slots) master
1 additional replica(s)
M: 83df08875c7707878756364039df0a4c8658f272 192.168.1.171:7003
slots:10923-16383 (5461 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
(提示一)
How many slots do you want to move (from 1 to 16384)? 200
(提示二)
What is the receiving node ID? 382634a4025778c040b7213453fd42a709f79e28
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots.
Type 'done' once you entered all the source nodes IDs.
Source node #1:all
Ready to move 200 slots.
Source nodes:
M: 614d0def75663f2620b6402a017014b57c912dad 192.168.1.171:7001
slots:0-5460 (5461 slots) master
1 additional replica(s)
M: 8aac82b63d42a1989528cd3906579863a5774e77 192.168.1.171:7002
slots:5461-10922 (5462 slots) master
1 additional replica(s)
M: 83df08875c7707878756364039df0a4c8658f272 192.168.1.171:7003
slots:10923-16383 (5461 slots) master
1 additional replica(s)
Destination node:
M: 382634a4025778c040b7213453fd42a709f79e28 192.168.1.171:7007
slots: (0 slots) master
0 additional replica(s)
Resharding plan:(分片执行计划日志)
Moving slot 5461 from 8aac82b63d42a1989528cd3906579863a5774e77
...
Moving slot 0 from 614d0def75663f2620b6402a017014b57c912dad
...
Moving slot 10923 from 83df08875c7707878756364039df0a4c8658f272
...
(提示三)
Do you want to proceed with the proposed reshard plan (yes/no)? yes
Moving slot 65 from 192.168.1.171:7001 to 192.168.1.171:7007:
...
Moving slot 10923 from 192.168.1.171:7003 to 192.168.1.171:7007:
...
Moving slot 5527 from 192.168.1.171:7002 to 192.168.1.171:7007:
...
1提示一:是希望你需要多少个槽移动到新的节点上,可以自己设置,比如200个槽。
2提示二:是你需要把这200个slot槽移动到那个节点上去(需要指定节点id),并且下个 提示是输入all为从所有主节点(7001 7002 7003)中分别抽取响应的槽数(一共为200个槽到指定的新节点中!,并且会打印执行分片的计划。)
3提示三:输入yes确认开始执行分片任务。在最后我们再次看一下集群状态:
如上图所示,现在我们的7007已经有slot槽了,也就是说可以在7007上进行读写数据啦!到此为止我们的7007已经加入到集群中啦,并且是主节点(Master)
(8)添加从节点(7008)到集群中去。
步骤一:还是需要执行add-node命令:
[root@bhz004 7001]#
/usr/local/redis3.0/src/redis-trib.rb add-node 192.168.1.171:7008 192.168.1.171:7001
提示添加成功后我们继续看一下集群的状态:
如图所示,还是一个master节点,没有被分配任何的slot槽。
步骤二:我们需要执行replicate命令来指定当前节点(从节点)的主节点id为哪个。
首先需要登录新加的7008节点的客户端,然后使用集群命令进行操作,把当前的7008(slave)节点指定到一个主节点下(这里使用之前创建的7007主节点,红色表示节点id)
[root@bhz004 ~]# /usr/local/redis/bin/redis-cli -c -h 192.168.1.171 -p 7008
192.168.1.171:7008> cluster replicate 382634a4025778c040b7213453fd42a709f79e28
192.168.1.171:7008> OK(提示OK则操作成功)
我们继续看一下当前集群的状态,如下图:我们已经成功的把7008放到7007这个主节点下面了,到此为止我们已经成功的添加完一个从节点了。
(9)我们可以对集群进行操作,来验证下是否可以进行读写(当然可以)。
(10)我们现在尝试删除一个节点(7008 slave)
步骤一:删除从节点7008,输入del-node命令,指定删除节点ip和端口,以及节点id(红色为7008节点id)
[root@bhz004 7001]# /usr/local/redis3.0/src/redis-trib.rb
del-node 192.168.1.171:7008 97b0e0115326833724eb0ffe1d0574ee34618e9f
输出如下:
>>> Removing node 97b0e0115326833724eb0ffe1d0574ee34618e9f from cluster 192.168.1.171:7008
Connecting to node 192.168.1.171:7008: OK
Connecting to node 192.168.1.171:7003: OK
Connecting to node 192.168.1.171:7006: OK
Connecting to node 192.168.1.171:7002: OK
Connecting to node 192.168.1.171:7005: OK
Connecting to node 192.168.1.171:7001: OK
Connecting to node 192.168.1.171:7004: OK
Connecting to node 192.168.1.171:7007: OK
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.
步骤二:再次查看一下集群状态,如下图所示,我们已经成功的移除了7008 slave节点,另外我们发现移除一个节点以后,当前节点的服务进程也会随之销毁。可以使用ps命令查看当前的服务(ps -el | grep redis),发现少了一个运行的server,也就是刚移除的7008从节点。
(11)最后,我们尝试删除之前加入的主节点7007,这个步骤会相对比较麻烦一些,因为主节点的里面是有分配了slot槽的,所以我们这里必须先把7007里的slot槽放入到其他的可用主节点中去,然后再进行移除节点操作才行,不然会出现数据丢失问题。
步骤一:删除7007(master)节点之前,我们需要先把其全部的数据(slot槽)移动到其他节点上去(目前只能把master的数据迁移到一个节点上,暂时做不了平均分配功能)。
[root@bhz004 7001]# /usr/local/redis3.0/src/redis-trib.rb reshard 192.168.1.171:7007
输出如下:
>>> Performing Cluster Check (using node 192.168.1.171:7007)
M: 382634a4025778c040b7213453fd42a709f79e28 192.168.1.171:7007
slots:0-65,5461-5527,10923-10988 (199 slots) master
0 additional replica(s)
S: fa299e41c173fa807ba04684c2f5e5e185d5f7d0 192.168.1.171:7006
slots: (0 slots) slave
replicates 83df08875c7707878756364039df0a4c8658f272
S: a69b98937844c6050ee5885266ccccb185a3f36a 192.168.1.171:7004
slots: (0 slots) slave
replicates 614d0def75663f2620b6402a017014b57c912dad
M: 614d0def75663f2620b6402a017014b57c912dad 192.168.1.171:7001
slots:66-5460 (5395 slots) master
1 additional replica(s)
M: 8aac82b63d42a1989528cd3906579863a5774e77 192.168.1.171:7002
slots:5528-10922 (5395 slots) master
1 additional replica(s)
S: adb99506ddccad332e09258565f2e5f4f456a150 192.168.1.171:7005
slots: (0 slots) slave
replicates 8aac82b63d42a1989528cd3906579863a5774e77
M: 83df08875c7707878756364039df0a4c8658f272 192.168.1.171:7003
slots:10989-16383 (5395 slots) master
1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
How many slots do you want to move (from 1 to 16384)? 199
(注释:这里不会是正好200个槽)
What is the receiving node ID? 614d0def75663f2620b6402a017014b57c912dad
(注释:这里是需要把数据移动到哪?7001的主节点id)
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots.
Type 'done' once you entered all the source nodes IDs.
Source node #1:382634a4025778c040b7213453fd42a709f79e28
(注释:这里是需要数据源,也就是我们的7007节点id)
Source node #2:done
(注释:这里直接输入done 开始生成迁移计划)
Ready to move 199 slots.
Source nodes:
M: 382634a4025778c040b7213453fd42a709f79e28 192.168.1.171:7007
slots:0-65,5461-5527,10923-10988 (199 slots) master
0 additional replica(s)
Destination node:
M: 614d0def75663f2620b6402a017014b57c912dad 192.168.1.171:7001
slots:66-5460 (5395 slots) master
1 additional replica(s)
Resharding plan:
Moving slot 0 from 382634a4025778c040b7213453fd42a709f79e28
...
Do you want to proceed with the proposed reshard plan (yes/no)? Yes
(注释:这里输入yes开始迁移)
Moving slot 0 from 192.168.1.171:7007 to 192.168.1.171:7001:
...
到此为止我们已经成功的把7007主节点的数据迁移到7001上去了,我们可以看一下现在的集群状态如下图,你会发现7007下面已经没有任何数据(slot)槽了,证明迁移成功!
步骤二:最后我们直接使用del-node命令删除7007主节点即可(红色表示7007的节点id)。
[root@bhz004 7001]# /usr/local/redis3.0/src/redis-trib.rb del-node
192.168.1.171:7007 382634a4025778c040b7213453fd42a709f79e28
输出如下:
>>> Removing node 382634a4025778c040b7213453fd42a709f79e28 from cluster 192.168.1.171:7007
Connecting to node 192.168.1.171:7007: OK
Connecting to node 192.168.1.171:7006: OK
Connecting to node 192.168.1.171:7004: OK
Connecting to node 192.168.1.171:7001: OK
Connecting to node 192.168.1.171:7002: OK
Connecting to node 192.168.1.171:7005: OK
Connecting to node 192.168.1.171:7003: OK
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node。
最后:我们查看集群状态,一切还原为最初始状态啦!OK 结束!
- pom.xml文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.wt.redis</groupId>
<artifactId>reidsdemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
- java代码
package cn.wt.redis;
import redis.clients.jedis.Jedis;
/**
* @author wb-wt261136
* @version 2017年9月13日 下午5:06:56
*/
public class JedisPoolUtil {
public static void main(String[] args) {
Jedis jedis = new Jedis("45.78.9.159",6379);
jedis.set("jedis", "jedisValue");
String result = jedis.ping();// 测试连接
String value = jedis.get("jedis");
System.out.println(result);
System.out.println(value);
jedis.close();
}
}
package bhz.redis01;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPipeline;
import redis.clients.jedis.ShardedJedisPool;
import redis.clients.jedis.Transaction;
public class TestSingleRedis {
private static Jedis jedis;
private static ShardedJedis shard;
private static ShardedJedisPool pool;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
//单个节点
jedis = new Jedis("192.168.1.101", 6379);
//分片
List<JedisShardInfo> shards = Arrays.asList(
new JedisShardInfo("192.168.1.106",6379));
shard = new ShardedJedis(shards);
GenericObjectPoolConfig goConfig = new GenericObjectPoolConfig();
goConfig.setMaxTotal(100);
goConfig.setMaxIdle(20);
goConfig.setMaxWaitMillis(-1);
goConfig.setTestOnBorrow(true);
pool = new ShardedJedisPool(goConfig, shards);
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
jedis.disconnect();
shard.disconnect();
pool.destroy();
}
@Test
public void testString() {
//-----添加数据----------
jedis.set("name","bhz");//向key-->name中放入了value-->xinxin
System.out.println(jedis.get("name"));//执行结果:xinxin
jedis.append("name", " is my lover"); //拼接
System.out.println(jedis.get("name"));
jedis.del("name"); //删除某个键
System.out.println(jedis.get("name"));
//设置多个键值对
jedis.mset("name","bhz","age","27","qq","174754613");
jedis.incr("age"); //进行加1操作
System.out.println(jedis.get("name") + "-" + jedis.get("age") + "-" + jedis.get("qq"));
}
/**
* redis操作Map
*/
@Test
public void testMap() {
//-----添加数据----------
Map<String, String> map = new HashMap<String, String>();
map.put("name", "xinxin");
map.put("age", "22");
map.put("qq", "123456");
jedis.hmset("user",map);
//取出user中的name,执行结果:[minxr]-->注意结果是一个泛型的List
//第一个参数是存入redis中map对象的key,后面跟的是放入map中的对象的key,后面的key可以跟多个,是可变参数
List<String> rsmap = jedis.hmget("user", "name", "age", "qq");
System.out.println(rsmap);
//删除map中的某个键值
jedis.hdel("user","age");
System.out.println(jedis.hmget("user", "age")); //因为删除了,所以返回的是null
System.out.println(jedis.hlen("user")); //返回key为user的键中存放的值的个数2
System.out.println(jedis.exists("user"));//是否存在key为user的记录 返回true
System.out.println(jedis.hkeys("user"));//返回map对象中的所有key
System.out.println(jedis.hvals("user"));//返回map对象中的所有value
Iterator<String> iter=jedis.hkeys("user").iterator();
while (iter.hasNext()){
String key = iter.next();
System.out.println(key+":"+jedis.hmget("user",key));
}
}
/**
* jedis操作List
*/
@Test
public void testList(){
//开始前,先移除所有的内容
jedis.del("java framework");
System.out.println(jedis.lrange("java framework",0,-1));
//先向key java framework中存放三条数据
jedis.lpush("java framework","spring");
jedis.lpush("java framework","struts");
jedis.lpush("java framework","hibernate");
//再取出所有数据jedis.lrange是按范围取出,
// 第一个是key,第二个是起始位置,第三个是结束位置,jedis.llen获取长度 -1表示取得所有
System.out.println(jedis.lrange("java framework",0,-1));
jedis.del("java framework");
jedis.rpush("java framework","spring");
jedis.rpush("java framework","struts");
jedis.rpush("java framework","hibernate");
System.out.println(jedis.lrange("java framework",0,-1));
}
/**
* jedis操作Set
*/
@Test
public void testSet(){
//添加
jedis.sadd("user","liuling");
jedis.sadd("user","xinxin");
jedis.sadd("user","ling");
jedis.sadd("user","zhangxinxin");
jedis.sadd("user","who");
//移除noname
jedis.srem("user","who");
System.out.println(jedis.smembers("user"));//获取所有加入的value
System.out.println(jedis.sismember("user", "who"));//判断 who 是否是user集合的元素
System.out.println(jedis.srandmember("user"));
System.out.println(jedis.scard("user"));//返回集合的元素个数
}
@Test
public void testRLpush() throws InterruptedException {
//jedis 排序
//注意,此处的rpush和lpush是List的操作。是一个双向链表(但从表现来看的)
jedis.del("a");//先清除数据,再加入数据进行测试
jedis.rpush("a", "1");
jedis.lpush("a","6");
jedis.lpush("a","3");
jedis.lpush("a","9");
System.out.println(jedis.lrange("a",0,-1));// [9, 3, 6, 1]
System.out.println(jedis.sort("a")); //[1, 3, 6, 9] //输入排序后结果
System.out.println(jedis.lrange("a",0,-1));
}
@Test
public void testTrans() {
long start = System.currentTimeMillis();
Transaction tx = jedis.multi();
for (int i = 0; i < 1000; i++) {
tx.set("t" + i, "t" + i);
}
//System.out.println(tx.get("t1000").get());
List<Object> results = tx.exec();
long end = System.currentTimeMillis();
System.out.println("Transaction SET: " + ((end - start)/1000.0) + " seconds");
}
@Test
public void testPipelined() {
Pipeline pipeline = jedis.pipelined();
long start = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
pipeline.set("p" + i, "p" + i);
}
//System.out.println(pipeline.get("p1000").get());
List<Object> results = pipeline.syncAndReturnAll();
long end = System.currentTimeMillis();
System.out.println("Pipelined SET: " + ((end - start)/1000.0) + " seconds");
}
@Test
public void testPipelineTrans() {
long start = System.currentTimeMillis();
Pipeline pipeline = jedis.pipelined();
pipeline.multi();
for (int i = 0; i < 100000; i++) {
pipeline.set("" + i, "" + i);
}
pipeline.exec();
List<Object> results = pipeline.syncAndReturnAll();
long end = System.currentTimeMillis();
System.out.println("Pipelined transaction SET: " + ((end - start)/1000.0) + " seconds");
}
@Test
public void testShard() {
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
String result = shard.set("shard" + i, "n" + i);
}
long end = System.currentTimeMillis();
System.out.println("shard SET: " + ((end - start)/1000.0) + " seconds");
}
@Test
public void testShardpipelined() {
ShardedJedisPipeline pipeline = shard.pipelined();
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
pipeline.set("sp" + i, "p" + i);
}
List<Object> results = pipeline.syncAndReturnAll();
long end = System.currentTimeMillis();
System.out.println("shardPipelined SET: " + ((end - start)/1000.0) + " seconds");
}
@Test
public void testShardPool() {
ShardedJedis sj = pool.getResource();
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
String result = sj.set("spn" + i, "n" + i);
}
long end = System.currentTimeMillis();
pool.returnResource(sj);
System.out.println("shardPool SET: " + ((end - start)/1000.0) + " seconds");
}
}
1)java单独代码
package cn.wt.redis;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
public class TestClusterRedis {
public static void main(String[] args) throws IOException {
Set<HostAndPort> jedisClusterNode = new HashSet<HostAndPort>();
jedisClusterNode.add(new HostAndPort("45.78.9.159", 6380));
jedisClusterNode.add(new HostAndPort("45.78.9.159", 6381));
jedisClusterNode.add(new HostAndPort("45.78.9.159", 6382));
jedisClusterNode.add(new HostAndPort("45.78.9.159", 6383));
jedisClusterNode.add(new HostAndPort("45.78.9.159", 6384));
jedisClusterNode.add(new HostAndPort("45.78.9.159", 6385));
//GenericObjectPoolConfig goConfig = new GenericObjectPoolConfig();
//JedisCluster jc = new JedisCluster(jedisClusterNode,2000,100, goConfig);
JedisPoolConfig cfg = new JedisPoolConfig();
cfg.setMaxTotal(100);
cfg.setMaxIdle(20);
cfg.setMaxWaitMillis(-1);
cfg.setTestOnBorrow(true);
JedisCluster jc = new JedisCluster(jedisClusterNode, 6000, 1000, cfg);
System.out.println(jc.set("age", "20"));
System.out.println(jc.set("sex", "男"));
System.out.println(jc.get("name"));
System.out.println(jc.get("name"));
System.out.println(jc.get("name"));
System.out.println(jc.get("name"));
System.out.println(jc.get("name"));
System.out.println(jc.get("name"));
System.out.println(jc.get("name"));
System.out.println(jc.get("name"));
System.out.println(jc.get("age"));
System.out.println(jc.get("sex"));
jc.close();
}
}
2)与spring整合
pom文件,示例只有redis,还需spring的一些pom文件
<!--redis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
spring-redis.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:redis.properties"/>
<!--扫描包名-->
<context:component-scan base-package="com.x.redis.dao">
</context:component-scan>
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}"/>
<!--<property name="maxTotal" value="${redis.maxActive}" />-->
<!--<property name="maxWaitMillis" value="${redis.maxWait}" />-->
<property name="testOnBorrow" value="${redis.testOnBorrow}"/>
</bean>
<bean id="hostport1" class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="45.78.9.159"/>
<constructor-arg name="port" value="6380"/>
</bean>
<bean id="hostport2" class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="45.78.9.159"/>
<constructor-arg name="port" value="6381"/>
</bean>
<bean id="hostport3" class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="45.78.9.159"/>
<constructor-arg name="port" value="6382"/>
</bean>
<bean id="hostport4" class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="45.78.9.159"/>
<constructor-arg name="port" value="6383"/>
</bean>
<bean id="hostport5" class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="45.78.9.159"/>
<constructor-arg name="port" value="6384"/>
</bean>
<bean id="hostport6" class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="45.78.9.159"/>
<constructor-arg name="port" value="6385"/>
</bean>
<bean id="redisCluster" class="redis.clients.jedis.JedisCluster">
<constructor-arg name="nodes">
<set>
<ref bean="hostport1"/>
<ref bean="hostport2"/>
<ref bean="hostport3"/>
<ref bean="hostport4"/>
<ref bean="hostport5"/>
<ref bean="hostport6"/>
</set>
</constructor-arg>
<constructor-arg name="timeout" value="6000"/>
<constructor-arg name="poolConfig">
<ref bean="jedisPoolConfig"/>
</constructor-arg>
</bean>
</beans>
redis.properties配置文件
redis.maxIdle=20
redis.maxActive=
redis.maxWait=-1
redis.testOnBorrow=true
java测试代码
package com.wtdig.redis;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import redis.clients.jedis.JedisCluster;
import java.io.IOException;
/**
* @author wb-wt261136
* @version 2018/5/21. 10:30
*/
public class TestClusterRedis {
public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-redisCluster.xml");
JedisCluster redisCluster = (JedisCluster) applicationContext.getBean("redisCluster");
redisCluster.set("wtdig", "test-wtdig");
System.out.println(redisCluster.get("wtdig"));
redisCluster.close();
}
}
commons-pool2-2.3.jar hamcrest-core-1.3.jar jedis-2.7.2.jar
错误信息:Exception in thread "main" redis.clients.jedis.exceptions.JedisConnectionException: java.net.ConnectException: Connection refused: connect
解决方案:打开 etc/sysconfig/iptables,在iptables中添加-A INPUT -p tcp -m state --state NEW -m tcp --dport 6379 -j ACCEPT
不让防火墙拦截6379端口,输入命令service iptables restart重启防火墙
如果依然错误:将redis.conf文件的 bind 127.0.0.1 这一行注释掉,(这样所有的ip都可以访问了,不需要是127.0.0.1了)
如果还是错误:将redis.conf文件的 protected-mode yes 改成no ,关闭保护模式
每次修改后,都要重新启动redis的服务
如果还是报错,重启服务器。