Redis基础与常用命令汇总

Redis Note

一、NoSQL

NoSQL = Not only SQL

NoSQL的四大分类

1、键值对:Redis、Tair…

2、列存储数据库:大数据中,HBase,分布式文件系统

3、文档型数据库:MongoDB…

4、图形数据库:Neo4J、InfoGrid…

二、Redis 概述

Remote Dicitionary Server —— 远程字典服务

C语言编写,可以基于内存也可以持久化的日志型,Key-Value形式的数据库。

Redis的默认端口6379

Redis能干嘛

  1. 内存存储、持久化
  2. 效率高,可以用于高速缓存
  3. 可以用来发布订阅系统
  4. 可以用来作为计时器、计数器(如记录浏览量)
  5. 可以进行地图信息分析

Redis的特性

  1. 多种数据类型
  2. 持久化
  3. 集群
  4. 支持事务

三、Linux安装Redis

# 将redis的tar包放在opt目录下,用tar -zxvf解压到当前目录下
[root@localhost opt]# tar -zxvf redis-5.0.9.tar.gz 
# 安装必要的环境
[root@localhost opt]# yum install gcc-c++
# 进入redis解压后的目录进行make操作
[root@localhost opt]# cd redis-5.0.9
[root@localhost redis-5.0.9]# make

make完成后:

使用make install 检验是否安装成功

Redis的默认安装路径是 usr/local/bin

# 将conf文件拷贝到安装目录中,命名为myredis.conf
[root@localhost bin]# mkdir myconfig
[root@localhost bin]# cp /opt/redis-5.0.9/redis.conf myconfig/myredis.conf

设置redis可以后台启动,将daemonize 从no改为yes

基础命令

# 通过自定义的配置文件启动redis服务
[root@localhost bin]# redis-server myconfig/myredis.conf

使用自带的客户端连接redis服务器

[root@localhost bin]# redis-cli  
# 输入ping 返回PONG表示已经连通
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> 

查看当前运行的redis进程的命令:

ps -ef | grep redis

关闭redis服务的命令:(通过客户端连接上redis后输入shutdown命令)

常用功能

压力测试

可以使用redis安装后自带的redis-benchmark来进行性能测试:

# -h 指定连接的地址, -p指定连接的端口号, -c指定并发连接的数量 ,-n指定请求数量
[root@localhost bin]# redis-benchmark -h localhost -p 6379 -c 100 -n 100000

分析压力测试的结果

四、Redis基础知识

  1. redis默认有16个数据库,且默认使用的是索引为0的数据库。

    可以使用 select [index] 来切换数据库

  2. 清除Redis数据库的命令:

    1. 清除当前的数据库flushdb
    2. 清除全部数据库的内容FLUSHALL
  3. Redis是单线程

为什么Redis的速度很快,但是却是单线程的?

官方表示:Redis基于内存操作,那么CPU不是Redis的性能瓶颈,那么就可以使用单线程来实现。

多线程并不一定代表了高性能,因为Redis的数据存放在内存中,本来就是十分快的,而如果采用了多线程来进行操作,反而为因为CPU的上下文切换产生多余的性能消耗,反而导致效率没有单线程那么高了,因此在Redis中单线程操作是目前最佳的方案。

五、常用命令


127.0.0.1:6379> keys *			# 获取当前数据库所有的key
1) "counter:__rand_int__"
2) "key:__rand_int__"
3) "mylist"
4) "myset:__rand_int__"
5) "me"
127.0.0.1:6379> set name fall	# 设置 key value
OK
127.0.0.1:6379> set age 1
OK
127.0.0.1:6379> exists name		# 判断key是否存在
(integer) 1
127.0.0.1:6379> move name 2		# 移动当前的key到index(此处是2)的数据库
(integer) 1
127.0.0.1:6379> expire age 10	# 设置当前key的生存周期
(integer) 1
127.0.0.1:6379> ttl age			# 查看当前key的剩余时间
(integer) 6
127.0.0.1:6379> type name		# 判断当前key的类型
string

六、五大数据类型

String(字符串)

