四大分类:
KV键值对:Redis
文档型数据库:MongoDB处理大量文档
Redis:
清除当前数据库
Flushdb
清楚全部数据库的内容
flushAll
切换数据库:
Select 0 切换到第一个数据库
是单线程的:
Redis是基于内存操作的,CPU不是Redis性能瓶颈,Redis的性能瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,就使用的单线程,所以就使用了单线程了
为什么单线程还很快:
1、误区一:高性能的服务器一定是多线程的?
2、误区二:多线程(CPU上下文会切换)一定比单线程效率高?
CPU>内存>硬盘的速度要有所了解!
核心:
redis是将所有的数据全部放在内存中的,所以使用单线程去操作效率就是最高的,耗时的操作
对于内存系统来说,如果没有上下文切换效率就是最高的,多次读写都在一个CPU上的,在内存情况下,这个就是最佳的方案!
Redis-Key:
查看所有key:
Keys *
查看是否存在:
EXISTS key
删除:
Move key
设置过期时间:
EXPIRE name(key) 10(过期时间:s)
查看key过期时间:
Ttl name(key)
查看当前key的数据类型:
Type name
五大基本类型:
String:
127.0.0.1:6379> select 8 切换到第8个数据库
OK
127.0.0.1:6379[8]> keys * 查看当前数据库所有key
(empty list or set)
127.0.0.1:6379[8]> set key1 v1 设置值
OK
127.0.0.1:6379[8]> get key1 获取值
"v1"
127.0.0.1:6379[8]> keys *
1) "key1"
127.0.0.1:6379[8]> exists key1 判断一个key是否存在
(integer) 1
127.0.0.1:6379[8]> append key1 cheyuyu 追加字符串 如果当前key不存在,那么就相当于set key
(integer) 9
127.0.0.1:6379[8]> get key1
"v1cheyuyu"
127.0.0.1:6379[8]> strlen key1 获取字符串长度
(integer) 9
127.0.0.1:6379[8]> append key1 zhangsan
(integer) 17
127.0.0.1:6379[8]> get key1
"v1cheyuyuzhangsan"
i++
步长 i+ =
127.0.0.1:6379[8]> set views 0 初始浏览量为0
OK
127.0.0.1:6379[8]> get views
"0"
127.0.0.1:6379[8]> incr views 自增一
(integer) 1
127.0.0.1:6379[8]> incr views
(integer) 2
127.0.0.1:6379[8]> get views
"2"
127.0.0.1:6379[8]> decr views 自减一
(integer) 1
127.0.0.1:6379[8]> decr views
(integer) 0
127.0.0.1:6379[8]> decr views
(integer) -1
127.0.0.1:6379[8]> incrby views 10 可以设置步长, 指定增长量
(integer) 9
127.0.0.1:6379[8]> incrby views 10
(integer) 19
127.0.0.1:6379[8]> decrby views 10
(integer) 9
字符串范围 range
127.0.0.1:6379[8]> get key1
"hello,cheyuyu"
127.0.0.1:6379[8]> getrange key1 0 3 截取字符串 0 -3
"hell"
127.0.0.1:6379[8]> getrange key1 0 -1 获取全部字符串
"hello,cheyuyu"
替换setrange
127.0.0.1:6379[8]> set key2 abcdefg
OK
127.0.0.1:6379[8]> get key2
"abcdefg"
127.0.0.1:6379[8]> setrange key2 1 xx 替换内容
(integer) 7
127.0.0.1:6379[8]> get key2
"axxdefg"
127.0.0.1:6379[8]>
Setex(set with expire) 设置过期时间
Setnex(set if not exist) 不存在在设置 (在分布式锁中会常常使用!)
127.0.0.1:6379[8]> mset k1 v1 k2 v2 k3 v3 k4 v4 同时设置多个值
OK
127.0.0.1:6379[8]> keys *
1) "k3"
2) "k1"
3) "k2"
4) "k4"
127.0.0.1:6379[8]> mget k1 k2 k3 k4同时获取多个值
1) "v1"
2) "v2"
3) "v3"
4) "v4"
127.0.0.1:6379[8]> msetnx k1 v1 k5 v5 msetnx是一个原子性的操作,要么一起成功,要么一起失败
(integer) 0
127.0.0.1:6379[8]> get k5
(nil)
对象:
Set user:1{name:zhangsan,age:3} 设置一个user1对象为json字符串保存一个对象
这里的key是一个巧妙的设计 如此设计是完全OK的
127.0.0.1:6379[8]> mset user:1:name zhangsan user:1:age 2
OK
127.0.0.1:6379[8]> mget user:1:name user:1:age
1) "zhangsan"
2) "2"
127.0.0.1:6379[8]>
Getset 先get 如何SET
127.0.0.1:6379[8]> getset db redis 如果不存在 则返回nil
(nil)
127.0.0.1:6379[8]> get db
"redis"
127.0.0.1:6379[8]> getset db mongodb 如果存在值,获取原来的值,并设置新的值
"redis"
127.0.0.1:6379[8]> get db
"mongodb"
数据结构是相通的
String 类似的使用类型场景:value除了是我们的字符串还可以是我们的数字
计数器、统计多单位的数量 、粉丝数、对象缓存
List:
基本的数据类型,列表
在redis里面,我们可以把list玩成栈,队列,阻塞队列
所有的list都是L 开头的
127.0.0.1:6379[8]> lpush list one 将一个值 或者多个值 插入到列表头部(左)
(integer) 1
127.0.0.1:6379[8]> lpush list two
(integer) 2
127.0.0.1:6379[8]> lpush list three
(integer) 3
127.0.0.1:6379[8]> lrange list 0 -1 lrange 获取list中的值
1) "three"
2) "two"
3) "one"
127.0.0.1:6379[8]> lrange list 0 2
1) "three"
2) "two"
3) "one"
127.0.0.1:6379[8]> lrange list 0 1
1) "three"
2) "two"
127.0.0.1:6379[8]> Rpush list right 将一个值 或者多个值 插入到列表头部(右)
(integer) 4
127.0.0.1:6379[8]> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
127.0.0.1:6379[8]> lpop list 移除list第一个元素
"three"
127.0.0.1:6379[8]> rpop list 移除list最后一个元素
"right"
127.0.0.1:6379[8]> lrange list 0 -1
1) "two"
2) "one"
127.0.0.1:6379[8]>
Lindex
127.0.0.1:6379[8]> lrange list 0 -1
1) "two"
2) "one"
127.0.0.1:6379[8]> lindex list 0 通过下标获取list中的值
"two"
127.0.0.1:6379[8]>
Len
127.0.0.1:6379[8]> lpush list one
(integer) 1
127.0.0.1:6379[8]> lpush list two
(integer) 2
127.0.0.1:6379[8]> lpush list three
(integer) 3
127.0.0.1:6379[8]> llen list 返回列表的长度
(integer) 3
127.0.0.1:6379[8]>
移除指定的值
lrem
127.0.0.1:6379[8]> lrem list 1 one 移除list中指定的Value 精确匹配
(integer) 1
127.0.0.1:6379[8]> lrange list 0 -1
1) "three"
2) "three"
3) "two"
127.0.0.1:6379[8]> lrem list 1 three
(integer) 1
127.0.0.1:6379[8]> lrange list 0 -1
1) "three"
2) "two"
127.0.0.1:6379[8]> lrem list 1 three
(integer) 1
127.0.0.1:6379[8]> lrange list 0 -1
1) "two"
Trim 修剪操作;list
127.0.0.1:6379[8]> rpush mylist "hello"
(integer) 1
127.0.0.1:6379[8]> rpush mylist "hello1"
(integer) 2
127.0.0.1:6379[8]> rpush mylist "hello12"
(integer) 3
127.0.0.1:6379[8]> rpush mylist "hello3"
(integer) 4
127.0.0.1:6379[8]> ltrim mylist 1 2 通过下标截取指定的长度,这个list已经被改变
OK
127.0.0.1:6379[8]> lrange mylist 0 -1
1) "hello1"
2) "hello12"
Rpoplpush 移除列表最后一个元素
127.0.0.1:6379[8]> rpush mylist "hello"
(integer) 1
127.0.0.1:6379[8]> rpush mylist "hello1"
(integer) 2
127.0.0.1:6379[8]> rpush mylist "hello2"
(integer) 3
127.0.0.1:6379[8]> rpoplpush mylist myotherlist 移除列表最后一个元素,将他移动到新的 列表中
"hello2"
127.0.0.1:6379[8]> lrange mylist 0 -1 查看原来的列表
1) "hello"
2) "hello1"
127.0.0.1:6379[8]> lrange myotherlist 0 -1 查看目标列表
1) "hello2"
127.0.0.1:6379[8]>
Lset将列表中下标的值替换为另外一个值 更新操作
127.0.0.1:6379[8]> exists list 这个列表是否存在
(integer) 0
127.0.0.1:6379[8]> lset list 0 item 不存在报错
(error) ERR no such key
127.0.0.1:6379[8]> lpush list value1
(integer) 1
127.0.0.1:6379[8]> lrange list 0 -1
1) "value1"
127.0.0.1:6379[8]> lset list 0 item 将列表中下标的值替换为另外一个值
OK
127.0.0.1:6379[8]> lrange list 0 0
1) "item"
linsert将某个具体的value插入到某个元素的前面或者后面
127.0.0.1:6379[8]> Rpush mylist hello
(integer) 1
127.0.0.1:6379[8]> Rpush mylist hello1
(integer) 2
127.0.0.1:6379[8]> Rpush mylist hello12
(integer) 3
127.0.0.1:6379[8]> Rpush mylist world
(integer) 4
127.0.0.1:6379[8]> linsert mylist before "world" "other"
(integer) 5
127.0.0.1:6379[8]> lrange mylist 0 0
1) "hello"
127.0.0.1:6379[8]> lrange mylist 0 -1
1) "hello"
2) "hello1"
3) "hello12"
4) "other"
5) "world"
127.0.0.1:6379[8]> linsert mylist after world new
(integer) 6
127.0.0.1:6379[8]> lrange mylist 0 -1
1) "hello"
2) "hello1"
3) "hello12"
4) "other"
5) "world"
6) "new"
小结:实际上是一个链表,before,node after ,left ,right都可以插入值
如果key不存在,创建新的链表
如果key存在,新增内容,
如果移除所有的值,空链表,也代表不存在
在两边插入或者改动值,效率最高,中间元素,相对来说效率会低一点
Set
set中的值不能重复
127.0.0.1:6379[8]> sadd myset hello 向set中添加元素
(integer) 1
127.0.0.1:6379[8]> sadd myset cheyuyu
(integer) 1
127.0.0.1:6379[8]> sadd myset mochen
(integer) 1
127.0.0.1:6379[8]> smembers myset 查看指定set的所有元素
1) "hello"
2) "mochen"
3) "cheyuyu"
127.0.0.1:6379[8]> sismember myset hello 判断某一个值是不是在set集合中
(integer) 1
127.0.0.1:6379[8]> sismember myset world
(integer) 0
127.0.0.1:6379[8]> scard myset 获取集合中的个数
(integer) 3
127.0.0.1:6379[8]> srem myset hello 移除某一个元素
(integer) 1
127.0.0.1:6379[8]> scard myset
(integer) 2
127.0.0.1:6379[8]> smembers myset
1) "mochen"
2) "cheyuyu"
set无序不重复集合
127.0.0.1:6379[8]> SRANDMEMBER myset 随机抽出元素
"cheyuyu"
127.0.0.1:6379[8]> SRANDMEMBER myset
"mochen"
127.0.0.1:6379[8]> SRANDMEMBER myset
"mochen"
127.0.0.1:6379[8]> SRANDMEMBER myset
"mochen"
127.0.0.1:6379[8]> SRANDMEMBER myset 2 随机抽出指定个数的元素
1) "cheyuyu"
2) "mochen"
移除指定的key,随机删除
127.0.0.1:6379[8]> SMEMBERS myset
1) "mochen"
2) "cheyuyu"
127.0.0.1:6379[8]> spop myset 随机移除一个元素
"mochen"
127.0.0.1:6379[8]> SMEMBERS myset
1) "cheyuyu"
127.0.0.1:6379[8]>
将一个指定的值,移动到另外一个集合中
127.0.0.1:6379[8]> sadd myset hello
(integer) 1
127.0.0.1:6379[8]> sadd myset world
(integer) 1
127.0.0.1:6379[8]> sadd myset mochen
(integer) 1
127.0.0.1:6379[8]> sadd myset2 set2
(integer) 1
127.0.0.1:6379[8]> smove myset myset2 mochen 将一个指定的值,移动到另外一个集合中
(integer) 1
127.0.0.1:6379[8]> SMEMBERS myset
1) "world"
2) "hello"
127.0.0.1:6379[8]> SMEMBERS myset2
1) "mochen"
2) "set2"
127.0.0.1:6379[8]> sadd key1 a
(integer) 1
127.0.0.1:6379[8]> sadd key1 b
(integer) 1
127.0.0.1:6379[8]> sadd key1 c
(integer) 1
127.0.0.1:6379[8]> sadd key2 c
(integer) 1
127.0.0.1:6379[8]> sadd key2 d
(integer) 1
127.0.0.1:6379[8]> sadd key2 e
(integer) 1
127.0.0.1:6379[8]> SDIFF key1 key2 差集
1) "b"
2) "a"
127.0.0.1:6379[8]> SINTER key1 key2 交集 共同好友就可以这样实现
1) "c"
127.0.0.1:6379[8]> SUNION key1 key2 并集
1) "c"
2) "e"
3) "b"
4) "a"
5) "d"
应用:微博,A用户将所有关注的人放到一个set集合中,将她的粉丝也放到一个集合中
共同好友,共同爱好,二度好友
Hash
map集合,
127.0.0.1:6379[8]> hset myhash field1 mochen set一个具体 key-value
(integer) 1
127.0.0.1:6379[8]> hget myhash fiedl1 获取一个字段值
(nil)
127.0.0.1:6379[8]> hget myhash field1
"mochen"
127.0.0.1:6379[8]> hmset myhash field1 hello field world set多个具体 key-value
OK
127.0.0.1:6379[8]> hmget myhash field1 field 获取多个字段值
1) "hello"
2) "world"
127.0.0.1:6379[8]> hgetall myhash 获取全部的数据
1) "field1"
2) "hello"
3) "field"
4) "world"
127.0.0.1:6379[8]> hdel myhash field1 删除哈希中指定的值 对于的value也就没了
(integer) 1
127.0.0.1:6379[8]> hgetall myhash
1) "field"
2) "world"
获取长度
127.0.0.1:6379[8]> hgetall myhash
1) "field"
2) "world"
3) "field1"
4) "hello"
5) "field3"
6) "world"
127.0.0.1:6379[8]>
127.0.0.1:6379[8]> hlen myhash 获取长度
(integer) 3
127.0.0.1:6379[8]> HEXISTS myhash field5
(integer) 0
127.0.0.1:6379[8]> HEXISTS myhash field1 判断是否存在
(integer) 1
只获取所有的key
127.0.0.1:6379[8]> HKEYS myhash
1) "field"
2) "field1"
3) "field3"
只获取所有的值
127.0.0.1:6379[8]> HVALS myhash
1) "world"
2) "hello"
3) "world"
127.0.0.1:6379[8]> hset myhash field9 5 指定增量
(integer) 1
127.0.0.1:6379[8]> HINCRBY myhash field9 1
(integer) 6
127.0.0.1:6379[8]> HINCRBY myhash field9 -1
(integer) 5
127.0.0.1:6379[8]> hsetnx myhash field4 hello 如果不存在则可以设置
(integer) 1
127.0.0.1:6379[8]> hsetnx myhash field4 world 存在不可以设置
(integer) 0
127.0.0.1:6379[8]>
应用场景:变更操作,尤其是用户信息,经常变动的信息 hash更适合于对象的存储,String更加适合字符串存储
ZSet在Set的基础上增加一个值
有序的
127.0.0.1:6379[8]> zadd myset 1 one 增加一个值
(integer) 1
127.0.0.1:6379[8]> zadd myset 2 two
(integer) 1
127.0.0.1:6379[8]> zadd myset 3 three 增加多个值
(integer) 1
127.0.0.1:6379[8]> zrange myset 0 -1
1) "one"
2) "two"
3) "three"
排序
从小到大
127.0.0.1:6379[8]> zadd salary 2500 xiaohong 添加用户薪水
(integer) 1
127.0.0.1:6379[8]> zadd salary 5000 mochen
(integer) 1
127.0.0.1:6379[8]> zadd salary 500 kuangshen
(integer) 1
127.0.0.1:6379[8]> ZRANGEBYSCORE salary -inf +inf 从小到大排序
1) "kuangshen"
2) "xiaohong"
3) "mochen"
127.0.0.1:6379[8]> ZRANGEBYSCORE salary 0 -1
(empty list or set)
127.0.0.1:6379[8]> ZRANGEBYSCORE salary +inf -inf
(empty list or set)
127.0.0.1:6379[8]> ZRANGEBYSCORE salary -inf +inf withscores 从小到大排序并且附带薪水
1) "kuangshen"
2) "500"
3) "xiaohong"
4) "2500"
5) "mochen"
6) "5000"
127.0.0.1:6379[8]> ZRANGEBYSCORE salary -inf 2500 withscores 显示工资小于2500的升序排序
1) "kuangshen"
2) "500"
3) "xiaohong"
4) "2500"
从大到小
127.0.0.1:6379[8]> ZREVRANGE salary 0 -1
1) "mochen"
2) "kuangshen"
移除元素
127.0.0.1:6379[8]> zrange salary 0 -1
1) "kuangshen"
2) "xiaohong"
3) "mochen"
127.0.0.1:6379[8]> zrem salary xiaohong 移除有序集合中的指定元素
(integer) 1
127.0.0.1:6379[8]> zrange salary 0 -1
1) "kuangshen"
2) "mochen"
获取有序集合中的个数
127.0.0.1:6379[8]> ZCARD salary
(integer) 2
获取指定区间的成员数量
127.0.0.1:6379[8]> zadd myset 1 hello 2 world 3 mochen
(integer) 3
127.0.0.1:6379[8]> zcount myset 1 3
(integer) 3
使用场景:set排序 存储当前成绩表,工资表排序!
普通消息,重要消息2 ,带权重进行判断
排行榜应用实现,Top N
三种特殊数据类型:
Geospatial :地理位置
朋友的定位,附近的人,打车距离计算
Redis的Geo 在Redis的3.2就推出了 可以推算地理位置信息,两地之间的距离
有效经度为 -180 到 180 度。
有效纬度是从 -85.05112878 到 85.05112878 度。
来自 <https://redis.io/commands/geoadd>
127.0.0.1:6379[8]> geoadd china:city 39.90 116.40 beijing
(error) ERR invalid longitude,latitude pair 39.900000,116.400000
可以查询一些测试数据:http://www.jsons.cn/lngcodeinfo/0844F38DEC46A106
Geoadd:
添加位置
规则 两级无法直接添加,我们一般会下载城市数据,直接通过Java程序一次性导入
参数key 值(维度,经度,名称 )
127.0.0.1:6379[8]> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379[8]> geoadd china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379[8]> geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shengzhen
(integer) 2
127.0.0.1:6379[8]> geoadd china:city 120.16 30.24 hangzhou 108.96 34.26 xian
(integer) 2
127.0.0.1:6379[8]>
Geopos:
获得当前定位
127.0.0.1:6379[8]> geopos china:city beijing 获取指定的城市经度维度
1) 1) "116.39999896287918"
2) "39.900000091670925"
127.0.0.1:6379[8]> geopos china:city beijing chongqing
1) 1) "116.39999896287918"
2) "39.900000091670925"
2) 1) "106.49999767541885"
2) "29.529999579006592"
Geodist:
两人之间距离!
给定一个表示地理空间索引的排序集,使用GEOADD命令填充,该命令返回指定单位中两个指定成员之间的距离。
如果缺少一个或两个成员,则该命令返回 NULL。
单位必须是以下之一,默认为米:
m为米。
公里为公里。
英里数英里。
英尺为英尺。
127.0.0.1:6379[8]> geodist china:city beijing shanghai 查看北京到上海的直线距离
"1067378.7564"
127.0.0.1:6379[8]> geodist china:city beijing shanghai km 查看北京到上海的直线距离 单位是千米
"1067.3788"
127.0.0.1:6379[8]> geodist china:city beijing chongqing
"1464070.8051"
Georedius:
我附近的人,获取所有附近的人,定位,地址,通过半径查询
127.0.0.1:6379[8]> GEORADIUS china:city 110 30 1000 km 以110,30这个经纬度为中心,寻找方圆1000km内的城市
1) "chongqing"
2) "xian"
3) "shengzhen"
4) "hangzhou"
127.0.0.1:6379[8]> GEORADIUS china:city 110 30 500 km
1) "chongqing"
2) "xian"
127.0.0.1:6379[8]> GEORADIUS china:city 110 30 500 km withdist 显示到中心距离的位置
1) 1) "chongqing"
2) "341.9374"
2) 1) "xian"
2) "483.8340"
127.0.0.1:6379[8]> GEORADIUS china:city 110 30 500 km withcoord 显示他人的定位信息
1) 1) "chongqing"
2) 1) "106.49999767541885"
2) "29.529999579006592"
2) 1) "xian"
2) 1) "108.96000176668167"
2) "34.2599996441893"
127.0.0.1:6379[8]> GEORADIUS china:city 110 30 500 km withdist withcoord count 1 筛选指定结果
1) 1) "chongqing"
2) "341.9374"
3) 1) "106.49999767541885"
2) "29.529999579006592"
GEORADIUSBYMEMBER:
城市之间定位,找出位于指定元素周围的其他元素
127.0.0.1:6379[8]> GEORADIUSBYMEMBER china:city shanghai 400 km
1) "hangzhou"
2) "shanghai"
GEOHASH:
将二维的经纬度转换为一维的字符串
127.0.0.1:6379[8]> GEOHASH china:city beijing chongqing
1) "wx4fbxxfke0"
2) "wm5xzrybty0"
GEO底层原理其实就是Zset我们可以使用Zset命令来操作GEo
127.0.0.1:6379[8]> zrange china:city 0 -1 查看地图中全部元素
1) "chongqing"
2) "xian"
3) "shengzhen"
4) "hangzhou"
5) "shanghai"
6) "beijing"
127.0.0.1:6379[8]> zrem china:city beijing 移除元素
(integer) 1
127.0.0.1:6379[8]> zrange china:city 0 -1
1) "chongqing"
2) "xian"
3) "shengzhen"
4) "hangzhou"
5) "shanghai"
Hyperloglog:
基数?不重复的元素,可以接受误差
简介:
Redis2.8.9就更新了。
基数统计的算法
优点:占用的内存是固定的,
127.0.0.1:6379[8]> PFADD mykey a b c d e f g h i j 创建第一组元素 mykey
(integer) 1
127.0.0.1:6379[8]> PFCOUNT mykey 统计元素基数数量
(integer) 10
127.0.0.1:6379[8]> PFADD mykey2 i j z x c b n m
(integer) 1
127.0.0.1:6379[8]> PFCOUNT mykey2
(integer) 8
127.0.0.1:6379[8]> PFMERGE mykey3 mykey mykey2 合并两组 到mykey3 并集
OK
127.0.0.1:6379[8]> PFCOUNT mykey3
(integer) 14
Bitmaps:
统计疫情感染人数,统计用户信息,打卡
Bitmaps数据结构,位图,都是操作二进制位来进行记录,
127.0.0.1:6379[8]> setbit sign 0 1
(integer) 0
127.0.0.1:6379[8]> setbit sign 1 0
(integer) 0
127.0.0.1:6379[8]> setbit sign 2 0
(integer) 0
127.0.0.1:6379[8]> setbit sign 3 0
(integer) 0
127.0.0.1:6379[8]> setbit sign 4 0
(integer) 0
127.0.0.1:6379[8]> setbit sign 5 0
(integer) 0
127.0.0.1:6379[8]> setbit sign 6 0
(integer) 0
127.0.0.1:6379[8]> GETBIT sign 3
(integer) 0
127.0.0.1:6379[8]> GETBIT sign 0
(integer) 1
127.0.0.1:6379[8]> BITCOUNT sign 统计这周打卡天数
(integer) 1
127.0.0.1:6379[8]>
事务:
Redis事务本质:一组命令集合!一个事务中的所有命令,都会被序列化,在事务执行过程中,会按照顺序执行,一次性,顺序性,排他性!执行一系列命令
Redis单挑命令是保证原子性的,但是事务是不保证原子性的 ,没有隔离级别的概念
所有的命令在事务中,并没有直接被执行,只有发起执行命令的时候才会被执行
开启事务(MULTI)
命令入队(….)
执行事务(exec)
锁:乐观锁
正常执行事务:
127.0.0.1:6379[8]> multi 开启事务
OK
127.0.0.1:6379[8]> set k1 v1 命令入队
QUEUED
127.0.0.1:6379[8]> set k2 v2
QUEUED
127.0.0.1:6379[8]> get k2
QUEUED
127.0.0.1:6379[8]> set k3 v3
QUEUED
127.0.0.1:6379[8]> exec 执行事务
1) OK
2) OK
3) "v2"
4) OK
127.0.0.1:6379[8]>
放弃事务:放弃后不会执行
127.0.0.1:6379[8]> MULTI
OK
127.0.0.1:6379[8]> set k5 v5
QUEUED
127.0.0.1:6379[8]> set k6 v6
QUEUED
127.0.0.1:6379[8]> DISCARD 放弃事务
OK
127.0.0.1:6379[8]> get k5
(nil)
变异性异常(命令有错),事物中,所有的命令都不会被执行
127.0.0.1:6379[8]> MULTI
OK
127.0.0.1:6379[8]> set k1 v1
QUEUED
127.0.0.1:6379[8]> set k2 v2
QUEUED
127.0.0.1:6379[8]> set k3 v3
QUEUED
127.0.0.1:6379[8]> getset k3 错误的命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379[8]> set k4 v4
QUEUED
127.0.0.1:6379[8]> set k5 v5
QUEUED
127.0.0.1:6379[8]> exec 执行事务报错
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379[8]> get k5 所有的命令都不会执行
(nil)
运行时异常,如果事务队列中,存在语法性错误,那么执行命令的时候,其他命令时可以正常执行的,错误命令会抛出异常
127.0.0.1:6379[8]> set k1 v1
OK
127.0.0.1:6379[8]> MULTI -
(error) ERR unknown command 'mult'
127.0.0.1:6379[8]> multi
OK
127.0.0.1:6379[8]> incr k1 执行时会失败
QUEUED
127.0.0.1:6379[8]> set k2 v2
QUEUED
127.0.0.1:6379[8]> set k3 v3
QUEUED
127.0.0.1:6379[8]> exec
1) (error) ERR value is not an integer or out of range 第一条会报错,
2) OK
3) OK
监控:
悲观锁:
认为什么时候都会出问题,无论做什么都会加锁
乐观锁:
认为什么时候都不会出问题,所以不上锁,更新数据的时候去判断一下,在此期间是否有人修改过数据
获取version
更新的时候比较version
Redis监视测试
127.0.0.1:6379[8]> set money 100
OK
127.0.0.1:6379[8]> set out 0
OK
127.0.0.1:6379[8]> watch money 监视 money对象
OK
127.0.0.1:6379[8]> MULTI 监视正常结束,数据期间没有发生变动,这个时候就正常执行成功
OK
127.0.0.1:6379[8]> DECRBY money 20
QUEUED
127.0.0.1:6379[8]> INCRBY out 20
QUEUED
127.0.0.1:6379[8]> exec
1) (integer) 80
2) (integer) 20
正常执行成功
测试多线程修改值,监视失败使用watch可以当作redis的乐观锁操作!
127.0.0.1:6379[8]> watch money 监视money
OK
127.0.0.1:6379[8]> multi
OK
127.0.0.1:6379[8]> DECRBY money 10
QUEUED
127.0.0.1:6379[8]> INCRBY out 10
QUEUED
127.0.0.1:6379[8]> exec 执行前另外一个线程 修改值,这个时候,就会导致事务执行失败
(nil)
127.0.0.1:6379[8]> UNWATCH 如果发现事务执行失败,先解锁
OK
127.0.0.1:6379[8]> watch money 获取最新的值 再次监视 ,select version
OK
127.0.0.1:6379[8]> multi
OK
127.0.0.1:6379[8]> decrby money 10
QUEUED
127.0.0.1:6379[8]> incrby out 10
QUEUED
127.0.0.1:6379[8]> exec 比对监视的值,是否发生了变化
1) (integer) 990
2) (integer) 30
Jedis
我们使用Java来操作Redis
导入依赖
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
</dependencies>
连接数据库
publicstaticvoidmain(String[]args){
//1、newJedis对象即可
Jedisjedis=newJedis("127.0.0.1",6379);
//Jedis所有的命令
System.out.println(jedis.ping());
}
常用API就是上面的命令
Redis持久化:
持久化就是,因为Redis是内存数据库,如果不将内存中的数据库状态保存到磁盘中的话,那么一旦服务器进程退出,服务器中的数据库状态也就会消失,所以Redis提供了持久化功能!
RDB:
在指定的时间间隔内将内存中的数据集快照写入磁盘中,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里,
Redis会单独创建(fork)一个子进程进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,在用这个临时文件替换上次持久化好的文件,整个过程中,主进程是不进行IO操作的,这就确保了极高的性能,如果需要大规模数据的恢复,且对于数据恢复的完整性不是非常铭感,那RDB方式要比AOF方式更加的高校,RDB的缺点是最后一次持久化的数据可能丢失(在最后一次宕机的时候,容易造成数据丢失),我们默认的就是RDB,
RDB保存的文件是dump.rdb,都是在我们的配置文件中快照进行配置的
触发规则:
1、save的规则满足的条件下,会自动触发RDB规则
2、执行flushAll命令,也会触发我们的RDB 规则
3、退出REDIS,也会产生RDB文件
备份就会自动生成一个dump.rdb
如何恢复RDB文件
1、只需要将RDB文件放在我们的Redist启动目录就可以了,redis启动的时候就会自动检查dump.rdd,恢复启动其中的数据!
2、查看需要存放的位置
127.0.0.1:6379> config get dir
1) "dir"
2) "D:\\Redis\\Redis-x64-3.2.100" 如果在这个目录下存在dump.rdb文件,启动就会自动恢复其中的数据
优点:
1、适合大规模的数据恢复!
2、对数据的完整性要求不高!
缺点:
1、需要一定的时间间隔进程操作,如果redis意外宕机了, 这个最后一次修改数据就没有了
2、fork进程的时候,会占用一定的内容空间 !!
AOF:
将我们的所以命令记录下来,恢复的时候,就把这个文件执行一遍
默认是不开启的,我们需要手动进行配置,我们只需要开启appendonly改为yes即可开启了AOF
重启reids即可生效
如果这个AOF文件有错位,这时候Redis是启动不起来的,我们需要修复这个AOF文件
redis给我们提供了一个工具,redis-check-aof --fix
如果文件正常,重启就可以直接恢复了
优点:
1、每一次修改,都同步文件的完整性就更加好
2、每秒同步一次,可能会丢失一秒的数据
3、从不同步,效率最高
缺点:
1、相对于数据文件来说,aof远远大于RDB,修复的速度也比RDB慢,
2、AOF运行效率也要比RDB慢,所以我们Redis默认的配置就是RDB持久化,而不是AOF,
Redis订阅:
订阅端:
127.0.0.1:6379[8]> SUBSCRIBE mochenshuo 订阅 一个频道
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "mochenshuo"
3) (integer) 1
1) "message" 消息
2) "mochenshuo" 来自哪个频道
3) "hello,world" 具体内容
1) "message"
2) "mochenshuo"
3) "hello,mochen"
发送端:
127.0.0.1:6379> PUBLISH mochenshuo "hello,world" 发布消息到频道
(integer) 1
127.0.0.1:6379> PUBLISH mochenshuo "hello,mochen"
(integer) 1
127.0.0.1:6379>
使用场景:
实时消息系统
主从复制:
作用:
1、数据冗余;2、故障恢复;3、负载均衡;4、高可用基石
环境配置:
只配置从库,不用配置主库
查看当前库信息
127.0.0.1:6379> info replication
# Replication
role:master 角色
connected_slaves:0 没有从机
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
复制3个配置文件,修改对应的信息
1、端口
2、pid名称
3、log文件的名字
4、dump.rdb名字
默认三台都是主机
认老大,一个主机,一主二从
在从机中配置:
127.0.0.1:6379> SLAVEOF 127.0.0.1 6379 SLAVEOF host 6379 找谁当自己的老大
细节了解:
主机可以写,从机只能读,主机中的所有数据都会被从机保存
测试:
主机断开连接,从机依旧连接到主机,但是没有写操作,这个时候,主机如果回来了,从机,依旧可以直接获取主机写的信息!
如果是使用命令行配置的主从,如果从库重启,那么就会变回主库,主要变回从机,就会立马拿到主机数据
哨兵模式(自动选取主库):
配置哨兵:Sentinel.conf
Sentinel monitor myredis 127.0.0.1 6379 1
数字一代表主机宕机了,slave投票让谁接替为主机
2、启动
优点:
哨兵集群,基于主从复制模式,所有的主从配置的优点,全有
主从可以切换,故障可以转移,系统的可用性会更好
哨兵模式,就是主从模式的升级
缺点:
Redis不好在线扩容,集群的容量一旦上限,在线扩容十分麻烦
缓存穿透和雪崩:
穿透:
解决方案:
布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统 的查询压力
缓存空对象,当存储层不命中后,即使返回的空对象也将其缓存起来,同时设置一个过期时间,之后在访问这个数据将会从缓存中获取,保护了后端数据源;
缓存击穿(量太大,缓存过期):
解决方案:
设置热点数据永不过期,从缓存层来看,没有设置过期时间,所以不会出现热点key过期后产生的问题
加互斥锁:分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可,这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大
雪崩:
解决方案:
1.保证缓存服务的高可用性,比如使用Redis哨兵监控Redis集群以及使用Redis Cluster等。即使单个节点宕机了,备用节点还能顶上去保证服务可用。
做二级缓存,当一级缓存停止服务,或大量失效时,由二级缓存顶住访问压力。
不同的key之间的失效时间设置不同保证数据失效均衡。
评论列表