Redis(Remote Dictionary Server ),即远程字典服务 !
是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库, 并提供多种语言的API。
Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
免费和开源!是当下最热门的 NoSQL 技术之一!也被人们称之为结构化数据库!
分类 | Examples举例 | 典型应用场景 | 数据模型 | 优点 | 缺点 |
---|---|---|---|---|---|
KV键值(key-value) | Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB | 内容缓存,主要用于处理大量数据的高访问负载,也用于一些日志系统等等。 | Key 指向 Value 的键值对,通常用hash table来实现 | 查找速度快 | 数据无结构化,通常只被当作字符串或者二进制数据 |
列存储数据库 | Cassandra, HBase, Riak | 分布式的文件系统 | 以列簇式存储,将同一列数据存在一起 | 查找速度快,可扩展性强,更容易进行分布式扩展 | 功能相对局限 |
文档型数据库 | CouchDB, MongoDb | Web应用(与Key-Value类似,Value是结构化的,不同的是数据库能够了解Value的内容) | Key-Value对应的键值对,Value为结构化数据 | 数据结构要求不严格,表结构可变,不需要像关系型数据库一样需要预先定义表结构 | 查询性能不高,而且缺乏统一的查询语法。 |
图形(Graph)数据库 | Neo4J, InfoGrid, Infinite Graph | 社交网络,推荐系统等。专注于构建关系图谱 | 图结构 | 利用图结构相关算法。比如最短路径寻址,N度关系查找等 | 很多时候需要对整个图做计算才能得出需要的信息,而且这种结构不太好做分布式的集群方案。 |
使用docker-compose的方式安装
version: "3.1"
services:
redis:
image: redis:latest
container_name: redis
ports:
- 6379:6379
volumes:
- ./data:/data
command: redis-server --requirepass 123456 #你的密码
docker exec -it redis redis-cli #docker进入redis
127.0.0.1:6379> PING #ping操作,ping通会显示PONG
(error) NOAUTH Authentication required. #没有权限
127.0.0.1:6379> auth 123456 #输入密码(如果设置了密码)
OK
127.0.0.1:6379> PING #此时ping通了
PONG
127.0.0.1:6379> CONFIG GET requirepass #获取密码
1) "requirepass"
2) "123456"
127.0.0.1:6379> CONFIG SET requirepass 654321 #重设密码
OK
127.0.0.1:6379> CONFIG GET requirepass
1) "requirepass"
2) "654321"
127.0.0.1:6379> SET name chen
OK
127.0.0.1:6379> EXISTS name #是否存在指定的key,存在返回1,不存在返回0
(integer) 1
127.0.0.1:6379> EXPIRE name 100 #设置某个key的过期时间 单位:秒
(integer) 1
127.0.0.1:6379> TTL name #查看剩余时间,当key不存在时,返回-2;存在但没有设置剩余生存时间时,返回-1,否则以秒为单位返回key剩余生存时间
(integer) 95
127.0.0.1:6379> PERSIST name #取消过期时间
(integer) 1
127.0.0.1:6379> TTL name
(integer) -1
127.0.0.1:6379> PEXPIRE name 100000 #修改key的过期时间为毫秒
(integer) 1
127.0.0.1:6379> DEL name #删除key
(integer) 1
127.0.0.1:6379> SELECT 1 #选择数据库1 数据库为0-15(默认一共16个数据库,设计多数据库实际上是为了数据库安全和备份)
OK
127.0.0.1:6379[1]> MOVE name 2 #将当前数据库中的key转移到2数据库
(integer) 1
127.0.0.1:6379> FLUSHDB #清空当前数据库
OK
127.0.0.1:6379> FLUSHALL #清空所有数据库
OK
127.0.0.1:6379> SET k1 v1
OK
127.0.0.1:6379> SET k2 v2
OK
127.0.0.1:6379> SET k3 v3
OK
127.0.0.1:6379> RANDOMKEY #随机返回一个key
"k2"
127.0.0.1:6379> RANDOMKEY
"k3"
127.0.0.1:6379> SET a 1
OK
127.0.0.1:6379> RENAME a b #重命名key
OK
127.0.0.1:6379> keys * #返回满足的所有键,可以模糊匹配
1) "b"
127.0.0.1:6379> type b #查看key的类型
string
127.0.0.1:6379> SET age 1
OK
127.0.0.1:6379> TYPE age
string
127.0.0.1:6379> DBSIZE #查看数据库的key数量
(integer) 2
127.0.0.1:6379> INFO #查看数据库信息
# Server
redis_version:6.0.1
redis_git_sha1:00000000
redis_git_dirty:0
...
127.0.0.1:6379> ECHO hello #打印命令
"hello"
127.0.0.1:6379> CONFIG GET * #实时传储收到的请求,返回相关的配置
1) "rdbchecksum"
2) "yes"
3) "daemonize"
...
redis 单个 key 允许存入512M大小
string 类型是最基本的数据类型,一个键最大能存储512MB.
string 数据结构是简单的 key-value 类型, value 其不仅是 string ,也可以是数字,是包含很多类型的特殊类型
string 类型是二进制安全的. 意思是 redis 的 string 可以包含任何数据
比如序列化的对象进行存储,比如一张图片进行二进制存储,比如一个简单的字符串,数值等等
127.0.0.1:6379> SET name chen #给key设置值
OK
127.0.0.1:6379> GET name #获取key的值
"chen"
127.0.0.1:6379> APPEND name gp #给key追加值,没有就创建
(integer) 6
127.0.0.1:6379> STRLEN name #获取key对应值的长度
(integer) 6
127.0.0.1:6379> GET name
"chengp"
127.0.0.1:6379> SET age 18
OK
127.0.0.1:6379> INCR age #将key对应的值加1(值必须是integer类型)
(integer) 19
127.0.0.1:6379> DECR age #将key对应的值减1(值必须是integer类型)
(integer) 18
127.0.0.1:6379> INCRBY age 10 #将key对应的值加10(值必须是integer类型)
(integer) 28
127.0.0.1:6379> DECRBY age 5 #将key对应的值减5(值必须是integer类型)
(integer) 23
127.0.0.1:6379> GETSET name manaphy #给key重新设置值(键必须存在) 返回旧值
"chengp"
127.0.0.1:6379> GETSET test hi
(nil)
127.0.0.1:6379> SETNX str hello #给key设置值(键必须不存在)
(integer) 1
127.0.0.1:6379> SETNX name hello
(integer) 0
127.0.0.1:6379> SETEX str 10 java #给key设置过期时间10秒并设置值(即10秒后该键会被删除)
OK
127.0.0.1:6379> SETRANGE name 1 e #设置key指定位置的字符
(integer) 7
127.0.0.1:6379> GET name
"menaphy"
127.0.0.1:6379> GETRANGE name 2 4 #获取key指定位置的字符
"nap"
127.0.0.1:6379> MSET k1 v1 k2 v2 k3 v3 #批量设置值
OK
127.0.0.1:6379> MGET k1 k2 k3 #批量获取值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> MSETNX k1 v1 k2 v2
(integer) 0
127.0.0.1:6379> MSETNX k4 v4 k5 v5 #批量设置值(键必须不存在)
(integer) 1
普通的链表结构
可以在头部或者尾部添加新的元素
和java中的集合概念差不多很类似
127.0.0.1:6379> LPUSH list a b c d #从list头部插入数据 可以一次性插入多个值
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1 #获取list的所有值
1) "d"
2) "c"
3) "b"
4) "a"
127.0.0.1:6379> LRANGE list 0 2 #获取指定坐标范围的值
1) "d"
2) "c"
3) "b"
127.0.0.1:6379> LRANGE list -3 -2 #获取指定坐标范围的值(从尾部读取 从-1开始)
1) "c"
2) "b"
127.0.0.1:6379> LPUSHX list e #往list头部插入数据(list必须存在)
(integer) 5
127.0.0.1:6379> LRANGE list 0 -1
1) "e"
2) "d"
3) "c"
4) "b"
5) "a"
127.0.0.1:6379> LPOP list #弹出list第一个值(如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止)
"e"
127.0.0.1:6379> LLEN list #获取list的长度
(integer) 4
127.0.0.1:6379> LREM list 2 a # 删除list中的元素 2代表个数 从头部开始删除(删除全部使用del key)
(integer) 1
127.0.0.1:6379> LINDEX list 1 #获取list位置1的值 (类似于list.get(1)方法)
"c"
127.0.0.1:6379> LSET list 1 e # 向list位置1插入值(会替换原来的值)
OK
127.0.0.1:6379> LRANGE list 0 -1
1) "d"
2) "e"
3) "b"
127.0.0.1:6379> LINSERT list BEFORE e z #向list第1个e之前插入z
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "d"
2) "z"
3) "e"
4) "b"
127.0.0.1:6379> LINSERT list AFTER e z #向list第1个e之后插入z
(integer) 5
127.0.0.1:6379> LRANGE list 0 -1
1) "d"
2) "z"
3) "e"
4) "z"
5) "b"
127.0.0.1:6379> LTRIM list 0 2 #通过下标截取list,此list只剩下被截取后的元素
OK
127.0.0.1:6379> LRANGE list 0 -1
1) "d"
2) "z"
3) "e"
127.0.0.1:6379> RPUSH list a b c #从list尾部插入数据
(integer) 6
127.0.0.1:6379> LRANGE list 0 -1
1) "d"
2) "z"
3) "e"
4) "a"
5) "b"
6) "c"
#其余R开头的命令与L相似相反
127.0.0.1:6379> RPOPLPUSH list list1 #从list中弹出最后一个值并往list1头部插入(没有lpushrpop)
"c"
127.0.0.1:6379> LRANGE list 0 -1
1) "d"
2) "z"
3) "e"
4) "a"
5) "b"
127.0.0.1:6379> LRANGE list1 0 -1
1) "c"
Redis 的 Set 是 String 类型的无序集合.集合成员是唯一的,这就意味着集合中不能出现重复的数据.
Redis 中集合是通过哈希表实现的, set 是通过 hashtable实现的
127.0.0.1:6379> SADD set a b c d #向set集合添加元素 可以一次性插入多个值
(integer) 4
127.0.0.1:6379> SMEMBERS set #查看set集合元素
1) "a"
2) "d"
3) "c"
4) "b"
127.0.0.1:6379> SCARD set #获取set集合的长度
(integer) 4
127.0.0.1:6379> SISMEMBER set a #判断set集合中是否存在某元素
(integer) 1
127.0.0.1:6379> SRANDMEMBER set 2 #随机返回2个元素(不写数字默认返回一个)
1) "a"
2) "b"
127.0.0.1:6379> SRANDMEMBER set
1) "c"
127.0.0.1:6379> SPOP set 2 #从set集合中随机弹出2个元素(不写数字默认弹出一个)
1) "a"
2) "c"
127.0.0.1:6379> SMEMBERS set #只剩2个元素
1) "d"
2) "b"
127.0.0.1:6379> SREM set d #删除指定元素(可删除多个)
(integer) 1
127.0.0.1:6379> SMEMBERS set
1) "b"
127.0.0.1:6379> SMOVE set set1 d #将set中的d移动到set1
(integer) 1
127.0.0.1:6379> FLUSHDB
OK
127.0.0.1:6379> SADD set a b c d
(integer) 4
127.0.0.1:6379> SADD set1 b c d e
(integer) 4
127.0.0.1:6379> SDIFF set set1 #获取两个(或多个)集合中的差集
1) "a"
127.0.0.1:6379> SDIFF set1 set #获取两个(或多个)集合中的差集
1) "e"
127.0.0.1:6379> SDIFFSTORE diffset set set1 #将差集保存到新的集合中
(integer) 1
127.0.0.1:6379> SMEMBERS diffset
1) "a"
127.0.0.1:6379> SINTER set set1 #获取两个(多个)集合中的交集
1) "d"
2) "c"
3) "b"
127.0.0.1:6379> SINTERSTORE interset set set1 # 将交集保存到新的集合中
(integer) 3
127.0.0.1:6379> SMEMBERS interset
1) "d"
2) "c"
3) "b"
127.0.0.1:6379> SUNION set set1 #获取两个(多个)集合中的并集
1) "b"
2) "d"
3) "e"
4) "a"
5) "c"
127.0.0.1:6379> SUNIONSTORE unionset set set1 #将并集保存到新的集合中
(integer) 5
127.0.0.1:6379> SMEMBERS unionset
1) "b"
2) "d"
3) "e"
4) "a"
5) "c"
常应用于: 对两个集合间的数据[计算]进行交集 并集 差集运算
Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
有序集合的成员是唯一的,但分数(score)却可以重复。
作用:用于大型游戏积分排行榜,每当玩家的分数发生变化时候,可以执行zadd命令更新玩家的分数,此后在通过zrange命令获取积分前10的用户信息。
127.0.0.1:6379> ZADD salary 2000 chen #向zset中添加成员
(integer) 1
127.0.0.1:6379> ZADD salary 2100 zhangsan 2200 lisi #向zset中添加多个成员
(integer) 2
127.0.0.1:6379> ZRANGE salary 0 -1 #通过索引获取成员
1) "chen"
2) "zhangsan"
3) "lisi"
127.0.0.1:6379> ZRANGE salary 0 -1 WITHSCORES #通过索引获取成员和分数
1) "chen"
2) "2000"
3) "zhangsan"
4) "2100"
5) "lisi"
6) "2200"
127.0.0.1:6379> ZCARD salary #获取zset中成员的数量
(integer) 3
127.0.0.1:6379> ZRANK salary chen # 获取成员在zset中的索引
(integer) 0
127.0.0.1:6379> ZCOUNT salary 2000 2100 #获取符合条件的成员数量,分数表达式左右都包含
(integer) 2
127.0.0.1:6379> ZSCORE salary chen #获取成员分数
"2000"
127.0.0.1:6379> ZINCRBY salary 1000 chen #将成员分数+1000
"3000"
127.0.0.1:6379> ZRANGEBYSCORE salary 2200 3000 #通过分数获取成员
1) "lisi"
2) "chen"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf #显示所有成员,从小到大排序
1) "zhangsan"
2) "lisi"
3) "chen"
127.0.0.1:6379> ZREVRANGE salary 0 -1 #显示所有成员,从大到小排序
1) "chen"
2) "lisi"
3) "zhangsan"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf WITHSCORES LIMIT 1 2 #-inf 表示是第一个成员 +inf表示最后一个成员 limit返回成员数量
1) "lisi"
2) "2200"
3) "chen"
4) "3000"
127.0.0.1:6379> ZREM salary lisi #删除成员(可以同时删除多个)
(integer) 1
127.0.0.1:6379> ZREMRANGEBYSCORE salary 2000 2100 #根据分数删除成员
(integer) 1
127.0.0.1:6379> ZREMRANGEBYRANK salary 0 0 #根据索引删除成员
(integer) 1
Hash类型是 String 类型的 field 和 value 的映射表,或者说是一个 String 集合. hash 特别适合用于存储对象,相比较而言, 将一个对象类型存储在 Hash 类型要比存储在 String 类型里占用更少的内存空间,并对整个对象的存取,可以看成具有 key 和 value 的 Map 容器, 该类型非常适合于存储值对象的信息,
如: uname,upass,age等.该类型的数据仅占用很少的磁盘空间(相比于JSON).
Redis 中每个 hash 可以存储 2^32 -1 键值对
127.0.0.1:6379> HSET hash name chen #给hash的键设置值
(integer) 1
127.0.0.1:6379> HGET hash name #获取hash的键对应的值
"chen"
127.0.0.1:6379> HLEN hash #获取hash的长度
(integer) 1
127.0.0.1:6379> HEXISTS hash name #判断hash是否存在
(integer) 1
127.0.0.1:6379> HDEL hash name #删除hash对应的键
(integer) 1
127.0.0.1:6379> HSETNX hash name chen #给hash的键设置值(键必须不存在)
(integer) 1
127.0.0.1:6379> HSETNX hash age 18
(integer) 1
127.0.0.1:6379> HLEN hash
(integer) 2
127.0.0.1:6379> HINCRBY hash age 1 #按照一定步长增加
(integer) 19
127.0.0.1:6379> HINCRBY hash age -2 #负数代表减少(没有HDECYBY)
(integer) 17
127.0.0.1:6379> HMSET hash k1 v1 k2 v2 #批量给hash设置键值
OK
127.0.0.1:6379> HMGET hash k1 k2 age name #批量获取hash中键对应的值
1) "v1"
2) "v2"
3) "17"
4) "chen"
127.0.0.1:6379> HGETALL hash #获取所有hash中的键值对
1) "name"
2) "chen"
3) "age"
4) "17"
5) "k1"
6) "v1"
7) "k2"
8) "v2"
127.0.0.1:6379> HVALS hash #仅获取hash键中所有的值
1) "chen"
2) "17"
3) "v1"
4) "v2"
127.0.0.1:6379> HKEYS hash #仅获取hash中的所有键
1) "name"
2) "age"
3) "k1"
4) "k2"
添加地理位置
规则:两级无法直接添加,一般会下载城市数据,直接通过java程序一次性导入!
有效的经度从-180度到180度。
有效的纬度从-85.05112878度到85.05112878度。
127.0.0.1:6379> GEOADD china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379> GEOADD china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379> GEOADD china:city 106.50 29.53 chongqing 114.05 22.52 shengzhen
(integer) 2
127.0.0.1:6379> GEOADD china:city 120.16 30.24 hangzhou 108.96 34.26 xian
(integer) 2
获取指定的城市的经度和纬度!
127.0.0.1:6379> GEOPOS china:city beijing
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
127.0.0.1:6379> GEOPOS china:city shanghai hangzhou
1) 1) "121.47000163793563843"
2) "31.22999903975783553"
2) 1) "120.1600000262260437"
2) "30.2400003229490224"
获取两地之间的距离
单位:
127.0.0.1:6379> GEODIST china:city beijing chongqing km
"1464.0708"
127.0.0.1:6379> GEODIST china:city shanghai hangzhou km
"166.7613"
以给定的经纬度为中心, 找出某一半径内的元素
127.0.0.1:6379> GEORADIUS china:city 120 28 500 km #以(120,28)这个坐标寻找方圆500km的城市
1) "hangzhou"
2) "shanghai"
127.0.0.1:6379> GEORADIUS china:city 120 28 500 km WITHDIST #显示到目标之间的距离
1) 1) "hangzhou"
2) "249.6314"
2) 1) "shanghai"
2) "386.3449"
127.0.0.1:6379> GEORADIUS china:city 120 28 500 km WITHCOORD #显示目标的经纬度
1) 1) "hangzhou"
2) 1) "120.1600000262260437"
2) "30.2400003229490224"
2) 1) "shanghai"
2) 1) "121.47000163793563843"
2) "31.22999903975783553"
127.0.0.1:6379> GEORADIUS china:city 120 28 500 km WITHDIST WITHCOORD count 1 #显示指定的个数
1) 1) "hangzhou"
2) "249.6314"
3) 1) "120.1600000262260437"
2) "30.2400003229490224"
找出位于指定元素周围的其他元素!
127.0.0.1:6379> GEORADIUSBYMEMBER china:city hangzhou 500 km
1) "hangzhou"
2) "shanghai"
返回一个或多个位置元素的 geohash 表示
# 将二维的经纬度转换为一维的字符串,如果两个字符串越接近,那么则距离越近!
127.0.0.1:6379> GEOHASH china:city hangzhou shanghai
1) "wtmkn31bfb0"
2) "wtw3sj5zbj0"
GEO 底层的实现原理其实就是 Zset!我们可以使用Zset命令来操作geo!
127.0.0.1:6379> ZRANGE china:city 0 -1 # 查看地图中全部的元素
1) "chongqing"
2) "xian"
3) "shengzhen"
4) "hangzhou"
5) "shanghai"
6) "beijing"
127.0.0.1:6379> ZREM china:city xian # 移除指定的元素
(integer) 1
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "chongqing"
2) "shengzhen"
3) "hangzhou"
4) "shanghai"
5) "beijing"
比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。
Redis 在 2.8.9 版本添加了 HyperLogLog 结构。
Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。
在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。
但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。
127.0.0.1:6379> PFADD key1 a b c d c b # 创建第一组元素 key1
(integer) 1
127.0.0.1:6379> PFCOUNT key1 # 统计 key1 元素的基数数量
(integer) 4
127.0.0.1:6379> PFADD key2 d e f # 创建第二组元素 key2
(integer) 1
127.0.0.1:6379> PFCOUNT key2
(integer) 3
127.0.0.1:6379> PFMERGE key3 key1 key2 #合并 key1 key2 到 key3 (并集)
OK
127.0.0.1:6379> PFCOUNT key3 #统计并集的数量
(integer) 6
注: Hyperloglog 有 0.81% 错误率!
统计用户信息,活跃,不活跃! 登录 、 未登录! 打卡,365打卡! 只有两个状态的都可以使用
使用 bitmap 来记录一周的打卡
127.0.0.1:6379> SETBIT daka 0 1
(integer) 0
127.0.0.1:6379> SETBIT daka 1 0
(integer) 0
127.0.0.1:6379> SETBIT daka 2 1
(integer) 0
127.0.0.1:6379> SETBIT daka 3 1
(integer) 0
127.0.0.1:6379> SETBIT daka 4 0
(integer) 0
127.0.0.1:6379> SETBIT daka 5 1
(integer) 0
127.0.0.1:6379> SETBIT daka 6 0
(integer) 0
127.0.0.1:6379> GETBIT daka 3 # 查看某天的打卡情况
(integer) 1
127.0.0.1:6379> BITCOUNT daka # 统计这周的打卡记录
(integer) 4
一组命令的集合! 一个事务中的所有命令都会被序列化,在事务执行过程的中,会按照顺序执行!
Redis事务没有没有隔离级别的概念!
所有的命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行!Exec
Redis单条命令式保存原子性的,但是事务不保证原子性!
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 k2
QUEUED
127.0.0.1:6379> SET k3 v3
QUEUED
127.0.0.1:6379> EXEC #执行事物
1) OK
2) OK
3) "v2"
4) OK
#------------放弃事物----------
127.0.0.1:6379> FLUSHDB
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> DISCARD #取消事物
OK
127.0.0.1:6379> GET k2 #事务队列中命令都不会被执行
(nil)
#------------编译型异常----------
127.0.0.1:6379> FLUSHDB
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> GETSET k2 #错误的命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> SET k3 v3
QUEUED
127.0.0.1:6379> EXEC #执行事物报错
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> GET k1 #所有的命令都不会被执行
(nil)
#------------运行时异常----------
127.0.0.1:6379> FLUSHDB
OK
127.0.0.1:6379> SET k1 v1
OK
127.0.0.1:6379> MULTI #开启事物
OK
127.0.0.1:6379> INCR k1 #会执行报错的命令
QUEUED
127.0.0.1:6379> SET k2 v2
QUEUED
127.0.0.1:6379> GET k2
QUEUED
127.0.0.1:6379> EXEC #执行事物 虽然第一条命令错误,但其余命令执行成功
1) (error) ERR value is not an integer or out of range
2) OK
3) "v2"
127.0.0.1:6379> GET k2
"v2"
#正常执行成功案例
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
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
QUEUED
127.0.0.1:6379> EXEC #在执行之前另一个线程修改了money值,就会导致事物执行失败
(nil)
127.0.0.1:6379> UNWATCH #执行失败后,先解锁再次watch money 直到执行成功
OK
接收端
127.0.0.1:6379> SUBSCRIBE chen
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "chen"
3) (integer) 1
1) "message"
2) "chen"
3) "hello"
1) "message"
2) "chen"
3) "world"
发送端 (重开一个客户端)
127.0.0.1:6379> PUBLISH chen hello
(integer) 1
127.0.0.1:6379> PUBLISH chen world
(integer) 1
Redis 是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失。所以 Redis 提供了持久化功能!
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里。
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的。 这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。我们默认的就是RDB,一般情况下不需要修改这个配置!
127.0.0.1:6379> CONFIG GET dir
1) "dir"
2) "/data"
以日志的形式来记录每个写操作,将Redis执行过的所有指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作
RDB 持久化方式能够在指定的时间间隔内对你的数据进行快照存储
AOF 持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以Redis 协议追加保存每次写的操作到文件末尾,Redis还能对AOF文件进行后台重 写,使得AOF文件的体积不至于过大。
只做缓存,如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化
同时开启两种持久化方式
性能建议
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower);数据的复制是单向的,只能由主节点到从节点。Master以写为主,Slave 以读为主。
默认情况下,每台Redis服务器都是主节点
且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。
主从复制的作用主要包括:
数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务 的冗余。
负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务
(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
高可用(集群)基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复 制是Redis高可用的基础。
docker-compose.yml
version: '3'
services:
master:
image: redis
container_name: redis-master
command: redis-server --requirepass 123456 --masterauth 123456
ports:
- 6380:6379
slave1:
image: redis
container_name: redis-slave-1
ports:
- 6381:6379
command: redis-server --slaveof redis-master 6379 --requirepass 123456 --masterauth 123456
slave2:
image: redis
container_name: redis-slave-2
ports:
- 6382:6379
command: redis-server --slaveof redis-master 6379 --requirepass 123456 --masterauth 123456
验证
主服务器
docker exec -it redis-master redis-cli
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> set key chen
OK
127.0.0.1:6379> INFO replication
# Replication
role:master
connected_slaves:2 #可以看到有两个从节点
slave0:ip=172.18.0.3,port=6379,state=online,offset=1343,lag=0
slave1:ip=172.18.0.2,port=6379,state=online,offset=1343,lag=0
master_replid:54d035f0e0eb1d4539ec132d4545827a7a345f7b
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1343
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1343
从服务器
docker exec -it redis-slave-1 redis-cli
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> set k1 v1 #可以看到从服务器不能写入
(error) READONLY You can't write against a read only replica.
127.0.0.1:6379> get key
"chen"
127.0.0.1:6379> INFO replication
# Replication
role:slave
master_host:redis-master
master_port:6379
master_link_status:up
master_last_io_seconds_ago:4
master_sync_in_progress:0
slave_repl_offset:1455
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:54d035f0e0eb1d4539ec132d4545827a7a345f7b
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1455
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1455
主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。Redis从2.8开始正式提供了Sentinel(哨兵) 架构来解决这个问题。
谋朝篡位的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
这里的哨兵有两个作用
然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。
假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一 定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover[故障转移]操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线。
docker-compose.yml
version: '3.4'
services:
sentinel1:
image: redis
container_name: redis-sentinel-1
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
restart: always
ports:
- 26379:26379
volumes:
- ./sentinel1.conf:/usr/local/etc/redis/sentinel.conf
sentinel2:
image: redis
container_name: redis-sentinel-2
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
restart: always
ports:
- 26380:26379
volumes:
- ./sentinel2.conf:/usr/local/etc/redis/sentinel.conf
sentinel3:
image: redis
container_name: redis-sentinel-3
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
restart: always
ports:
- 26381:26379
volumes:
- ./sentinel3.conf:/usr/local/etc/redis/sentinel.conf
sentinel.conf
port 26379
dir /tmp
sentinel monitor mymaster 192.168.174.129 6379 2 #自己的服务器ip
sentinel auth-pass mymaster 123456
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
sentinel1.conf sentinel2.conf sentinel3.conf 的内容同sentinel.conf
总目录结构
.
├── redis/
│ └── docker-compose.yml #redis集群
└── sentinel/
├── docker-compose.yml #sentinel集群
├── sentinel1.conf #对应的三个配置文件
├── sentinel2.conf
├── sentinel3.conf
└── sentinel.conf
docker ps
效果
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ca316e86aac0 redis "docker-entrypoint.s…" 9 minutes ago Up 9 minutes 6379/tcp, 0.0.0.0:26381->26379/tcp redis-sentinel-3
a230eac1ee21 redis "docker-entrypoint.s…" 9 minutes ago Up 9 minutes 6379/tcp, 0.0.0.0:26380->26379/tcp redis-sentinel-2
24c3df4c2658 redis "docker-entrypoint.s…" 9 minutes ago Up 9 minutes 6379/tcp, 0.0.0.0:26379->26379/tcp redis-sentinel-1
e37a8be2acc0 redis "docker-entrypoint.s…" 59 minutes ago Up 59 minutes 0.0.0.0:6380->6379/tcp redis-master
494ee3c3744f redis "docker-entrypoint.s…" 59 minutes ago Up 59 minutes 0.0.0.0:6382->6379/tcp redis-slave-2
9f7efa9e3d7f redis "docker-entrypoint.s…" 59 minutes ago Up 59 minutes 0.0.0.0:6381->6379/tcp redis-slave-1
哨兵模式的全部配置
# Example sentinel.conf
# 哨兵sentinel实例运行的端口 默认26379 port 26379
# 哨兵sentinel的工作目录
dir /tmp
# 哨兵sentinel监控的redis主节点的 ip port
# master-name 可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。# quorum 配置多少个sentinel哨兵统一认为master主节点失联 那么这时客观上认为主节点失联了# sentinel monitor
sentinel monitor mymaster 127.0.0.1 6379 2
# 当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供密码
# 设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码
# sentinel auth-pass
sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
# 指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒
# sentinel down-after-milliseconds sentinel down-after-milliseconds mymaster 30000
# 这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步, 这个数字越小,完成failover所需的时间就越长,
但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。
可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
# sentinel parallel-syncs sentinel parallel-syncs mymaster 1
# 故障转移的超时时间 failover-timeout 可以用在以下这些方面:
#1. 同一个sentinel对同一个master两次failover之间的间隔时间。
#2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。
#3.当想要取消一个正在进行的failover所需要的时间。
#4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时, slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
# 默认三分钟
# sentinel failover-timeout
sentinel failover-timeout mymaster 180000
# SCRIPTS EXECUTION
#配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知 相关人员。
#对于脚本的运行结果有以下规则:
#若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10 #若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
#如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
#一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
#通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等), 将会去调用这个脚本,这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,一个是事件的类型,一个是事件的描述。如果sentinel.conf配 置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无 法正常启动成功。
#通知脚本
# shell编程
# sentinel notification-script sentinel notification-script mymaster /var/redis/notify.sh
# 客户端重新配置主节点参数脚本
# 当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。
# 以下参数将会在调用脚本时传给脚本:
# # 目前总是“failover”,
# 是“leader”或者“observer”中的一个。
# 参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的
# 这个脚本应该是通用的,能被多次调用,不是针对性的。
# sentinel client-reconfig-script
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh # 一般都是由运维来配置!