命令汇总
命令具体写法作用
setset [key] [value]设置值
getget [key]得到值
keys *keys *得到所有的key
existsexists [key]判断是否存在key
appendappend [key] [value]在对应的key的数据后追加value
strlenstrlen [key]获得对应key的数据长度
incrincr [key]自增+1
decrdecr [key]自减-1
incrbyincrby [key] [n]带步长自增+n
decrbydecrby [key] [n]带步长自减-n
getrangegetrange [key] [start] [end]截取字符串[start,end]返回
setrangesetrange [key] [start] [value]设置字符串的start下标后追加value
setexsetex [key] [time] [value]set with expire,设置带过期时间的值
setnxsetnx [key] [value]set if not exist,如果key不存在则设置value
msetmset [key1] [value1] [key2] [value2] … …多个k-v对赋值
mgetmget [key1] [key2] … …同时通过多个key得到多个value
msetnxmsetnx [key1] [value1] [key2] [value2] … …如果不存在则进行多个k-v对赋值(有原子性)
要求全部不存在才会成功
getsetgetset [key] [target]返回原先的值,修改为新的值(target)
用例
127.0.0.1:6379> set key1 value1			# 设置值
OK
127.0.0.1:6379> get key1				# 得到值
"value1"
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
(integer) 11
127.0.0.1:6379> get key1
"value1hello"
127.0.0.1:6379> strlen key1				# 获得key对应的value的长度
(integer) 11

127.0.0.1:6379> set views 0
OK
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> incr views				# 进行自增+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操作
(integer) 1
127.0.0.1:6379> get views
"1"
127.0.0.1:6379> incrby views 10			# 设置步长为10的自增操作(+10)
(integer) 11
127.0.0.1:6379> get views
"11"
127.0.0.1:6379> decrby views 5			# 设置步长为5的自减操作
(integer) 6

127.0.0.1:6379> set key1 "hello,world"
OK
127.0.0.1:6379> get key1
"hello,world"
127.0.0.1:6379> getrange key1 0 3		# 截取字符串操作[0,3],左右都是闭区间
"hell"
127.0.0.1:6379> getrange key1 0 -1		# 范围[0,-1]相当于get key
"hello,world"

127.0.0.1:6379> set key2 abcdefg	
OK
127.0.0.1:6379> get key2 
"abcdefg"
127.0.0.1:6379> setrange key2 2 xxx		# 替换从指定位置开始的字符串
(integer) 7
127.0.0.1:6379> get key2
"abxxxfg"

# setex = set with expire
# setnx = set if not exist
127.0.0.1:6379> setex key3 30 hello		# 设置带过期时间的key
OK
127.0.0.1:6379> ttl key3				# 查看剩余生存时间
(integer) 27
127.0.0.1:6379> setnx mykey "redis"		# 如果key不存在,则设置
(integer) 1
127.0.0.1:6379> keys *
1) "key3"
2) "views"
3) "key2"
4) "key1"
5) "mykey"
127.0.0.1:6379> setnx mykey "mysql"		# 如果key已经存在了,设置失败
(integer) 0
127.0.0.1:6379> get mykey
"redis"


127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3	# 同时设置多个k-v对
OK
127.0.0.1:6379> keys *
1) "k2"
2) "k1"
3) "k3"
127.0.0.1:6379> mget k1 k2 k3			# 同时获得多个值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 vv k4 v4		# 在其中的key不存在的前提下,同时设置多个k-v对,是原子性的,要么全部成功,要么全部失败
(integer) 0
127.0.0.1:6379> keys *
1) "k2"
2) "k1"
3) "k3"

# 设置对象
127.0.0.1:6379> set user:1 {name:zhangsan,age:10}	# 通过value为json格式来保存一个对象
OK
127.0.0.1:6379> get user:1				# get得到
"{name:zhangsan,age:10}"

# 通过mset设置多个,用" user:id:filed "作为key,具体值作为value
127.0.0.1:6379> mset user:1:name zhangsan user:1:age 10	
OK
127.0.0.1:6379> keys *
1) "user:1:age"
2) "user:1:name"

# 通过mget也可以获得多个值
127.0.0.1:6379> mget user:1:age user:1:name
1) "10"
2) "zhangsan"

