Redis简介:
Redis是一个开源的内存中的数据结构存储系统,它可以用作:数据库、缓存和消息中间件。
数据结构类型有:String、List、Set、Hash、ZSet这5种
Redis默认有16个数据库
默认使用的是第0个
可以使用select 【下标】来切换数据库
127.0.0.1:6379> keys * #查看数据库所有的key
(empty array)
127.0.0.1:6379> flushdb #清除当前库
OK
127.0.0.1:6379> flushall #清除所有库
OK
Redis是单线程的
明白Redis是很快的,官方表示,redis是基于内存操作,CPU不是redis性能瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了
Redis为什么这么快?
1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);
2、数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗
4、使用多路I/O复用模型,非阻塞IO;这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。
多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,
轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作
5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
那么为什么Redis是单线程的?
因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了(毕竟采用多线程会有很多麻烦!)。
127.0.0.1:6379> EXPIRE name 10 #设置key的过期时间,单位是秒 实际业务可用于单点登录,例如存储一定时间的验证码
(integer) 1
127.0.0.1:6379> ttl name # 查看key的剩余时间
(integer) 5
redis-key
String
127.0.0.1:6379> set key1 v1 #设置值
OK
127.0.0.1:6379> get key1 #获得值
"v1"
127.0.0.1:6379> keys * #获得所有key
1) "key1"
127.0.0.1:6379> EXISTS key1 #判断某一个key是否存在
(integer) 1
127.0.0.1:6379> APPEND key1 hello #追加字符串,如果某一个key不存在,就相当于set key
(integer) 7
127.0.0.1:6379> EXISTS key1
(integer) 1
127.0.0.1:6379> get key1
"v1hello"
127.0.0.1:6379> STRLEN key1 #获取字符串的长度
(integer) 7
127.0.0.1:6379> append key1 "hello1" #追加字符串,如果某一个key不存在,就相当于set key
(integer) 13
## i++操作
#步长i+=
127.0.0.1:6379> set views 0 #初始浏览量为0
OK
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> incr views #自增1 浏览量为+1
(integer) 1
127.0.0.1:6379> incr views
(integer) 2
127.0.0.1:6379> get views
"2"
127.0.0.1:6379> decr views #自减1 浏览量为-1
(integer) 1
127.0.0.1:6379> decr views
(integer) 0
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> INCRBY views 10 #可以设置步长,指定增量
(integer) 10
127.0.0.1:6379> INCRBY views 10
(integer) 20
127.0.0.1:6379> DECRBY views 5 #可以设置步长,指定减量
(integer) 15
127.0.0.1:6379> DECRBY views 5
(integer) 10
#截取
127.0.0.1:6379> set key1 "hello,dijia"
OK
127.0.0.1:6379> get key1
"hello,dijia"
127.0.0.1:6379> GETRANGE key1 0 3 #截取下标0-3的字符串
"hell"
127.0.0.1:6379> GETRANGE key1 0 -1 #查看所有的字符串
"hello,dijia"
#替换
127.0.0.1:6379> set key1 hello123
OK
127.0.0.1:6379> SETRANGE key1 1 xx 替换下标为1的字符为xx
(integer) 8
127.0.0.1:6379> get key1
"hxxlo123"
#setex (set with expire) #设置过期时间
#setnx (set if not exist) #不存在设置
127.0.0.1:6379> setex key3 30 "hello" #设置key3的值为hello,30秒后过期
OK
127.0.0.1:6379> ttl key3
(integer) 23
127.0.0.1:6379> setnx mykey "redis" #如果mykey不存在,创建mykey
(integer) 1
127.0.0.1:6379> keys *
1) "mykey"
2) "key1"
127.0.0.1:6379> ttl key3
(integer) -2
127.0.0.1:6379> setnx mykey "monggoDb" #如果mykey存在,创建失败
(integer) 0
127.0.0.1:6379> get mykey
"redis"
#批量设置值
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 #
OK
127.0.0.1:6379> keys *
1) "k3"
2) "k1"
3) "k2"
127.0.0.1:6379> mget k1 k2 k3
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 v1 k4 v4 #msetnx 是一个原子性的操作,要么一起成功要么一起失败
(integer) 0
127.0.0.1:6379> get k4
(nil)
#对象
set user:1{name:zhangsan,age:12} #设置一个user1对象 值为json字符来保存一个对象
#这里的key是一个巧妙的设计: user:{id}:{fileid} ,如此设计在redis中完全OK
127.0.0.1:6379> mset user:1:name zhangsan user:1:age 2
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "zhangsan"
2) "2"
##########################################
getset #先get然后再set
127.0.0.1:6379> getset db redis #如果不存在值则返回nil
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongdb #如果存在值则获取原来的值并设置新的值
"redis"
127.0.0.1:6379> get db
"mongdb"
List
list基本的数据类型,列表
在redis里,我们可以把list玩成,栈,队列,阻塞队列
所有的list命令都是l开头的
##########################################
127.0.0.1:6379> lpush list one #将一个值或者多个值插入到列表的头部(左)
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> LRANGE list 0 -1 #获取list中值
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> LRANGE list 0 1 #通过区间获取具体的值
1) "three"
2) "two"
127.0.0.1:6379> rpush list right #将一个值或者多个值插入到列表的尾部(右)
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
##########################################
lpop
rpop
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
127.0.0.1:6379> lpop list #移除列表的第一个元素
"three"
127.0.0.1:6379> rpop list #移除列表的最后一个元素
"right"
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
#####################################################
lindex
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> lindex list 1 #通过下标获取list中的某一个值
"one"
127.0.0.1:6379> lindex list 0
"two"
#######################################################
llen
127.0.0.1:6379> lpush list one
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> llen list #返回列表的长度
(integer) 3
#######################################################
lrem
127.0.0.1:6379> lrem list 1 one #移除list集合中指定个数的value,精确匹配
(integer) 1
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> lrem list 2 three
(integer) 2
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
#################################################
ktrim
127.0.0.1:6379> Rpush list "hello"
(integer) 1
127.0.0.1:6379> rpush list "hello1"
(integer) 2
127.0.0.1:6379> rpush list "hello2"
(integer) 3
127.0.0.1:6379> rpush list "hello3"
(integer) 4
127.0.0.1:6379> ltrim list 1 2 #通过下标截取指定长度
OK
127.0.0.1:6379> Lrange list 0 -1
1) "hello1"
2) "hello2"
####################
#rpoplpush #移除列表的最后一个元素,并移到新的列表中
127.0.0.1:6379> Lrange list 0 -1
1) "hello1"
2) "hello2"
127.0.0.1:6379> rpoplpush list mylist #移除列表的最后一个元素,并移到新的列表中
"hello2"
127.0.0.1:6379> lrange mylist 0 -1 #查看新列表确实存在该值
1) "hello2"
########################################
lset 将列表中指定下标的值替换为另一个,更新操作
127.0.0.1:6379> exists list #判断这个列表是否存在,不存在则报错
(integer) 1
127.0.0.1:6379> lset list 0 item #存在更新该下标的值
OK
127.0.0.1:6379> lrange list 0 -1 #查询更新成功
1) "item"
##############################################
linsert 将某个具体的value插入到某个元素的前面或者后面
127.0.0.1:6379> rpush list "hello"
(integer) 1
127.0.0.1:6379> rpush list "word"
(integer) 2
127.0.0.1:6379> linsert list before "word" "other"
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "hello"
2) "other"
3) "word"
127.0.0.1:6379> linsert list after "word" "new"
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "hello"
2) "other"
3) "word"
4) "new"
Set集合类型
set集合中的值是不能重复的
127.0.0.1:6379> sadd myset "hello" #set集合合中添加值
(integer) 1
127.0.0.1:6379> sadd myset "kuangshen"
(integer) 1
127.0.0.1:6379> sadd myset "love"
(integer) 1
127.0.0.1:6379> SMEMBERS myset #查看set集合中的值
1) "hello"
2) "love"
3) "kuangshen"
127.0.0.1:6379> SISMEMBER myset hello #判断某一个值是不是在set集合中
(integer) 1
127.0.0.1:6379> scard myset #获取set集合中的内容元素个数
(integer) 3
######################
srem 移除
127.0.0.1:6379> srem myset kuangshen #移除set集合中的指定元素
(integer) 1
127.0.0.1:6379> SMEMBERS myset #查看set集合中的值
1) "hello"
2) "love"
#################
set 无序不重复集合
127.0.0.1:6379> SRANDMEMBER myset #随机抽选出一个元素
"hello"
127.0.0.1:6379> SRANDMEMBER myset
"hello"
127.0.0.1:6379> SRANDMEMBER myset
"hello"
127.0.0.1:6379> SRANDMEMBER myset
"love"
127.0.0.1:6379> SRANDMEMBER myset 2 #随机抽选出指定个数的元素
1) "hello"
2) "love"
#################################
spop 随机删除set集合中的一些元素
127.0.0.1:6379> SMEMBERS myset
1) "456"
2) "hello"
3) "love"
4) "123"
127.0.0.1:6379> spop myset
"hello"
127.0.0.1:6379> spop myset
"456"
127.0.0.1:6379> SMEMBERS myset
1) "love"
2) "123"
127.0.0.1:6379> sadd myset2 "word"
(integer) 1
127.0.0.1:6379> smove myset myset2 123 #把指定set的值一定到另一个set集合
(integer) 1
127.0.0.1:6379> SMEMBERS myset2
1) "word"
2) "123"
Hash
Map集合,key-map 时候是一个map集合!本质和string类型没有太大区别,还是一个简单的key-value
127.0.0.1:6379> hset myhash filed1 123 #set一个具体 key-value
(integer) 1
127.0.0.1:6379> hget myhash filed1 #获取一个字段值
"123"
127.0.0.1:6379> hmset myhash filed1 hell0 filed2 world #set多个 key-value
OK
127.0.0.1:6379> hmget myhash filed1 filed2 #获取多个字段值
1) "hell0"
2) "world"
127.0.0.1:6379> hgetall myhash #获取全部的数据
1) "filed1"
2) "hell0"
3) "filed2"
4) "world"
127.0.0.1:6379> hdel myhash filed1 #删除指定hash 的key字段,对应的value值也就没有了
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "filed2"
2) "world"
127.0.0.1:6379> hlen myhash #查看hash的字段长度
(integer) 1
127.0.0.1:6379> HEXISTS myhash filed1 #判断hash中指定字段是否存在
(integer) 0
127.0.0.1:6379> HEXISTS myhash filed2
(integer) 1
127.0.0.1:6379> hkeys myhash #只获取所有的hash key
1) "filed2"
127.0.0.1:6379> hvals myhash #只获取所有的hash value
1) "world"
############################################
127.0.0.1:6379> hset myhash filed3 5
(integer) 1
127.0.0.1:6379> HINCRBY myhash filed3 1 #指定增量
(integer) 6
127.0.0.1:6379> HINCRBY myhash filed3 -1 #指定减量
(integer) 5
127.0.0.1:6379> Hsetnx myhash filed4 hello #如果不存在则可以设置
(integer) 1
127.0.0.1:6379> hsetnx myhash filed4 word #如果存在则不可以设置
(integer) 0
Zset(有序集合)
127.0.0.1:6379> zadd myset 1 one #添加一个值
(integer) 1
127.0.0.1:6379> zadd myset 2 two
(integer) 1
127.0.0.1:6379> zadd myset 3 three
(integer) 1
127.0.0.1:6379> zrange myset 0 -1 #查看所有值
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> zadd myset 4 fore 5 five #添加多个值
(integer) 2
####################################################
排序如何实现
#添加三个用户
127.0.0.1:6379> zadd salary 500 a
(integer) 1
127.0.0.1:6379> zadd salary 800 b
(integer) 1
127.0.0.1:6379> zadd salary 1500 c
(integer) 1
#ZRANGEBYSCORE key min max 语法
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf #显示全部的用户从小到大
1) "a"
2) "b"
3) "c"
127.0.0.1:6379> ZREVRANGE salary 0 -1 #显示全部的用户从大到小
1) "c"
2) "b"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores #显示全部的用户从小到大并且附带成绩
1) "a"
2) "500"
3) "b"
4) "800"
5) "c"
6) "1500"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf 1000 withscores #显示工资小于1000的用户降序排序
1) "a"
2) "500"
3) "b"
4) "800"
#######################################################
127.0.0.1:6379> zrange salary 0 -1 #查看所有元素
1) "a"
2) "b"
3) "c"
127.0.0.1:6379> zrem salary a 移除有序集合中的指定元素
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "b"
2) "c"
#获取有序集合中的个数
127.0.0.1:6379> zcard salary
(integer) 2
########################################
127.0.0.1:6379> zadd myset 1 hello
(integer) 1
127.0.0.1:6379> zadd myset 2 world 3 baby
(integer) 2
127.0.0.1:6379> zcount myset 1 3 #获取指定区间的成员数量
(integer) 3
127.0.0.1:6379> zcount myset 1 2
(integer) 2
三种特殊数据类型
Geospatial地理位置
朋友的定位,附近的人,打车位置距离计算
可以查询一些地理位置数剧http://www.jsons.cn/lngcode/02
getadd #添加地理位置
官方文档:https://www.redis.net.cn/order/3685.html
将指定的地理空间位置(纬度、经度、名称)添加到指定的key中。这些数据将会存储到sorted set这样的目的是为了方便使用GEORADIUS或者GEORADIUSBYMEMBER命令对数据进行半径查询等操作。
#有效的经度从-180度到180度。
有效的纬度从-85.05112878度到85.05112878度。
当坐标位置超出上述指定范围时,该命令将会返回一个错误
127.0.0.1:6379> geoadd china:city 39.90 116.40 beijin 31.23 121.47 shanghai
(error) ERR invalid longitude,latitude pair 39.900000,116.400000
#getadd添加地理位置
#规则 两级无法直接添加,我们一般会下载城市数据,通过java程序直接导入
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
(integer) 1
127.0.0.1:6379> geoadd china:city 114.05 22.52 shenzhen
(integer) 1
127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou 108.96 34.26 xian
(integer) 2
####################################
geopos 查看城市经纬度纬度信息
获得当前定位,一定是一个坐标值
127.0.0.1:6379> geopos china:city beijing
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
127.0.0.1:6379> geopos china:city chongqing
1) 1) "106.49999767541885376"
2) "29.52999957900659211"
Redis GEODIST 命令 - 返回两个给定位置之间的距离
如果两个位置之间的其中一个不存在, 那么命令返回空值。
指定单位的参数 unit 必须是以下单位的其中一个:
m 表示单位为米。
km 表示单位为千米。
mi 表示单位为英里。
ft 表示单位为英尺。
如果用户没有显式地指定单位参数, 那么 GEODIST 默认使用米作为单位。
127.0.0.1:6379> geodist china:city shanghai chongqing #返回两个给定位置之间的距离
"1447673.6920"
Redis GEORADIUS 命令 - 以给定的经纬度为中心, 找出某一半径内的元素
我附近的人(获得所有附近的人的地址定位) ,通过半径来查询
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km #以110 30经纬度为中心查找1000km内的城市
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
127.0.0.1:6379> 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> GEORADIUS china:city 110 30 500 km withcoord #显示他人的定位信息100km内的城市并显示到
1) 1) "chongqing"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
2) 1) "xian"
2) 1) "108.96000176668167114"
2) "34.25999964418929977"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withcoord count 1 #筛选指定的结果
1) 1) "chongqing"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withcoord count 2
1) 1) "chongqing"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
2) 1) "xian"
2) 1) "108.96000176668167114"
2) "34.25999964418929977"
Redis GEORADIUSBYMEMBER 命令 - 找出位于指定范围内的元素,中心点是由给定的位置元素决定
找出位于指定元素周围的其他元素
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km
1) "beijing"
2) "xian"
geohash 返回一个或多个位置的geohash表示
#将二维的经纬度转化为一维的字符串,如果两个字符串越接近,则表示距离越近
127.0.0.1:6379> geohash china:city beijing shanghai
1) "wx4fbxxfke0"
2) "wtw3sj5zbj0"
Geo底层的实现原理其实就是Zset,我们可以使用Zset命令来操作geo
127.0.0.1:6379> zrange china:city 0 -1 #查看地图中全部的元素
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
5) "shanghai"
6) "beijing"
127.0.0.1:6379> zrem china:city beijing #移除指定的元素
(integer) 1
127.0.0.1:6379> zrange china:city 0 -1
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
5) "shanghai"
hyperloglog
**测试使用**
127.0.0.1:6379> Pfadd mykey 1 2 3 4 5 6 7 8 9 10 **创建第一组元素mykey**
(integer) 1
127.0.0.1:6379> pfcount mykey **统计mykey元素的基数数量**
(integer) 10
127.0.0.1:6379> pfadd mykey2 a s d f g h j k l m n b v c x **创建第二组元素mykey2**
(integer) 1
127.0.0.1:6379> pfcount mykey2
(integer) 15
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2 #合并两组mykey mykey2=》mykey3 并集
OK
127.0.0.1:6379> pfcount mykey3 #查看并集的数量
(integer) 25
如果允许容错那么一定可以使用hyperloglog
Bitmap
测试
查看总共有几天打卡
127.0.0.1:6379> bitcount sign
(integer) 3
事务
Redis事物本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程中,会按顺序执行。一次性,顺序性,排他性
redis事务没有隔离级别的概念
redis单条命令是保存原子性的,但是事务不保证原子性
所有的命令在事务中,并没有直接被执行,只有在发起执行命令的时候才被执行!exec
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 k2
QUEUED
127.0.0.1:6379> exec 执行事务
1) OK
2) OK
3) "v2"
放弃事务 discard
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 k3 v3
QUEUED
127.0.0.1:6379> discard 取消事务
OK
127.0.0.1:6379> get k3 事务队列中的命令都没有被执行
(nil)
Redis订阅发布
127.0.0.1:6379> SUBSCRIBE kuangshenshuo #订阅狂神说显示订阅成功
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "nan"
127.0.0.1:6379> PUBLISH kuangshenshuo "hello world" #狂神说发布消息发布成功
(integer) 1
127.0.0.1:6379> publish kuangshenshuo "hello,baby"
(integer) 1
127.0.0.1:6379> SUBSCRIBE kuangshenshuo
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "kuangshenshuo"
3) (integer) 1
1) "message" #订阅端接收到消息
2) "kuangshenshuo"
3) "hello world"
1) "message"
2) "kuangshenshuo"
3) "hello,baby"