1、安装启动
1.1安装
# 安装gcc环境 如果没有安装gcc会出现错误(此时需要删除文件夹重新解压)
yum install gcc-c++
# 将redis-5.0.9.tar.gz下载到/home,该版本为最后一个 5.0
cd /home
wget https://download.redis.io/releases/redis-5.0.9.tar.gz
tar xzf redis-5.0.9.tar.gz
cd redis-5.0.9
# 编译
make
# 安装到/usr/local/redis目录下
make PREFIX=/usr/local/redis install
# 拷贝配置文件
cp redis.conf /usr/local/redis/
#进入安装思路
cd /usr/local/redis/
1.2修改配置文件
#调整配置文件
vim /usr/local/redis/redis.conf
#修改配置文件,后台启动改为yes
#daemonize yes
#开启外网访问 ,注释这个内容
#bind 127.0.0.1
#密码
# requirepass foobared
1.3 启动
#启动命令
./bin/redis-server ./redis.conf
# 启动后查看redis默认端口号为6379
ps -ef | grep -i redis
# 关闭redis
./bin/redis-cli shutdown
#测试 ./bin/redis-cli -h 192.168.182.137 -p 6379 ,-h默认为本地,可以不写
./bin/redis-cli -p 6379
ping
2.Key操作
redis默认有16个数据库
可以使用
2.2.1 select
切换数据库
#切换数据库
127.0.0.1:6379> select 1
2.2.2 set
设置值
#设置一个key
127.0.0.1:6379> set k1 v1
127.0.0.1:6379> del key
2.2.3 EXISTS
判断key是否存在
127.0.0.1:6379> EXISTS k1
keys
#查看是否有 k1,并返回
127.0.0.1:6379> keys k1
#查看所有key
127.0.0.1:6379> keys *
move expire ttl type flushdb
#把 k1从当前数据库,移动到 数据库2
127.0.0.1:6379>move k1 1
127.0.0.1:6379> expire k1 10 #设置k1的有效期为10s
127.0.0.1:6379> ttl k1 #查看k1的剩余有效期
127.0.0.1:6379> type k1 #查看类型,这个返回是 string
127.0.0.1:6379> flushdb #清空当前的数据库
127.0.0.1:6379> flushall #清空所有数据库
2.五大数据类型
1 String字符串操作
1append
127.0.0.1:6379> APPEND k1 " hello" #字符串追加 返回字符串的长度,如果key不存在则创建
2 strlen
127.0.0.1:6379> strlen k1 #获取字符串的长度
3 incr
# 设置 views为浏览量,使用redis的 i++方法
127.0.0.1:6379> set views 0
127.0.0.1:6379> incr views #把 views的值进行+1,调用一次加一次,不存在则新建,默认是在0的基础上+1
4 decr
127.0.0.1:6379> decr views #把 views的值进行-1,调用一次减一次,不存在则新建,默认是在0的基础上-1
5 getRange
##字符串范围
127.0.0.1:6379> set k1 gsh456
OK
127.0.0.1:6379> GETRANGE k1 0 -1 #获取k1的全部内容
"gsh456"
127.0.0.1:6379> GETRANGE k1 0 2 #获取k1的指定区间内容
"gsh"
6 setRange
127.0.0.1:6379> set k2 abcdefghi
OK
127.0.0.1:6379> get k2
"abcdefghi"
127.0.0.1:6379> SETRANGE k2 3 gsh #从第4个元素进行 替换字符串
(integer) 9
127.0.0.1:6379> get k2
"abcgshghi"
7 setex
setex(set with expire) #设置一个值(带过期时间)
127.0.0.1:6379> setex k3 60 value3 #设置一个值(带过期时间)
OK
8 ttl
查看有效期
127.0.0.1:6379> ttl k3
(integer) 55
9 setnx
setnx (set if not exist) #不存在的时候就设置。
127.0.0.1:6379> setnx mykey test_setnx #如果不存在则新建,这里返回值是1,代表成功
(integer) 1
127.0.0.1:6379> get mykey
"test_setnx"
127.0.0.1:6379> setnx k2 test #如果不存在则新建,这里返回值是0,代表失败
(integer) 0
10mset&mget
批量设置
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 #批量设置
OK
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"
127.0.0.1:6379> mget k1 k3 #批量获取
1) "v1"
2) "v3"
# 这里的事务是原子性,一起成功一起失败
127.0.0.1:6379> msetnx k1 v1 k4 v4 #批量设置(判断是否已经存在),这里返回值为0,代表失败,因为k1已经有值
(integer) 0
11 key的技巧
#下面是一个user对象,有两个属性,name和age 这里分别设置他们的值
127.0.0.1:6379> mset user:1:name gsh user:1:age 10 #批量设置
OK
127.0.0.1:6379> get user:1:age #获取用户年龄信息,方便的使用 incr等方法 进行运算
"10"
12 GetSet
先获取,在设置新值
########################GetSet##########################
#先获取再设置,如果存在则先获取 null 设置新的值。
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> getset k1 new1 #先获取k1的值,再重新设置k1的值
"v1"
127.0.0.1:6379> get k1
"new1"
2 List
redis的list是先进后出,后进先出。
所有的list的相关命令都是以 L
开头
1. lpush
添加列表创建或新增元素
127.0.0.1:6379> keys *
(empty list or set)
# lpush 将一个值或者多个值,插入到列表的头部
127.0.0.1:6379> lpush list1 one #向list1里添加 one 元素,
(integer) 1
127.0.0.1:6379> lpush list1 two #向list1里添加 two 元素
(integer) 2
127.0.0.1:6379> lpush list1 three #向list1里添加 three 元素
(integer) 3
2LRANGE
查看列表
127.0.0.1:6379> LRANGE list1 0 -1 #查看list1所有 元素,先进后出。
1) "three"
2) "two"
3) "one"
127.0.0.1:6379[1]> LRANGE list1 0 1 #获取具体区间的值,先进先出
1) "three"
2) "two"
3 rpush
从尾部添加元素
# rpush 将一个值或者多个值,插入到列表的尾部
127.0.0.1:6379> rpush list1 zero
(integer) 4
127.0.0.1:6379> LRANGE list1 0 -1
1) "three"
2) "two"
3) "one"
4) "zero"
4 LPOP&RPOP
从左边删除、从右边删除
#LPOP 从左边移除
#RPOP 从右边移除
127.0.0.1:6379> LRANGE list1 0 -1
1) "three"
2) "two"
3) "one"
4) "zero"
127.0.0.1:6379> lpop list1 #移除列表的第一个元素
"three"
127.0.0.1:6379> LRANGE list1 0 -1
1) "two"
2) "one"
3) "zero"
127.0.0.1:6379> rpop list1 #移除列表的最后一个元素
"zero"
127.0.0.1:6379> LRANGE list1 0 -1
1) "two"
2) "one"
5 lindex
获取指定下标的值
127.0.0.1:6379> LINDEX list1 1 #获取指定下标的值,从0开始
"one"
127.0.0.1:6379> LINDEX list1 0
"two"
6 llen
获取列表长度
127.0.0.1:6379> llen list1 #获取列表的长度
(integer) 2
7 lrem
删除指定元素
# lrem # 移除一个值
127.0.0.1:6379> lpush list1 two
(integer) 3
127.0.0.1:6379> LRANGE list1 0 -1
1) "two"
2) "two"
3) "one"
###新增部分值
127.0.0.1:6379> LRANGE list1 0 -1
1) "two"
2) "3"
3) "two"
4) "two"
5) "one"
127.0.0.1:6379> LREM list1 1 two #移除 一个'two'元素 ,从上到下
(integer) 1
127.0.0.1:6379> LRANGE list1 0 -1
1) "3"
2) "two"
3) "two"
4) "one"
127.0.0.1:6379> LREM list1 2 two #移除 两个'two'元素
(integer) 2
127.0.0.1:6379> LRANGE list1 0 -1
1) "3"
2) "one"
8 ltrim
截取
######trim
127.0.0.1:6379> lpush list1 one two three four #数据初始化
127.0.0.1:6379> LRANGE list1 0 -1
1) "four"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> ltrim list1 1 2 #通过下表获取指定的范围
OK
127.0.0.1:6379> LRANGE list1 0 -1
1) "three"
2) "two"
9 rpoplpush
移除列表的最后一个元素,并将它移动到新的列表中
#rpoplpush #
127.0.0.1:6379> lpush list1 one two three four
(integer) 4
127.0.0.1:6379> LRANGE list1 0 -1
1) "four"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> RPOPLPUSH list1 list2 #移动 list1的最后一个元素到list2中
"one"
127.0.0.1:6379> LRANGE list1 0 -1 #老的列表
1) "four"
2) "three"
3) "two"
127.0.0.1:6379> LRANGE list2 0 -1 #新的列表
1) "one"
10 exists
判断列表是否存在
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> lpush list1 one two three four
(integer) 4
127.0.0.1:6379> LRANGE list1 0 -1
1) "four"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> exists list1
(integer) 1
127.0.0.1:6379> exists list2
(integer) 0
11 lset
重新设置列表中指定下标的值
127.0.0.1:6379> lset list1 0 four4 #
OK
127.0.0.1:6379> LRANGE list1 0 -1
1) "four4"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> lset list2 0 four4 #如果 list2 不存在则报错
(error) ERR no such key
12 LINSERT
在列表中,指定元素的前面添加一个元素
127.0.0.1:6379> LINSERT list1 before two 2.5 #在列表中,指定元素的前面添加一个元素
(integer) 5
127.0.0.1:6379> LRANGE list1 0 -1
1) "four4"
2) "three"
3) "2.5"
4) "two"
5) "one"
127.0.0.1:6379> LINSERT list1 after four4 3.5 #在列表中,指定元素的后面添加一个元素
(integer) 6
127.0.0.1:6379> LRANGE list1 0 -1
1) "four4"
2) "3.5"
3) "three"
4) "2.5"
5) "two"
6) "one"
小结
它的实质是一个链表,before Node after ,left,right 都可以插入值
如果key不存在,创建新的链表。
如果key存在,新增链表中的元素。
如果一处了所有的值,空链表,也代表不存在。
在两个插入或者改动值,效率是最高的。
可以做消息队列,队列:Lpush Rpop ,栈:Lpush Lpop
3 Set(集合)
特点:
不可重复
乱序
set值不能重复,命令的开头都是 S
sadd
127.0.0.1:6379> sadd set1 v1 #向集合中添加一个值
(integer) 1
127.0.0.1:6379> sadd set1 v1 # 不可重复
(integer) 0
127.0.0.1:6379> sadd set1 v2 v3 v4 #向集合中添加多个值
(integer) 3
#SMEMBERS 查看成员
127.0.0.1:6379> SMEMBERS set1 #查看集合的所有值
1) "v3"
2) "v2"
3) "v1"
4) "v4"
127.0.0.1:6379> SISMEMBER set1 v5 #查看集合中,是否存在某个元素,不存在返回0
(integer) 0
127.0.0.1:6379> SISMEMBER set1 v1 #查看集合中,是否存在某个元素,存在返回1
(integer) 1
127.0.0.1:6379> scard set1 #获取元素个数
(integer) 4
127.0.0.1:6379> srem set1 v1 #移除指定元素
(integer) 1
127.0.0.1:6379> scard set1
(integer) 3
127.0.0.1:6379> SMEMBERS set1
1) "v2"
2) "v4"
3) "v3"
127.0.0.1:6379> SRANDMEMBER set1 #随机获取,一个元素
"v3"
127.0.0.1:6379> SRANDMEMBER set1 #随机获取,一个元素
"v4"
127.0.0.1:6379> SRANDMEMBER set1 2 #随机获取,指定个数元素
1) "v3"
2) "v1"
127.0.0.1:6379> SRANDMEMBER set1 2 #随机获取,指定个数元素
1) "v1"
2) "v4"
127.0.0.1:6379> spop set1 #随机删除,后面可以跟个数 spop set1 2
"v2"
########################元素移动###################
127.0.0.1:6379> SMEMBERS set1
1) "v3"
2) "v2"
3) "v1"
4) "v4"
127.0.0.1:6379> smove set1 set2 v2 #移动set1中的指定元素到set2,如果set2不存在则新建
(integer) 1
127.0.0.1:6379> SMEMBERS set2
1) "v2"
127.0.0.1:6379> SMEMBERS set1
1) "v3"
2) "v1"
3) "v4"
#####################共同关注#############
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> sadd set1 v1 v2 v3 #新增一个集合
(integer) 3
127.0.0.1:6379> sadd set2 v3 v4 v5 #新增一个集合
(integer) 3
127.0.0.1:6379> SDIFF set1 set2 #求 set1和set2的不同元素(差集)
1) "v1"
2) "v2"
127.0.0.1:6379> SDIFF set2 set1 #求 set2和set1的不同元素(差集)
1) "v5"
2) "v4"
127.0.0.1:6379> Sinter set2 set1 #求 set1和set2的共通(交集)
1) "v3"
127.0.0.1:6379> Sunion set2 set1 #求 set1和set2的所有元素(并集)
1) "v3"
2) "v5"
3) "v2"
4) "v4"
5) "v1"
4 Hash(Map集合)
key-Map<key,value> 值是map集合
命令以 H开头
127.0.0.1:6379> hset map1 name gsh456 #设置一个值
(integer) 1
127.0.0.1:6379> HGET map1 name #获取一个值
"gsh456"
#批量设置 hmset key field1 value1 field2 value2 ...
127.0.0.1:6379> hmset map1 age 20 height 185cm wight 75kg
OK
#批量获取 hmget key field1 field2 ...
127.0.0.1:6379> hmget map1 name age
1) "gsh456"
2) "20"
127.0.0.1:6379> HGETALL map1 #获取集合的全部属性
1) "name"
2) "gsh456"
3) "age"
4) "20"
5) "height"
6) "185cm"
7) "wight"
8) "75kg"
127.0.0.1:6379> hdel map1 age #删除集合中的指定 字段
(integer) 1
127.0.0.1:6379> HGETALL map1
1) "name"
2) "gsh456"
3) "height"
4) "185"
5) "wight"
6) "75kg"
127.0.0.1:6379> HLEN map1 #获取集合多少个键值对。
(integer) 3
127.0.0.1:6379> HEXISTS map1 name #判断集合中的字段是否存在,1存在
(integer) 1
127.0.0.1:6379> HEXISTS map1 age #判断集合中的字段是否存在,1不存在
(integer) 0
127.0.0.1:6379> hkeys map1 #获取集合的所有key
1) "name"
2) "height"
3) "wight"
127.0.0.1:6379> hvalues map1
(error) ERR unknown command `hvalues`, with args beginning with: `map1`,
127.0.0.1:6379> hvals map1 #获取集合的所有值
1) "gsh456"
2) "185"
3) "75kg"
127.0.0.1:6379> HINCRBY map1 height 1 #集合中指定字段 +1
(integer) 186
127.0.0.1:6379> hvals map1
1) "gsh456"
2) "186"
3) "75kg"
127.0.0.1:6379> hvals map1
1) "gsh456"
2) "186"
3) "75kg"
127.0.0.1:6379> HINCRBY map1 height -2 #集合中指定字段 -2
(integer) 184
127.0.0.1:6379> hvals map1
1) "gsh456"
2) "184"
3) "75kg"
127.0.0.1:6379> HSETNX map1 age 20 #集合不存在则新增,1代表成功
(integer) 1
127.0.0.1:6379> HSETNX map1 age 21 #集合不存在则新增,0代表失败
(integer) 0
本质和string类型没有太大区别,还是一个简单的集合。
hash变更数据,user name age 和string 差不多。用于存储进行变动的数据。更适合对象的存储。
5Zset(有序集合)
在set基础上,增加一个值。
#集合增加一个或者多个元素,注意元素前面加数字
127.0.0.1:6379> zadd salary 3000 xiaoming 2500 xiaohong 4000 xiaoli #三个人和他们的工资
(integer) 3
127.0.0.1:6379> ZRANGE salary 0 -1 #查看全部
1) "xiaohong"
2) "xiaoming"
3) "xiaoli"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf #排序,-负无穷,和+无穷
1) "xiaohong"
2) "xiaoming"
3) "xiaoli"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores #排序,-负无穷,和+无穷 带成绩
1) "xiaohong"
2) "2500"
3) "xiaoming"
4) "3000"
5) "xiaoli"
6) "4000"
127.0.0.1:6379> zrem salary xiaohong #移除元素
(integer) 1
127.0.0.1:6379> ZRANGE salary 0 -1
1) "xiaoming"
2) "xiaoli"
127.0.0.1:6379> zcard salary #集合元素个数
(integer) 2
127.0.0.1:6379> ZREVRANGE salary 0 -1 #倒叙排列
1) "xiaoli"
2) "xiaoming"
127.0.0.1:6379> ZCOUNT salary 2000 4000 #获取指定区间的成员数量
(integer) 2
案例思路
set排序,存储班级层级,工资表
普通消息:1,重要消息:2。带权重的判断
排行榜应用实现。
三大特殊类型
6. geospatial 位置
# geoadd 添加地理位址
将指定的地理空间项目(纬度,经度,名称)添加到指定的键。数据作为一个排序集存储在密钥中,以便以后可以使用带有 GEORADIUS 或 GEORADIUSBYMEMBER 命令的半径查询来检索项目。
该命令采用标准格式 x,y 的参数,因此经度必须在纬度之前指定。可以索引的坐标是有限的:非常靠近极点的区域不可索引。EPSG:900913 / EPSG:3785 / OSGEO:41001的具体限制如下:
有效经度从-180到180度。
有效纬度从 -85.05112878 到 85.05112878 度。
当用户尝试索引超出指定范围的坐标时,该命令将报告错误。
注意:没有 GEODEL 命令,因为您可以使用 ZREM 来删除元素。地理索引结构只是一个有序集合。
6.1 geoadd-添加位置节点
127.0.0.1:6379> geoadd key longitude latitude member [longitude latitude member ...127.0.0.1:6379> geoadd key longitude latitude member [longitude latitude member ...127.0.0.1:6379> geoadd key longitude latitude member [longitude latitude member ...127.0.0.1:6379> geoadd
(error) ERR wrong number of arguments for 'geoadd' command
127.0.0.1:6379> geoadd china:city 116.408 39.904 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.445 31.213 shanghai 114.109 22.544 shenzhen
(integer) 2
127.0.0.1:6379> geoadd china:city 113.641 34.758 zhengzhou 108.969 34.285 xian
(integer) 2
127.0.0.1:6379> geoadd china:city 113.265 23.108 guangzhou 120.165 30.319 hangzhou
(integer) 2
127.0.0.1:6379> geoadd china:city 106.549 29.581 chongqing 121.576 38.944 dalian
(integer) 2
127.0.0.1:6379> geopos china:city beijing
1) 1) "116.40800267457962036"
2) "39.90399988166036138"
127.0.0.1:6379> geodist china:city beijing zhengzhou
"622398.3332"
127.0.0.1:6379> geodist china:city beijing zhengzhou km
"622.3983"
127.0.0.1:6379> geodist china:city beijing shanghai km
"1068.2320"
6.2 georadius-以指定坐标搜索位置
GEORADIUS:以给定的坐标为中心,寻找指定范围的位置列表,通过半径查找。
附近的人?附近的餐馆?
#获取坐标为 110 30 1000公里范围内的位置信息
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "guangzhou"
5) "hangzhou"
6) "zhengzhou"
#获取坐标为 110 30 500公里范围内的位置信息
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km
1) "chongqing"
2) "xian"
#获取坐标为 110 30 1000公里范围内的位置信息 并显示位置
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist
1) 1) "chongqing"
2) "336.3467"
2) 1) "xian"
2) "486.3850"
#获取坐标为 110 30 500公里范围内的位置 并显示经纬度
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withcoord
1) 1) "chongqing"
2) 1) "106.54900163412094116"
2) "29.58100070345364685"
2) 1) "xian"
2) 1) "108.96899789571762085"
2) "34.28499959898385896"
#获取坐标为 110 30 500公里的一个地址信息
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist count 1
1) 1) "chongqing"
2) "336.3467"
#获取坐标为 110 30 500公里的2个地址信息
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist count 2
1) 1) "chongqing"
2) "336.3467"
2) 1) "xian"
2) "486.3850"
#获取坐标为 110 30 500公里的3个地址信息
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist count 3
1) 1) "chongqing"
2) "336.3467"
2) 1) "xian"
2) "486.3850"
6.3 georadiusbymember
GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
以 一个元素的位置为中心,方圆距离内的距离信息
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km
1) "zhengzhou"
2) "beijing"
3) "dalian"
4) "xian"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 500 km
1) "beijing"
2) "dalian"
6.4 geohash--获取位置信息的hash值
GEOHASH key member [member ...]
获取元素的hash性质的坐标,从二维编程一维,hash值越像近说明,两个地点离得越近。
127.0.0.1:6379> GEOHASH china:city beijing zhengzhou
1) "wx4g0bm9xh0"
2) "ww0v9zwb500"
6.5 geo的删除和其他操作 zset
geo的底层实现原理是zset,我们可以使用zset命令来操作 geo元素
127.0.0.1:6379> ZRANGE china:city 0 -1 #获取城市列表
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "guangzhou"
5) "hangzhou"
6) "shanghai"
7) "zhengzhou"
8) "beijing"
9) "dalian"
127.0.0.1:6379> zrem china:city dalian #删除一个元素
(integer) 1
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "guangzhou"
5) "hangzhou"
6) "shanghai"
7) "zhengzhou"
8) "beijing"
2. hyperloglog
什么是基数?不重复元素的个数。
a{1,3,5,7,8}
b{1,3,5,7,8,8}
基数=5,可以接受误差。
redis的hyperloglog的有点
优点。占用的内存是固定的,例如存储 2^64的set,只需要12KB内存,如果从内存角度来比较的话hyperloglog是首选!
网页UV(一个人的访问一个网站,多次但是还是算作一次)
传统的解决方案是,例如set集合,保存用户的id,以set集合中的元素数量为标准判断。
这个方式如果保存大量的用户id,会比较麻烦,我们的目的是计数,而不是保存用户id。
但是官方说有0.81%的错误率。但是有些场景不用。
127.0.0.1:6379> pfadd h1 a b c d e f g h i # 新增、添加元素
(integer) 1
127.0.0.1:6379> PFCOUNT h1 #查看数量
(integer) 9
127.0.0.1:6379> pfadd h1 a b s #添加新元素
(integer) 1
127.0.0.1:6379> PFCOUNT h1 # 查看数量
(integer) 10
127.0.0.1:6379> pfadd h2 a b c 1 2 3 # 新增一个h2
(integer) 1
127.0.0.1:6379> pfcount h2
(integer) 6
127.0.0.1:6379> PFMERGE h3 h1 h2 # h1和h2合并为h3
OK
127.0.0.1:6379> PFCOUNT h3
(integer) 13
3. bitMap
位存储,两种数据状态的,非0即1。省空间
统计疫情感染人数:0 1 0 1 0,没有感染用0,感染用1
统计在线和离线,
使用bitmap来记录一周的打卡
1 setbit 添加
127.0.0.1:6379> SETBIT user:1 1 1 #用户1 周1 打卡
(integer) 0
127.0.0.1:6379> SETBIT user:1 2 0 #用户1 周2 未打卡
(integer) 0
127.0.0.1:6379> SETBIT user:1 3 0 #用户1 周3 未打卡
(integer) 0
127.0.0.1:6379> SETBIT user:1 4 1 #用户1 周4 打卡
(integer) 0
127.0.0.1:6379> SETBIT user:1 5 1 #用户1 周5 打卡
(integer) 0
127.0.0.1:6379> SETBIT user:1 6 0 #用户1 周6 未打卡
(integer) 0
127.0.0.1:6379> SETBIT user:1 7 0 #用户1 周7 未打卡
(integer) 0
2 getbit 数据获取
查看某一天是否打卡
127.0.0.1:6379> getbit user:1 2
(integer) 0
127.0.0.1:6379> getbit user:1 1
(integer) 1
3 bitcount 统计
统计打卡天数
127.0.0.1:6379> BITCOUNT user:1
(integer) 3
事务
mysql:acid,
原子性:要么全部成功,要么全部失败。
redis的单条命令是保证原子性的,但是redis的事务不是原子性的。
redis事务的本质:一组命令的集合。一个事务中的所有命令都会被序列化,按照顺序一次性执行完毕,排他性。
队列开始--序列化--set set ---执行---
redis 单挑命令,但是事务不保证原子性。
锁:redis可以实现乐观锁
redis的事务:
开启事务(multi)
命令入队(....)
执行事务(exec)
正常执行
127.0.0.1:6379> MULTI #开启事务
OK
127.0.0.1:6379> set k1 v1 #命令入队
QUEUED
127.0.0.1:6379> set k2 v2 #命令入队
QUEUED
127.0.0.1:6379> get k1 #命令入队
QUEUED
127.0.0.1:6379> set k3 v3 #命令入队
QUEUED
127.0.0.1:6379> exec #执行事务
1) OK
2) OK
3) "v1"
4) OK
放弃事务
127.0.0.1:6379> MULTI #开启事务
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> get k4
QUEUED
127.0.0.1:6379> DISCARD #取消事务
OK
127.0.0.1:6379> get k4
(nil)
编译时异常:代码有问题,编译器发现。事务中的所有命令都不会被执行。
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> getset k5 #编译异常
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k6 v6
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors. #事务失败
127.0.0.1:6379> get k6
(nil)
运行时异常(一除以零),逻辑有问题。那么执行命令的时候,其他命令是可以正常执行的,错误命令抛出异常!
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> set k1 "v1" #设置k1的值为v1
127.0.0.1:6379> MULTI #开启事务
OK
127.0.0.1:6379> incr k1 #对v1进行+1,由于v1是字符串,肯定报错
QUEUED
127.0.0.1:6379> set k2 v2 #设置其他值
QUEUED
127.0.0.1:6379> set k3 v3 #设置其他值
QUEUED
127.0.0.1:6379> get k3 #获取k3
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range
2) OK
3) OK
4) "v3"
127.0.0.1:6379> get k3 #很明显执行成功了
"v3"
127.0.0.1:6379> get k1
"v1"
watch--监控
悲观锁
认为什么时候都可能出问题,无论做什么都加锁!
乐观锁
认为认为什么时候都不一样出问题,所以不会枷锁。在更新数据的时候,判断一下,在此期间是否有人修改过这个数据。mysql表中加一个version进行监控。
获取version
更新的时候比较version
redis的监控测试
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money #简史money对象
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY out 20
QUEUED
127.0.0.1:6379> exec #事务正常结束,事务期间数据未发生任何变化,执行成功!
1) (integer) 80
2) (integer) 20
##############################多线程
127.0.0.1:6379> watch money #使用watch 进行乐观锁操作
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby out 10 #在未提交事务之前,另外一个线程2执行
QUEUED
127.0.0.1:6379> exec
(nil) # 事务提交失败
线程2
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 1000.
OK
重新获取锁
127.0.0.1:6379> UNWATCH #,如果发现事务执行失败,先解除乐观锁
OK
127.0.0.1:6379> watch money #获取最新的值,再次加乐观锁
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby money 5
QUEUED
127.0.0.1:6379> exec #如果加锁的key 没有发生变化,执行成功
1) (integer) 990
2) (integer) 995
乐观锁一般用于秒杀!
Jedis
redis for java,用java操作redis
什么是jedis,是redis官方推荐的java连接redis的工具。使用java操作redis的中间件。
导入对应的依赖
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
</dependencies>
编码测试
连接redis
Jedis j = new Jedis(“host”,""); j.ping(); //pong //所有的执行命令。
事务控制
public static void main(String[] args) { //1. new一个 jedis对象 Jedis jedis = new Jedis("127.0.0.1",6379); jedis.flushDB(); JSONObject jsonObject =new JSONObject(); jsonObject.put("hello","world"); jsonObject.put("name","gsh456"); Transaction multi = jedis.multi(); try { multi.set("user1",jsonObject.toJSONString()); //代码运行时异常,执行失败 int i = 1/0; multi.set("user2",jsonObject.toJSONString()); //执行事务 multi.exec(); }catch (Exception e){ //放弃事务 multi.discard(); }finally { System.out.println(jedis.get("user1")); // null System.out.println(jedis.get("user2")); // null //两个获取全部为空,代表事务全部执行失败 jedis.close(); } }
springBoot整合
springBoot 操作数据:spring-data-jpa/spring-data-jdbc/spring-data-mongodb
springData也整合了redis。
导入
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
spring 2. 0以后把原来的jedis改为了lettuce!
jedis:采用直连的方式,多线程操作的话是不安全的,如果避免这个问题,需要使用jedis-pool解决。更像BIO
模式。
lettuce:底层采用netty,速度快,实例可以在多个线程共享,不存在线程不安全,可以有效减少线程数量,更像NIO模式。
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")//自己定义
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
//默认的RedisTemplate 没有过多的设置,可以自己@bean 进行自定义,redis对象需要序列化
//两个对象都是object,一般key是string,每次强转太麻烦
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean //由于string类型最常使用的
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
配置文件
注意如果要配置连接池 spring.redis.lettuce.pool
,jedis的配置没有实现,不生效。
spring.redis.host=127.0.0.1
spring.redis.port=6379
#spring.redis.lettuce.pool.max-active=8 #需要写其他配置
测试代码
@Test
void contextLoads() {
//操作不通用的数据类型,redisTemplate.opsForGeo()
//opsForValue 操作string
//操作 opsForList list
//opsForSet
//opsForGeo
//处理基本的操作,常用的方法,都可以直接 redisTemplate进行操作。比例事务和基本的增删改查
// redisTemplate.multi();
// RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
// connection.flushDb();
final String key1 = "key1";
final String key2 = "key2";
redisTemplate.opsForValue().set(key1,"test1");
redisTemplate.opsForValue().set(key2,"这个是中文测试");
System.out.println(redisTemplate.opsForValue().get(key1));
System.out.println(redisTemplate.opsForValue().get(key2));
}
如果通过redis-cli获取key2会出现乱码!
127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\x04key2"
2) "\xac\xed\x00\x05t\x00\x04key1"
默认的序列化工具
序列化测试
@Test
public void test() throws JsonProcessingException {
final String key1 = "user1";
final String key2 = "user2";
//先清空数据库
redisTemplate.getConnectionFactory().getConnection().flushDb();
//一般的开发,都是用json保存对象
User gsh = new User("gsh", 3);
String jsonUser = new ObjectMapper().writeValueAsString(gsh);
redisTemplate.opsForValue().set(key1,jsonUser);
redisTemplate.opsForValue().set(key2,gsh);
System.out.println("redisTemplate.opsForValue().get(key) = " + redisTemplate.opsForValue().get(key1));
//报错 Failed to serialize object using DefaultSerializer ,user未实现 序列化接口,如果实现了 Serializable则不会报错
System.out.println("redisTemplate.opsForValue().get(key) = " + redisTemplate.opsForValue().get(key2));
}
自己配置序列化
redis配置文件详解
启动的时候就是通过配置文件进行启动的
单位,常用单位如下,并且对大小写不敏感
#常用单位
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
# 不区分大小写
# units are case insensitive so 1GB 1Gb 1gB are all the same.
包含其他配置文件,和nginx有点像
# 包含其他配置文件
# include /path/to/local.conf
# include /path/to/other.conf
NETWORK
# Examples: 绑定ip
# bind 192.168.1.100 10.0.0.1
# bind 127.0.0.1 ::1
保护模式
#默认开启,如果注释掉 bind的话,需要开启保护模式的话,需要配置密码
protected-mode yes
端口
#一般在配置集群的时候用到
port 6379
通用配置
#开启守护进程
daemonize yes
#配置文件的pid文件,如果daemonize=yes,需要这个文件
pidfile /var/run/redis_6379.pid
#日志的级别
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
loglevel notice
logfile "" #日志位置,全路径
databases 16 #数据库个数
always-show-logo yes #是否总是显示LOgo
SNAPSHOTTING
save,持久化,在执行多少次命令之后会进行持久化到文件 .rdb .aof两种持久化文件
#如果 900秒内,最少有一个key修改过数据,那么就就行持久化操作
save 900 1
#如果 300秒内,最少有10个key修改过数据,那么就就行持久化操作
save 300 10
#如果 60秒内,最少有10000个key修改过数据,那么就就行持久化操作
save 60 10000
#持久化错误之后是否继续工作
stop-writes-on-bgsave-error yes
#是否压缩 rdb文件,注意该操作消耗cpu的部分资源
rdbcompression yes
#保存rdb文件的时候 是否校验 rdb文件
rdbchecksum yes
#持久化文件(rdb)路径,默认是当前目录
dir ./
REPLICATION:复制,主从复制
SECURITY:安全
# requirepass foobared #连接密码
127.0.0.1:6379> config get requirepass #获取配置文件内容
1) "requirepass"
2) ""
127.0.0.1:6379> config set requirepass gsh456 #命令设置密码,这样的好处是 不用重启redis
OK
127.0.0.1:6379> ping
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth gsh456
OK
127.0.0.1:6379> ping
PONG
CLIENTS:客户端
# maxclients 10000 #最大连接数量
MEMORY MANAGEMENT :内存管理
# maxmemory <bytes> #最大内存占用
# maxmemory-policy noeviction #内存满的时候的处理策略
maxmemory-policy 六种方式
1、volatile-lru:只对设置了过期时间的key进行LRU(默认值)
2、allkeys-lru : 删除lru算法的key
3、volatile-random:随机删除即将过期key
4、allkeys-random:随机删除
5、volatile-ttl : 删除即将过期的
6、noeviction : 永不过期,返回错误
APPEND ONLY MODE :AOF设置
#默认不开启 aof,默认的使用的是rdb,大部分情况下 rdb够用
appendonly no
#持久化文件的名字
appendfilename "appendonly.aof"
# 持久化策略,默认每秒同步一次,可能丢失一秒的数据
# appendfsync always #比较消耗性能
appendfsync everysec
# appendfsync no
redis的持久化
redis的持久化分为两种,rdb和aof,默认采用rdb,aof关闭。
1 rdb
rdb保存的文件一般是 dump.rdb,
触发机制,save到指定条件,会触发rdb 规则,如果flushdb 和 shutdown 主动关闭,都会产生rdb文件,
dump.rdb文件,只要在redis的安装目录,redis将自动加载,rdb的文件位置在配置文件 dir ./
配置中,
优点:
适合大规模的数据恢复。
缺点:
redis意外宕机,可能导致最后的数据丢失
fork子进程(保存进程),可能占用更多的系统资源。
在生产环境,注意需要将 dump.rdb进行备份。
2 aof
APPEND ONLY MODE (append to file) 文件追加
本质上是吧所有命令都记录下来(只记录写,不记录读命令),恢复的时候就把所有命令在重新执行一次,如果是大数据恢复起来效率不高。
aop文件名字:appendonly.aof
该配置 默认不开启,改为 appendonly yes即可。其他配置尽量不要动。
两个是同时开启的。
appendonly no
# The name of the append only file (default: "appendonly.aof")
appendfilename "appendonly.aof" #文件位置
# appendfsync always
appendfsync everysec #每秒同步一次
# appendfsync no
no-appendfsync-on-rewrite no #延迟重写机制
auto-aof-rewrite-percentage 100 #
auto-aof-rewrite-min-size 64mb #aof文件 达到这个大小后,重写分割一个行文件
aop修复工具
aop有检查aop文件是否有错误并且自动修复策略,如果aof文件不正常,客户端将无法启动。
使用安装目录中/bin的 redis-check-aof
进行修复
#修复命令
./bin/redis-check-aof --fix appendonly.aof
优点
如果每次修改都同步,文件的完整性更好
如果每秒同步一次,可能丢失1s的数据
从不同步,效率最高
缺点
对于数据文件来说,aof文件更大,文件恢复的数据比rdb慢很多。
aop的io操作,读写效率比rdb要低。
发布订阅
订阅端
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> SUBSCRIBE gsh456
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "gsh456"
3) (integer) 1
#等待发布者发布信息
1) "message"
2) "gsh456"
3) "test1"
1) "message"
2) "gsh456"
3) "test2"
发布者
127.0.0.1:6379> PUBLISH gsh456 test1 #发布信息
(integer) 1
127.0.0.1:6379> PUBLISH gsh456 test2
(integer) 1
代码使用
主从复制
主从复制,是指将一台redis服务器的数据,复制到其他redis服务器,前者被称之为主节点(master/leader),后者被称之为从节点(slave/follower):==数据的复制是单向的==,只能由主节点复制到从节点。主节点以写为主,从节点以读为主。
默认情况下,每台redis服务器都是主节点;且一个主节点可以和多个从节点(或者没有从节点),但是一个从节点只有一个主节点。
主从复制的主要特点
数据冗余:主从复制实现了热备份,是持久化之外的一种数据冗余方式
数据故障:当主节点出了问题,可以由从节点提供服务,实现快速故障恢复;实际上是一种服务冗余。
副总均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,从节点提供读服务(即写redis数据的时候连接主节点,读redis数据的时候连接从节点)。分担服务器压力,尤其在写少读多的场景下,可以使用多从节点进行负载,可以大大提高读取效率和redis的并发量
高可用基石:除了上述作用外,主从复制还是哨兵模式能够实施的基础,也是redis高可用的基础。
一般来说,要将redis运用于项目中,只有一台redis服务器是万万不能的,原因如下:
从勾结上,单台redis服务发生单点故障,并且一台服务器处理所有redis请求的负载,压力很大。
从容量上,单台redis服务器的内存有限,一般来说redis最大使用内存在20G左右。
一般项目都是写少读多,主从模式可以大大提高缓存效率。
最低配: 一主二从,要不然主节点宕机后,根据哨兵模式,无法从从节点选举新的主节点。
环境配置
只用配从库,不用配主库!
127.0.0.1:6379> info replication
# Replication
role:master #当前服务的角色
connected_slaves:0 #没有从节点
master_replid:0513f36f35e2d8bcf6f7a0b5ef6b78fa30dd0917
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
#从上面的信息,可以看出来,默认自己就是主节点
主从配置
1 先停掉主服务,
2 打开从服务的端口
pidfile #pid文件
logfile # 日志文件位置
[root@ecs-90b1 redis]# cd ../
[root@ecs-90b1 local]# cp -r redis/ redis80 #复制三个从节点
[root@ecs-90b1 local]# cp -r redis/ redis81
[root@ecs-90b1 local]# cp -r redis/ redis82
[root@ecs-90b1 local]# ls
agentInstall.sh games influxdb-1.7.9.x86_64.rpm kafka_2.12-2.2.2 libexec nginx-1.17.10.tar.gz redis81 share telescope WWW
bin gsh456 java lib mysql redis redis82 src telescope_linux_amd64 xyjsoft
etc include jdk lib64 nginx redis80 sbin srs telescope_linux_amd64.tar.gz zookeeper-3.4.14
[root@ecs-90b1 local]# vim redis80/redis.conf # 修改从节点的 端口号和pid文件
客户端都准备好了
配置从节点的配置文件,让他们 认老大
[root@ecs-90b1 local]# ./redis/bin/redis-server ./redis/redis.conf #启动 6379节点
22780:C 17 Oct 2020 11:10:40.485 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
22780:C 17 Oct 2020 11:10:40.485 # Redis version=5.0.9, bits=64, commit=00000000, modified=0, pid=22780, just started
22780:C 17 Oct 2020 11:10:40.485 # Configuration loaded
[root@ecs-90b1 local]# ./redis80/bin/redis-server ./redis80/redis.conf #启动 6380节点
23307:C 17 Oct 2020 11:10:53.162 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
23307:C 17 Oct 2020 11:10:53.162 # Redis version=5.0.9, bits=64, commit=00000000, modified=0, pid=23307, just started
23307:C 17 Oct 2020 11:10:53.162 # Configuration loaded
[root@ecs-90b1 local]# ./redis81/bin/redis-server ./redis81/redis.conf #启动 6381节点
23986:C 17 Oct 2020 11:11:08.928 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
23986:C 17 Oct 2020 11:11:08.928 # Redis version=5.0.9, bits=64, commit=00000000, modified=0, pid=23986, just started
23986:C 17 Oct 2020 11:11:08.928 # Configuration loaded
[root@ecs-90b1 local]# ./redis82/bin/redis-server ./redis82/redis.conf #启动 6382节点
24400:C 17 Oct 2020 11:11:19.201 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
24400:C 17 Oct 2020 11:11:19.201 # Redis version=5.0.9, bits=64, commit=00000000, modified=0, pid=24400, just started
24400:C 17 Oct 2020 11:11:19.201 # Configuration loaded
[root@ecs-90b1 local]# ps -ef|grep redis
root 22781 1 0 11:10 ? 00:00:00 ./redis/bin/redis-server *:6379
root 23308 1 0 11:10 ? 00:00:00 ./redis80/bin/redis-server *:6380
root 23987 1 0 11:11 ? 00:00:00 ./redis81/bin/redis-server *:6381
root 24402 1 0 11:11 ? 00:00:00 ./redis82/bin/redis-server *:6382
root 24768 7355 0 11:11 pts/1 00:00:00 grep --color=auto redis
#从一主三从全部启动
认老大
[root@ecs-90b1 local]# ./redis80/bin/redis-cli -p 6380 #连接从
127.0.0.1:6380> SLAVEOF 127.0.0.1 6379 #认老大
OK
但是上面的命令指示暂时的,建议修改配置文件的方式。注意只用修改主节点
replicaof 127.0.0.1 6379 #主节点的地址和端口号
# masterauth <master-password> #主节点的密码
重新启动三个从节点
[root@ecs-90b1 local]# ./redis80/bin/redis-cli -p 6380
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:1106
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:b2b12f93ead19f85b4d1bf4dc0ce35a36c798e1c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1106
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:239
repl_backlog_histlen:868
[root@ecs-90b1 local]# ./redis/bin/redis-cli -p 6379
127.0.0.1:6379> info replication
# Replication 一主三从,可以看出已经配置好了
role:master
connected_slaves:3
slave0:ip=127.0.0.1,port=6380,state=online,offset=1162,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=1162,lag=0
slave2:ip=127.0.0.1,port=6382,state=online,offset=1162,lag=0
master_replid:b2b12f93ead19f85b4d1bf4dc0ce35a36c798e1c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1162
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1162
可以从信息中看出,一主三从已经配置好了
主机的配置文件中已经指定,从节点不允许写,所以从会报错。
关于主从的 springboot的配置
如果 主节点挂了,从节点依旧连接到主机的,但是没有写操作了。这个时候,主机重启,一切恢复。
如果从节点挂了一个,主节点一些没问题。从节点恢复,从节点
复制原理:
从节点连接到主节点后,会发送一个sync命令,
主节点接受到名将,启动后台存盘进程,同时手机所有接收到的用于修改的数据集命令,后台执行完成之后,主节点将传送整个数据文件到从节点,并完成一次完整同步。
全量复制:而从节点服务在接受到数据文件后,将其存盘并加载到内存中去。
增量复制:主节点将新的命令集传到到从节点,完成同步
只要重新连接到主节点,全量复制将会执行。
哨兵模式
自动选老大模式
概述:
主从切换技术的方法是:当服务器宕机后,需要手动把一台服务器切换为主服务器,这个需要人工干预,费时费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。redis2.8之后开始提供sentinel(哨兵)架构来解决泽恩问题。
哨兵模式是一种特殊的模式,首先redis提供了哨兵命令,哨兵是一个独立的进程,作为进程,他会独立运行。其原理是哨兵通过发送命令,等待redis服务响应,从而监控多个运行的redis实例。如果redis主服务宕机,自动根据投票数将从节点变成主节点。
哨兵模式的流程图。
运行程序在 redis的安装目录的 bin中redis-sentinel
哨兵作为一个进程,也可能挂掉,一般配置的为双哨兵或者多哨兵,形成多哨兵模式。
1.新建哨兵的配置文件
[root@ecs-90b1 redis]# vim sentinel.conf
# sentinel monitor 任意名字 redis信息,1启用投票模式
sentinel monitor myredis 127.0.0.1 6379 1
启动哨兵
[root@ecs-90b1 redis]# ./bin/redis-sentinel ./sentinel.conf
启动成功,
手动关闭主节点
[root@ecs-90b1 redis]# ./bin/redis-cli -p 6379
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:3
slave0:ip=127.0.0.1,port=6380,state=online,offset=19235,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=19235,lag=1
slave2:ip=127.0.0.1,port=6382,state=online,offset=19235,lag=1
master_replid:b2b12f93ead19f85b4d1bf4dc0ce35a36c798e1c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:19235
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:19235
127.0.0.1:6379> shutdown
not connected> exit
哨兵监视到主节点挂了,就会从从节点选举,投票最多的变成新的主节点。
如果这时再次启动主节点 -6379
如果6379主节点再次启动,那么将会变成新主节点的从节点。
优点:
拥有主从复制的优点
主从自动切换,故障转移。
哨兵模式是主从复制的升级版。
缺点
redis不好在线扩容,如果集群容量一旦到达上限,在线扩容就十分麻烦。
实现哨兵模式的配置很麻烦,里面有很多选择。
哨兵模式的全部配置文件
评论区