# getset => 先get再set
127.0.0.1:6379> getset db redis		# get的值不存在,返回nil
(nil)
127.0.0.1:6379> get db				# 值已经放入对应的key
"redis"
127.0.0.1:6379> getset db mysql		# 修改值
"redis"								# 返回原先的值
127.0.0.1:6379> get db				# get发现已经设置成了后面的值
"mysql"

String类的使用场景:

  • 计数器(value除了字符串也可以作为数字)
  • 统计多单位的数量
  • 计算浏览量等
  • 对象缓存存储

List(列表)

List有时候可以看作队列或者栈来使用。

命令汇总
命令具体写法作用
lpushlpush [key] [value1] [value2] … …从列表头部插入一个或多个值
rpushrpush [key] [value1] [value2] … …从列表尾部插入一个或多个值
lrangelrange [key] [start] [end]查看列表中下标为start~end范围的值
lpoplpop [key]从列表头部弹出一个值(会删除)
rpoprpop [key]从列表尾部弹出一个值(会删除)
lindexlindex [key] [index]查看列表中下标为index的值(不会删除)
llenllen [key]查看列表的长度(值的数量)
lremlrem [key] [n] [value]删除列表中值为[value]的值,删除的数量由[n]指定
ltrimltrim [key] [start] [end]截断列表,只剩下start~end之间的值(会删除)
rpoplpushrpoplpush [sourceKey] [targetKey]从[sourceKey]列表尾部弹出一个值,
插入[targetKey]列表的头部
existsexists [key]判断列表是否存在
lsetlset [key] [index] [value]更改列表中[index]下标的数据为[value]
列表不存在会报错;对应下标没有值也会报错
linsertlinsert [key] before|after [targetValue] [value]在列表的[tagetValue]的前面(before)或
后面(after)插入一个[value]
用例
127.0.0.1:6379> lpush list one 				# 从列表头部插入一个或多个值
(integer) 1
127.0.0.1:6379> lpush list two three		# 从列表头部插入了多个值
(integer) 3
127.0.0.1:6379> lrange list 0 -1			# lrange查看列表中下表范围内的值(闭区间)
1) "three"									# 可以发现插入多个值时,也是先头插two,再头插three
2) "two"
3) "one"
127.0.0.1:6379> rpush list four				# 从列表尾部插入一个或多个值
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "four"

127.0.0.1:6379> lpop list					# 从列表头部弹出一个值(会删除)
"three"
127.0.0.1:6379> rpop list					# 从列表尾部弹出一个值(会删除)
"four"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"

127.0.0.1:6379> lindex list 1				# 查看列表中对应下标的值【lindex key index】(不会删除)
"one"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> llen list					# 查看列表的长度
(integer) 3

127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lpush list two				# 列表已经存在了一个two,但是仍然可以push一个two进去
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> lrem list 1 one				# lrem删除列表中指定数量的某个值
(integer) 1
127.0.0.1:6379> lrem list 2 two				# 删除list中的two,删除数量为2
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "three"

127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "hello1"
3) "hello12"
4) "hello123"
127.0.0.1:6379> ltrim mylist 1 2			# trim操作,对列表只保留给定的下标范围内的数据
OK
127.0.0.1:6379> lrange mylist 0 -1			# 可以发现只有下标在1~2之间的被保留了,其他都被删除了
1) "hello1"
2) "hello12"


127.0.0.1:6379> rpush mylist hello1 hello2 hello3
(integer) 3
127.0.0.1:6379> lrange mylist 0 -1
1) "hello1"
2) "hello2"
3) "hello3"
127.0.0.1:6379> rpoplpush mylist otherlist	# 从列表的尾部弹出一个数据,插入目标列表的头部
"hello3"
127.0.0.1:6379> rpoplpush mylist otherlist
"hello2"
127.0.0.1:6379> lrange otherlist 0 -1		# 对另外一个列表进行的是头插操作
1) "hello2"
2) "hello3"


127.0.0.1:6379> exists list					# 判断列表是否存在
(integer) 0
127.0.0.1:6379> lset list 0 val				# lset可以更改列表中指定下标的数据
(error) ERR no such key						# 但是如果列表的key是无效的,会失败
127.0.0.1:6379> lpush list val
(integer) 1
127.0.0.1:6379> lrange list 0 -1
1) "val"
127.0.0.1:6379> lset list 0 value			# key存在,可以修改成功
OK
127.0.0.1:6379> lrange list 0 -1
1) "value"
127.0.0.1:6379> lset list 1 val				# lset设置尚未存在的下标也会报错
(error) ERR index out of range

127.0.0.1:6379> rpush mylist hello
(integer) 1
127.0.0.1:6379> rpush mylist world
(integer) 2
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "world"
127.0.0.1:6379> linsert mylist before world in	# linsert在列表的某一个数据的前面或后面插入一个值
(integer) 3
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "in"
3) "world"
127.0.0.1:6379> linsert mylist after world end	# 在后面插值
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "in"
3) "world"
4) "end"

Set(不重复集合)

Set中存的value不可以重复。

命令汇总
命令具体写法作用
saddsadd [key] [value1] [value2] … …向set集合中添加一个或多个元素
smemberssmembers [key]查看集合中的所有元素
sismembersismember [key] [value]判断集合中是否有[value]
scardscard [key]返回集合中的元素个数
sremsrem [key] [value]删除集合中的制定元素
srandmembersrandmember [key] [count]从集合中随机取出count个元素,不加count
则默认取出1个(不会删除)
spopspop [key] [count]从集合中弹出count个元素,
不加count则弹出一个(会删除)
smovesmove [sourceKey] [targetKey] [value]从sourceKey的集合将value移动到targetKey集合
sdiffsdiff [key1] [key2]得到两个集合的差集
sintersinter [key1] [key2]得到两个集合的交集
sunionsunion [key1] [key2]得到两个集合的并集
用例
127.0.0.1:6379> SADD myset "hello"				# 向set集合中添加一个或多个元素
(integer) 1
127.0.0.1:6379> sadd myset world
(integer) 1
127.0.0.1:6379> smembers myset					# 查看set集合中的所有元素
1) "world"
2) "hello"
127.0.0.1:6379> sismember myset hello			# 判断set集合中是否存在指定的值
(integer) 1
127.0.0.1:6379> sismember myset hel				# 存在则返回1,不存在返回0
(integer) 0

127.0.0.1:6379> scard myset						# 返回set集合的元素个数
(integer) 2
127.0.0.1:6379> smembers myset
1) "world"
2) "hello"
127.0.0.1:6379> srem myset hello				# 删除set集合中的指定的元素
(integer) 1
127.0.0.1:6379> smembers myset
1) "world"

127.0.0.1:6379> sadd myset java php cpp c html	# 添加一些测试用例
(integer) 5
127.0.0.1:6379> smembers myset
1) "java"
2) "c"
3) "cpp"
4) "php"
5) "html"
127.0.0.1:6379> SRANDMEMBER myset				# srandmember 返回set集合中随机一个元素
"c"
127.0.0.1:6379> SRANDMEMBER myset 
"c"
127.0.0.1:6379> SRANDMEMBER myset 
"html"
127.0.0.1:6379> SRANDMEMBER myset 2				# 加一个数字,表示返回set集合中的随机多个元素
1) "c"
2) "cpp"

127.0.0.1:6379> SMEMBERS myset
1) "java"
2) "html"
3) "c"
4) "php"
5) "cpp"
127.0.0.1:6379> spop myset						# 从set集合中随机弹出一个元素(会删除)
"java"
127.0.0.1:6379> spop myset
"c"
127.0.0.1:6379> spop myset 2					# 从set集合中随机弹出n个元素(会删除)
1) "html"
2) "cpp"
127.0.0.1:6379> SMEMBERS myset
1) "php"

127.0.0.1:6379> SMEMBERS myset
1) "fall"
2) "world"
3) "hello"
127.0.0.1:6379> SMOVE myset toset fall			# 将制定元素(fall)从一个set集合(myset)移动到另一个集合(toset)
(integer) 1
127.0.0.1:6379> smembers toset
1) "fall"
127.0.0.1:6379> SMEMBERS myset
1) "world"
2) "hello"

127.0.0.1:6379> SMEMBERS myset1
1) "d"
2) "c"
3) "a"
4) "b"
127.0.0.1:6379> SMEMBERS myset2
1) "d"
2) "c"
3) "f"
4) "e"
5) "g"
127.0.0.1:6379> SDIFF myset1 myset2				# 取两个集合的差集输出
1) "a"
2) "b"
127.0.0.1:6379> SINTER myset1 myset2			# 取两个集合的交集输出
1) "d"
2) "c"
127.0.0.1:6379> SUNION myset1 myset2			# 取两个集合的并集输出
1) "e"
2) "d"
3) "c"
4) "a"
5) "g"
6) "f"
7) "b"

Hash(哈希)

Hash可以看作一个Map集合,就是一个key-map(map中又是一个key-value对)

Hash比较适合用于存储对象

命令汇总

注:这里统一用key表示每一个hash的key,用field-value代表hash内部的键值对。

命令具体写法作用
hsethset [key] [field] [value]添加k-v对
hgethget [key] [field]通过key与field得到对应hash中对应的value
hmsethmset [key] [field1] [value1] [field2] [value2] … …设置对个k-v对
hmgethmget [key] [field1] [field2] … …通过多个field得到多个value
hgetallhgetall [key]得到hash中的全部元素(field与value一起显示)
hdelhdel [key] [field1] [field2]删除hash中指定的k-v对
hlenhlen [key]返回hash的长度(一对k-v对算1长度)
hexistshexists [key] [field]判断hash中[field]是否存在
hkeyshkeys [key]得到hash中的全部field
hvalshvals [key]得到hash中的全部value
hincrbyhincrby [key] [field] [n]对hash中的[field]进行步长为n的自增操作
hsetnxhsetnx [key] [field] [value]如果hash中不存在[field]则添加k-v对
hscanhscan [key] [cursor] match [pattern] [count]目前还不明白如何使用
用例
127.0.0.1:6379> hset myhash f1 v1					# 给指定hash添加一个具体的k-v对
(integer) 1
127.0.0.1:6379> hget myhash f1						# 通过hash的key和内部field获得value
"v1"
127.0.0.1:6379> hmset myhash f2 v2 f3 v3 f4 v4		# 设置多个k-v对
OK
127.0.0.1:6379> hmget myhash f2 f3					# 得到多个value
1) "v2"
2) "v3"
127.0.0.1:6379> hgetall myhash						# 得到指定hash的全部元素(field与value都获得)
1) "f1"
2) "v1"
3) "f2"
4) "v2"
5) "f3"
6) "v3"
7) "f4"
8) "v4"
127.0.0.1:6379> hdel myhash f1 f2					# 删除hash中的指定k-v对
(integer) 2
127.0.0.1:6379> hgetall myhash
1) "f3"
2) "v3"
3) "f4"
4) "v4"


127.0.0.1:6379> hmset myhash f1 v1 f2 v2 f3 v3 f4 v4
OK
127.0.0.1:6379> hgetall myhash
1) "f1"
2) "v1"
3) "f2"
4) "v2"
5) "f3"
6) "v3"
7) "f4"
8) "v4"
127.0.0.1:6379> hlen myhash							# 返回当前hash的长度(一对k-v对长度为1)
(integer) 4
127.0.0.1:6379> HEXISTS myhash v2					# 判断hash中是否存在指定的field
(integer) 0
127.0.0.1:6379> HEXISTS myhash f2
(integer) 1

127.0.0.1:6379> hkeys myhash						# 得到hash的所有field
1) "f1"
2) "f2"
3) "f3"
4) "f4"
127.0.0.1:6379> hvals myhash						# 得到hash的所有value
1) "v1"
2) "v2"
3) "v3"
4) "v4"
127.0.0.1:6379> hset myhash num 5
(integer) 1
127.0.0.1:6379> hincrby myhash num 2				# 带步长的自增
(integer) 7
127.0.0.1:6379> HINCRBY myhash num -1				# 通过设置步长为负数来实现自减
(integer) 6
127.0.0.1:6379> hsetnx myhash num 2					# 如果field存在,添加失败
(integer) 0
127.0.0.1:6379> hsetnx myhash fall hello			# 如果field不存在,则添加该field
(integer) 1

ZSet(有序集合)

Zset可以用于需要排序的数据(如工资表、成绩表)

或者用于表示分不同重要程度的消息、排行榜排名

命令汇总
命令具体写法作用
zaddzadd [key] [score2] [value] [score2] [value]带score添加元素(score可用来排序)
zrangezrange [key] [start] [end] [withscores]返回zset中的元素(一般用0~-1取全部),带withscores可以显示score
zrangebyscorezrangebyscore [key] [start] [end] [withscores]将zset中的元素升序排序,取score在start-end之间的返回(-inf,+inf可以得到全部),带withscores可以显示score
zrevrangebyscorezrevrangebyscore [key] [start] [end] [withscores]与zrangebyscore类似,进行降序排序
zremzrem [key] [value]删除zset中指定元素
zcardzcard [key]返回zset中的元素个数
zcountzcount [key] [start] [end]返回zset中score在start~end之间的元素
用例
127.0.0.1:6379> zadd myzset 1 one 2 two 3 three						# 向zset集合中放入一个或多个带score的值
(integer) 3
127.0.0.1:6379> zrange myzset 0 -1									# 返回zset中的元素
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> zrange myzset 0 -1 withscores						# 返回元素,并且附带各自的score
1) "one"
2) "1"
3) "two"
4) "2"
5) "three"
6) "3"


127.0.0.1:6379> zadd salary 2000 lisi 1500 zhangsan 4000 wangwu		
(integer) 3
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores	# 将zset中的元素按照score从小到大排序,带score
															#(-inf,+inf表示最小与最大的整数,这就表示是全范围的)
1) "zhangsan"
2) "1500"
3) "lisi"
4) "2000"
5) "wangwu"
6) "4000"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf				# 将zset中的元素按score从小到打排序,不带score
1) "zhangsan"
2) "lisi"
3) "wangwu"
127.0.0.1:6379> ZRANGEBYSCORE salary 1000 2500 withscores	# 将zset中元素从小到大排序,且范围在1000-2500之间
1) "zhangsan"
2) "1500"
3) "lisi"
4) "2000"
127.0.0.1:6379> ZREVRANGEBYSCORE salary +inf -inf withscores	# 将zset中的元素从大到小排序
1) "wangwu"
2) "4000"
3) "lisi"
4) "2000"
5) "zhangsan"
6) "1500"

127.0.0.1:6379> zrange salary 0 -1
1) "zhangsan"
2) "lisi"
3) "wangwu"
127.0.0.1:6379> zrem salary lisi							# 删除zset中指定的元素
(integer) 1
127.0.0.1:6379> zrange salary 0 -1
1) "zhangsan"
2) "wangwu"
127.0.0.1:6379> zcard salary								# 返回zset中元素的个数
(integer) 2
127.0.0.1:6379> zadd myzset 1 one 2 two 3 three 4 four
(integer) 4
127.0.0.1:6379> zcount myzset 1 3							# 返回score在指定范围之间的元素的个数
(integer) 3
127.0.0.1:6379> 

七、三种特殊数据类型

Geospatial 地理位置

命令汇总
命令具体写法作用
GEOADDGEOADD [key] [经度] [维度] [value]向指定geospatial添加一个或多个带经纬度的元素
GEOPOSGEOPOS [key] [value]得到一个或多个元素的经纬度
GEODISTGEODIST [key] [value1] [value2] [unit]得到两个元素之间的距离,用[unit]指定单位
GEORADIUSGEORADIUS [key] [经度] [维度] [dist] [unit] [withcoord] [withdist] (count) [count]得到与给定的经度纬度距离在[dist] (unit作为单位)范围内的元素
withcoord可以使元素附带坐标显示,withdist使元素附带与当前坐标的距离显示,count + [count] 可以指定显示的数量(由距离从近到远排序)
GEORADIUSBYMEMBERGEORADIUSBYMEMBER [key] [value] [dist] [unit] [withcoord] [withdist] [count]得到与指定的元素距离在dist范围内的元素,withcoord等作用与上面的相同
GEOHASHGEOHASH [key] [value]返回指定元素的HASH格式的坐标(不常用)

由于Geospatial 底层实现原理就是ZSet,因此也可以使用ZSet的命令来操作Geospatial

用例
127.0.0.1:6379> GEOADD city 121.47 31.23 shanghai 106.50 29.53 chongqing	# geoadd给key添加对应经纬度的元素
(integer) 2
127.0.0.1:6379> GEOADD city 114.05 22.52 shenzhen
(integer) 1
127.0.0.1:6379> geoadd city 120.16 30.24 hangzhou
(integer) 1
127.0.0.1:6379> geoadd city 108.96 34.26 xian
(integer) 1


127.0.0.1:6379> GEOPOS city beijing							# geopos得到一个或多个元素的经纬度
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
127.0.0.1:6379> GEOPOS city beijing chongqing
1) 1) "116.39999896287918091"
   2) "39.90000009167092543"
2) 1) "106.49999767541885376"
   2) "29.52999957900659211"

127.0.0.1:6379> GEODIST city beijing hangzhou 				# geodist得到两个元素之间的距离,可以通过km等指定单位
"1127337.7813"
127.0.0.1:6379> GEODIST city beijing hangzhou km
"1127.3378"


127.0.0.1:6379> GEORADIUS city 110 30 1000 km				# georadius得到指定经纬度的一定距离范围内的元素
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
127.0.0.1:6379> GEORADIUS city 110 30 1000 km withcoord		# withcoord可以附带元素的经纬度
1) 1) "chongqing"
   2) 1) "106.49999767541885376"
      2) "29.52999957900659211"
2) 1) "xian"
   2) 1) "108.96000176668167114"
      2) "34.25999964418929977"
3) 1) "shenzhen"
   2) 1) "114.04999762773513794"
      2) "22.5200000879503861"
4) 1) "hangzhou"
   2) 1) "120.1600000262260437"
      2) "30.2400003229490224"
127.0.0.1:6379> GEORADIUS city 110 30 1000 km withdist		# withdis附带与目标元素的距离
1) 1) "chongqing"
   2) "341.9374"
2) 1) "xian"
   2) "483.8340"
3) 1) "shenzhen"
   2) "924.6408"
4) 1) "hangzhou"
   2) "977.5143"
127.0.0.1:6379> GEORADIUS city 110 30 1000 km withdist count 1	# 通过count指定返回的元素个数(由距离近到远排序)
1) 1) "chongqing"
   2) "341.9374"
127.0.0.1:6379> GEORADIUS city 110 30 1000 km withdist count 2
1) 1) "chongqing"
   2) "341.9374"
2) 1) "xian"
   2) "483.8340"


127.0.0.1:6379> GEORADIUSBYMEMBER city hangzhou 500 km withdist	# georadiusbymember 返回两个元素之间的距离
1) 1) "hangzhou"
   2) "0.0000"
2) 1) "shanghai"
   2) "166.7613"

127.0.0.1:6379> GEOHASH city xian								# geohash对元素的经纬度进行hash处理
1) "wqj6zky6bn0"
127.0.0.1:6379> GEOHASH city xian beijing
1) "wqj6zky6bn0"
2) "wx4fbxxfke0"

# Geo底层实际是通过ZSet实现的,因此可以用zrange+key查看当前的元素,也可以用Zset的命令删除元素
127.0.0.1:6379> zrange city 0 -1							
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
5) "shanghai"
6) "beijing"

Hyperloglog

Hyperloglog可以用来计算基数(也就是不重复的元素数量)

如A{1,3,5,6,5} 基数为4,因为5有一个是重复的。

Hyperloglog是一种可以基数统计的数据结构。

Hyperloglog的优点:占用的内存是固定的,即时是2^64个不同的数据,也只需要12KB的内存空间。

但是Hyperloglog也存在一定的错误率(0.81%),因此如果项目有一定容错率,可以使用它来进行计数的作用。

命令汇总
命令具体写法作用
PFADDPFADD [key] [value1] [value2] … …向数据结构中存入元素
PFCOUNTPFCOUNT [key]计算对应key的元素个数(不重复的)
PFMERGEPFMERGE [destkey] [sourcekey1] [sourcekey2] …将多组元素合并为一组元素(destkey)
用例
127.0.0.1:6379> PFADD mypf a b c d e f g h				# 存入元素
(integer) 1
127.0.0.1:6379> PFCOUNT mypf							# 返回不重复的元素个数
(integer) 8
127.0.0.1:6379> PFADD mypf2 b d i j k l m a				
(integer) 1
127.0.0.1:6379> PFCOUNT mypf2
(integer) 8
127.0.0.1:6379> PFMERGE mypf3 mypf mypf2				# 合并两组数据
OK
127.0.0.1:6379> PFCOUNT mypf3							# 合并后因为有重复数据,因此count减少了
(integer) 13

Bitmap(位图)

将数据用位存储(0与1存储)。 用0和1来计数(如计算是否打卡用0和1表示)会更加节省空间与高效。

命令汇总
命令具体写法作用
SETBITsetbit [key] [offset] [value]给指定的位图的[offset]位置设置位(0/1)
GETBITgetbit [key] [offset]得到指定位图的[offset]的位值
BITCOUNTbitcount [key] [start end]得到范围在[start end]之间的位值为1的数量,
不指定范围则计算的是全部的数量

**注意:**bitcount中的start-end范围,单位是字节,而8个位相当于一个字节。而不是通过每一个offset来判断。bitcount key 0 0代表查找offset在0-7的位值为1的数量

用例
127.0.0.1:6379> SETBIT sign 0 1
(integer) 0
127.0.0.1:6379> SETBIT sign 1 1
(integer) 0
127.0.0.1:6379> SETBIT sign 2 0
(integer) 0
127.0.0.1:6379> SETBIT sign 3 1
(integer) 0
127.0.0.1:6379> SETBIT sign 4 1
(integer) 0
127.0.0.1:6379> getbit sign 0
(integer) 1
127.0.0.1:6379> getbit sign 2
(integer) 0
127.0.0.1:6379> BITCOUNT sign
(integer) 4

八、事务

Redis事务的本质就是一组命令的集合,执行事务时,事务中的所有命令会序列化,按照输入的顺序依次执行

Redis事务没有隔离级别的概念,并且Redis的事务不保证原子性

事务命令

命令作用
MULTI开启事务
EXEC执行事务
DISCARD取消事务
watch [key] [key] … …监控元素
unwatch取消监控
常规用例

正常执行事务:

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"

取消事务

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get ke
QUEUED
127.0.0.1:6379> DISCARD				# 取消事务
OK
事务执行过程中出错
①:命令出错

命令出错时,整个事务的命令都不会被执行

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v2
QUEUED
127.0.0.1:6379> set k2 v3
QUEUED
127.0.0.1:6379> getset key		# 输入不符合要求的命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors.	# 显示事务执行失败

②:执行过程出错

命令非语法的错误时,错误命令不会被执行,但是别的命令正常执行

127.0.0.1:6379> set k1 v1			# 事先设置k1的值为字符串类型
OK
127.0.0.1:6379> MULTI				# 开启事务
OK
127.0.0.1:6379> INCR k1				# 对k1进行自增操作(非语法错误,没有被检查到)
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 k1
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) OK
4) "v1"
5) "v2"

Redis的锁

Redis通过watch命令监控指定的key来实现乐观锁的功能。

乐观锁:认为什么时候都不会出问题,所以不会上锁,只会在更新数据的时候判断,在更新的期间该数据是否被修改了。

示例

正常情况,watch后事务中修改,没有什么反应

# 正常的情况:
127.0.0.1:6379> set save 200
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch save			# watch监控"save"
OK
127.0.0.1:6379> MULTI 				# 开启事务
OK
127.0.0.1:6379> DECRBY save 100
QUEUED
127.0.0.1:6379> INCRBY out 100
QUEUED
127.0.0.1:6379> EXEC				# 正常执行后没有问题
1) (integer) 100
2) (integer) 100

在事务中,有其他的线程修改了被监控的元素时,watch就会生效,使之后事务中的修改失效:

127.0.0.1:6379> WATCH save
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECRBY save 10
QUEUED
127.0.0.1:6379> INCRBY out 10
QUEUED
127.0.0.1:6379> EXEC			# 监控的值在其他线程被修改时,执行事务会失败
(nil)


# 在save被watch后,事务执行前,已经在另一个线程修改了save的值
127.0.0.1:6379> DECRBY save 20
(integer) 80
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值