一、5大基础类型
1. 基本命令
keys * #查看当前库所有k
set k v #设置k, v
get k #获取k
expire k 10 #设置key过期时间 单位秒
ttl k #查看k的剩余时间
select 0 #切换数据库 0/16 默认16个库 配置文件中配置
flushdb #清空当前库k
flushall #清空所有库k
move k #移除k
exists k,k1 #查询k是否存在
type k #查询k对应的v存储类型
2. String类型
append k v #追加字符串,如果k不存在就相当添加
strlen k #获取字符串长度
-----------------------------
incr k #原子加1
decr k #原子减1
incrby k 10 #原子加10 10是步长 可用在浏览量
decrby k 10 #原子减10 10是步长
-----------------------------
getrange k 0 3 #截取字符串 从0位置-3位置 4个字符串
getrange k 0 -1 #去字符串所有内容
-----------------------------
setrange k 1 xxx #从下标位1开始替换后面的内容
-----------------------------
setex k v 60 #设置k,v,过期时间
-----------------------------
setnx k v #k不存在再设置值(常在分布式锁中使用)
-----------------------------
mset k1 v1 k2 v2 k3 v3 #批量设置k,v
msetnx k1 v1 k4 v4 #存在的key,值就不设置了
mget k1 k2 k3 #获取多个key值
-----------------------------
getset k v #先获取在设置, CAS 原子操作
e.g
114.116.230.154:6397> getset db redis
(nil)
114.116.230.154:6397> get db
"redis"
114.116.230.154:6397> getset db mongodb
"redis"
114.116.230.154:6397> get db
"mongodb"
3. List
list 可以用来实现队列、栈,所有的命令都是以==L==开头。
#从左向右添加数据,如果集合不存在创建集合
114.116.230.154:6397> lpush list one #lpush:从左向右添加,后添加的总是在头部0的位置(栈)
(integer) 1
114.116.230.154:6397> lpush list two
(integer) 2
114.116.230.154:6397> lpush list three
(integer) 3
114.116.230.154:6397> lrange list 0 -1 #获取list中的所有值
1) "three"
2) "two"
3) "one"
114.116.230.154:6397> lrange list 0 1 #获取list中的区间值,如果获取第一个值 lrange list 0 0
1) "three"
2) "two"
------------------------------------------
#从右向左添加数据,如集合不存在添加集合
114.116.230.154:6397> rpush list right #rpush:从右向左添加
(integer) 4
114.116.230.154:6397> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
------------------------------------------
#从左边或者右边弹出元素
114.116.230.154:6397> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
114.116.230.154:6397> lpop list #弹出左边第一个元素,并移除
"three"
114.116.230.154:6397> lrange list 0 -1
1) "two"
2) "one"
3) "right"
114.116.230.154:6397> rpop list #弹出右边第一个元素,并移除
"right"
114.116.230.154:6397> lrange list 0 -1
1) "two"
2) "one"
# lpush + lpop(栈),lpush + rpop(队列)
-----------------------------------------
#获取集合中指定下标元素
114.116.230.154:6397> lrange list 0 -1 #展示list集合中所有元素
1) "two"
2) "one"
114.116.230.154:6397> lindex list 0 #根据list集合下标获取集合元素
"two"
114.116.230.154:6397> lindex list 1
"one"
-----------------------------------------
#获取集合长度
114.116.230.154:6397> lrange list 0 -1
1) "two"
2) "one"
114.116.230.154:6397> llen list #获取list集合长度
(integer) 2
-----------------------------------------
#移除集合中的指定元素值,并指定移除几个
114.116.230.154:6397> lpush list one two three three #从左向右添加值
(integer) 4
114.116.230.154:6397> lrange list 0 -1
1) "three"
2) "three"
3) "two"
4) "one"
114.116.230.154:6397> lrem list 1 one #移除指定值one,并移除1个
(integer) 1
114.116.230.154:6397> lrange list 0 -1
1) "three"
2) "three"
3) "two"
(0.53s)
114.116.230.154:6397> lrem list 3 three #移除指定值three,并移除3个,但实际就2个所以都移除了。
(integer) 2
114.116.230.154:6397> lrange list 0 -1
1) "two"
---------------------------------------
#结合修剪,截取指定下标 n~m 的元素包含n,m的内容
114.116.230.154:6397> lrange mylist 0 -1
1) "h5"
2) "h4"
3) "h3"
4) "h2"
5) "h1"
114.116.230.154:6397> ltrim mylist 1 3 #ltrim:集合修剪操作,只保留下标1~3的元素
OK
114.116.230.154:6397> lrange mylist 0 -1
1) "h4"
2) "h3"
3) "h2"
---------------------------------------
#将一个集合的最后一个元素pop,push到另一个集合中
114.116.230.154:6397> lrange mylist 0 -1
1) "h6"
2) "h5"
3) "h4"
4) "h3"
5) "h2"
114.116.230.154:6397> rpoplpush mylist newlist #从mylist最后一个元素移除,到新列表newlist
"h2"
114.116.230.154:6397> lrange newlist 0 -1
1) "h2"
114.116.230.154:6397> lrange mylist 0 -1
1) "h6"
2) "h5"
3) "h4"
4) "h3"
---------------------------------------
#指定下标赋值
114.116.230.154:6397> lrange mylist 0 -1
1) "h6"
2) "h5"
3) "h4"
4) "h3"
114.116.230.154:6397> lset list 0 hdx #指定下标赋值list不存在报错,所以先lpush或rpush
(error) ERR no such key
114.116.230.154:6397> lset mylist 1 h7 #向mylist集合的下标 1 处赋值 h7, 如下标不存在也会报错
OK
114.116.230.154:6397> lrange mylist 0 -1 #结果原下标 1 处的 h5 被覆盖
1) "h6"
2) "h7"
3) "h4"
4) "h3"
---------------------------------------
#指定内容元素前or后插入新元素
114.116.230.154:6397> lrange mylist 0 -1
1) "h6"
2) "h7"
3) "h4"
4) "h3"
114.116.230.154:6397> linsert mylist before h7 hdx #向mylist集合中h7元素前面插入hdx元素
(integer) 5
114.116.230.154:6397> lrange mylist 0 -1
1) "h6"
2) "hdx"
3) "h7"
4) "h4"
5) "h3"
# 操作类似一个连表 [before] Node [after]
小结
底层实现相当一个连表,比如lpush是向头节点插入数据,rpush是向尾节点插入数据。可以向某个具体节点插入数据 比如 linsert。
可用用作队列,栈
4. set
set值是不能重复的,set的命令都是==S==开头
#set的基本操作
114.116.230.154:6397> sadd myset hello #向myset集合中set值
(integer) 1
114.116.230.154:6397> sadd myset hdx
(integer) 1
114.116.230.154:6397> smembers myset #展示myset集合中所有值
1) "hdx"
2) "hello"
114.116.230.154:6397> sismember myset hdx #查询某个值(hdx),是否存在集合中,存在返回1
(integer) 1
---------------------------------------
#获取set集合元素个数
114.116.230.154:6397> smembers myset
1) "hdx"
2) "hello"
114.116.230.154:6397> scard myset #查看set集合元素个数
(integer) 2
---------------------------------------
#移除set集合元素
114.116.230.154:6397> smembers myset #展示set集合中的元素[hdx, hello]
1) "hdx"
2) "hello"
114.116.230.154:6397> srem myset hdx #移除set集合中的hdx元素
(integer) 1
114.116.230.154:6397> scard myset #查询set集合中元素个数
(integer) 1
114.116.230.154:6397> smembers myset #展示set集合中全部元素,只剩下hello
1) "hello"
----------------------------------------
#从set集合中随机获取指定个数元素
114.116.230.154:6397> sadd myset a b c d e f g #向set集合中添加7个元素
(integer) 7
114.116.230.154:6397> smembers myset #展示set集合中所用元素
1) "d"
2) "c"
3) "a"
4) "g"
5) "f"
6) "e"
7) "b"
114.116.230.154:6397> srandmember myset 1 #随机获取 1 个元素
1) "b"
114.116.230.154:6397> srandmember myset 2 #随机获取 2 个元素
1) "a"
2) "g"
#实现抽奖需求
----------------------------------------
#将一个元素从一个集合移动到另一个集合
114.116.230.154:6397> smembers myset
1) "a"
2) "g"
3) "f"
4) "c"
5) "d"
6) "e"
7) "b"
114.116.230.154:6397> smove myset newset a #将myset集合中的a元素移动到newset集合中
(integer) 1
114.116.230.154:6397> smembers newset
1) "a"
114.116.230.154:6397> smembers myset
1) "g"
2) "f"
3) "c"
4) "d"
5) "e"
6) "b"
---------------------------------------
#差集
#交集
#并集
#差集
114.116.230.154:6397> smembers myset #myset集合元素
1) "g"
2) "f"
3) "c"
4) "d"
5) "e"
6) "b"
114.116.230.154:6397> smembers newset #newset集合元素
1) "a"
2) "x"
3) "y"
4) "b"
5) "z"
114.116.230.154:6397> sdiff myset newset #myset与newset的差集(myset中哪些元素是newset没有的,比如 b元素 )
1) "f"
2) "g"
3) "d"
4) "c"
5) "e"
114.116.230.154:6397> sdiff newset myset #newset与myset的差集(newset中哪些元素是myset中没有的,比如 b元素)
1) "y"
2) "x"
3) "a"
4) "z"
#根据 myset,newset的顺序不同,结果集也不同。
-----------------------------
#交集
114.116.230.154:6397> smembers myset
1) "g"
2) "f"
3) "c"
4) "d"
5) "e"
6) "b"
114.116.230.154:6397> smembers newset
1) "a"
2) "x"
3) "y"
4) "b"
5) "z"
114.116.230.154:6397> sinter myset newset #myset与newset的交集 b
1) "b"
114.116.230.154:6397> sinter newset myset #newset与myset的交集 b
1) "b"
#找出两个集合共有的元素
-----------------------------
#并集
114.116.230.154:6397> smembers myset
1) "g"
2) "f"
3) "c"
4) "d"
5) "e"
6) "b"
114.116.230.154:6397> smembers newset
1) "a"
2) "x"
3) "y"
4) "b"
5) "z"
114.116.230.154:6397> SUNION myset newset #myset 与newset的并集
1) "c"
2) "d"
3) "e"
4) "b"
5) "a"
6) "f"
7) "g"
8) "y"
9) "x"
10) "z"
#相当两个集合元素去重
5. hash
String数据类型是key:value
hash数据类型是 key : (k:v),也可以理解成一对对象里面有多个属性。hash命令是以==h==开头。
#hash 的形式
114.116.230.154:6397> hset myhash filed1 hdx #向myhash中设置一个键值对 k是field1,v是hdx
(integer) 1
114.116.230.154:6397> hget myhash filed1 #获取myhash中field1属性的值
"hdx"
114.116.230.154:6397> hmset myhash field1 hello field2 world #hmset向myhash中设置多个键值
OK
114.116.230.154:6397> hmget myhash field1 field2 #hmget 通过多个键从myhash中取出多个值
1) "hello"
2) "world"
114.116.230.154:6397> hgetall myhash #取出myhash的所有键和值
1) "filed1" #键
2) "hdx" #值
3) "field1" #键
4) "hello" #值
5) "field2" #键
6) "world" #值
114.116.230.154:6397> hkeys myhash #获取myhahs中所有的key
1) "filed1"
2) "field2"
114.116.230.154:6397> hvals myhash #获取myhash中所有的value
1) "hdx"
2) "world"
-----------------------------------
114.116.230.154:6397> hgetall myhash
1) "filed1" #键
2) "hdx" #值
3) "field2" #键
4) "world" #值
114.116.230.154:6397> hlen myhash #获取myhash中的长度,结果证明是键值对的个数。
(integer) 2
-----------------------------------
114.116.230.154:6397> hgetall myhash
1) "filed1"
2) "hdx"
3) "field2"
4) "world"
114.116.230.154:6397> hexists myhash field2 #判断 myhash中field2属性是否存在
(integer) 1
-----------------------------------
114.116.230.154:6397> hset myhash field3 5
(integer) 1
114.116.230.154:6397> hincrby myhahs field3 1 #field3属性自增 1
(integer) 1
114.116.230.154:6397> hincrby myhash field3 -1 #field3属性自减 1
(integer) 4
hash数据类型可以用于对象的存储,而string只适合简单的字符串存储。
6. zset
zset 命令与set类似,zset命令以==z==开头,zset是有序的集合,只支持最小值到最大值排序。
114.116.230.154:6397> zadd salary 500 hdx
(integer) 1
114.116.230.154:6397> zadd salary 1500 haha
(integer) 1
114.116.230.154:6397> zadd salary 2500 sanmao
(integer) 1
114.116.230.154:6397> zrangebyscore salary -inf +inf
1) "hdx"
2) "haha"
3) "sanmao"
114.116.230.154:6397> zrangebyscore salary +inf -inf #不支持
(empty list or set)
----------------------------------------
114.116.230.154:6397> zrange salary 0 -1 #查询所有key
1) "hdx"
2) "haha"
3) "sanmao"
114.116.230.154:6397> zrem salary sanmao #移除sanmao元素
(integer) 1
114.116.230.154:6397> zrange salary 0 -1
1) "hdx"
2) "haha"
----------------------------------------
114.116.230.154:6397> zcard salary #查看个数
(integer) 2
二、3大特殊类型
1. geospatial 地理空间
朋友定位,附近的人,打车
#添加城市经纬度 前面是精度[-180~180] 纬度[-85~85]
114.116.230.154:6397> geoadd china:city 116.40 39.91 beijing
(integer) 1
114.116.230.154:6397> geoadd china:city 121.47 31.23 shanghai
(integer) 1
114.116.230.154:6397> geoadd china:city 106.50 29.53 chongqing
(integer) 1
114.116.230.154:6397> geoadd china:city 114.05 22.52 shenzhen
(integer) 1
114.116.230.154:6397> geoadd china:city 120.16 30.24 hangzhou
(integer) 1
114.116.230.154:6397> geoadd china:city 108.96 34.26 xian
(integer) 1
---------------------------------
#geopos 获取城市北京经纬度
114.116.230.154:6397> geopos china:city beijing
1) "116.39999896287918"
2) "39.909999566644508"
---------------------------------
#geopos两个城市之间的直线距离
114.116.230.154:6397> geopos china:city beijing
1) 1) "116.39999896287918"
2) "39.909999566644508"
114.116.230.154:6397> geodist china:city beijing shanghai
"1068370.2525"
(1.72s)
114.116.230.154:6397> geodist china:city beijing shanghai km
"1068.3703"
---------------------------------
#georadius附近的人,给出一个精度查询半径之内的值
114.116.230.154:6397> georadius china:city 110 30 500 km
1) "chongqing"
2) "xian"
#精度110纬度30 半径500千米以内的城市
-----------------
#精度110纬度30 半径500千米以内的城市,并显示直线距离withdist,经纬度withcoord,查询个数count
114.116.230.154:6397> georadius china:city 110 30 500 km withcoord withdist count 1
1) 1) "chongqing"
2) "341.9374"
3) 1) "106.49999767541885"
2) "29.529999579006592"
------------------
#查询某个城市(key)的半径400千米的城市
114.116.230.154:6397> georadiusbymember china:city hangzhou 400 km
1) "hangzhou"
2) "shanghai"
------------------
#geospatial底层是通过zset实现的,所以移除元素可以是用zrem
114.116.230.154:6397> zrange china:city 0 -1
1) "chongqing"
2) "xian"
3) "shenzhen"
4) "hangzhou"
5) "shanghai"
6) "beijing"
114.116.230.154:6397> zrem china:city xian
(integer) 1
114.116.230.154:6397> zrange china:city 0 -1
1) "chongqing"
2) "shenzhen"
3) "hangzhou"
4) "shanghai"
5) "beijing"
2. hyperloglog
是什么?是两个set集合去重合并返回元素数量。官方0.81%错误率,如果允许误差建议使用,效率高节省空间。如果不允许误差那么不建议使用。
114.116.230.154:6397> pfadd mykey a b c d e f g h i j #添加不可重复的元素集合
(integer) 1
114.116.230.154:6397> pfcount mykey #获取元素数量
(integer) 10
114.116.230.154:6397> pfadd mykey2 i j z x c v b n m
(integer) 1
114.116.230.154:6397> pfcount mykey
(integer) 10
114.116.230.154:6397> pfmerge mykey3 mykey mykey2 #合并mykye和mykey2元素,结果存到mykey3中
OK
114.116.230.154:6397> pfcount mykey3 #mykey3数量15,将两个集合去重合并统计数值
(integer) 15
3. bitmaps
位存储,记录0,1状态
114.116.230.154:6397> setbit sign 0 1 #
(integer) 0
114.116.230.154:6397> setbit sign 1 0
(integer) 0
114.116.230.154:6397> setbit sign 2 0
(integer) 0
114.116.230.154:6397> setbit sign 3 1
(integer) 0
114.116.230.154:6397> setbit sign 4 1
(integer) 0
114.116.230.154:6397> setbit sign 5 0
(integer) 0
114.116.230.154:6397> setbit sign 6 0
(integer) 0
114.116.230.154:6397> getbit sign 3 #获取到sign里面3 的状态
(integer) 1
114.116.230.154:6397> getbit sign 6 #获取到sign里面6 的状态
(integer) 0
114.116.230.154:6397> bitcount sign #获取到sign里面状态为1的个数
(integer) 3
三、事务
1.redis事务特性
redis的事务是不保证原子性的(redis单条命令是保证原子性的)
redis 事务没有隔离级别概念
redis事务的特性:一次性、顺序性、排他性
redis事务:
开启事务(multi) -> 命令入队(...) -> 执行事务(exec)
2.redis事务基本操作
#正常开启事务执行事务
114.116.230.154:6397> multi #开启事务
OK
114.116.230.154:6397> set k1 v1
QUEUED
114.116.230.154:6397> set k2 v2
QUEUED
114.116.230.154:6397> get k2
QUEUED
114.116.230.154:6397> set k3 v3
QUEUED
114.116.230.154:6397> exec #执行队列,并结束事务
1) OK
2) OK
3) "v2"
4) OK
-----------------------
114.116.230.154:6397> multi
OK
114.116.230.154:6397> set k1 v1
QUEUED
114.116.230.154:6397> set k2 v2
QUEUED
114.116.230.154:6397> set k4 v4
QUEUED
114.116.230.154:6397> discard #取消事务,入队的命令都不会执行
OK
114.116.230.154:6397> get k4
(nil)
3.事务错误异常情况
类比编译时异常:就是语法或命令错误导致的无法编译或执行,这类的异常在redis事务中是不会被执行的。
114.116.230.154:6397> multi
OK
114.116.230.154:6397> set k1 v1
QUEUED
114.116.230.154:6397> set k2 v2
QUEUED
114.116.230.154:6397> set k3 v3
QUEUED
114.116.230.154:6397> getset k3
(error) ERR wrong number of arguments for 'getset' command
114.116.230.154:6397> set k4 v4
QUEUED
114.116.230.154:6397> set k5 v5
QUEUED
114.116.230.154:6397> exec
(error) EXECABORT Transaction discarded because of previous errors.
运行时异常:编译器在编译的时候无法发现错误,只有在程序运行过程中产生的错误。这类错误redis事务中是会被执行的。
114.116.230.154:6397> multi
OK
114.116.230.154:6397> set k1 v1
QUEUED
114.116.230.154:6397> incr k1
QUEUED
114.116.230.154:6397> set k2 v2
QUEUED
114.116.230.154:6397> get k2
QUEUED
114.116.230.154:6397> exec
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
4) "v2"
114.116.230.154:6397> get k1
"v1"
114.116.230.154:6397> get k2
"v2"
4.事务监控
监控:watch -- 乐观锁
#正常执成功,事务正常结束,事务期间没有发生任何错误
114.116.230.154:6397> set money 100
OK
114.116.230.154:6397> set out 0
OK
114.116.230.154:6397> watch money #监视money值(乐观锁)
OK
114.116.230.154:6397> multi
OK
114.116.230.154:6397> decrby money 20
QUEUED
114.116.230.154:6397> incrby out 20
QUEUED
114.116.230.154:6397> exec
1) (integer) 80
2) (integer) 20
多线程演示 watch
#第一个线程操作
114.116.230.154:6397> watch money #监视money
OK
114.116.230.154:6397> multi #开启事务
OK
114.116.230.154:6397> decrby money 20
QUEUED
114.116.230.154:6397> incrby out 20
QUEUED
#第二个线程操作
114.116.230.154:6397> decrby money 10 #减掉10块
(integer) 70
#第一个线程操作
114.116.230.154:6397> exec #第一个线程执行事务不成功
(nil)
#第一线程操作失败之后可以继续操作
114.116.230.154:6397> unwatch #接触监控
OK
114.116.230.154:6397> watch money #重新获取监控
OK
114.116.230.154:6397> multi #开启事务继续执行后续命令
OK
114.116.230.154:6397> decrby mone 20
QUEUED
114.116.230.154:6397> incrby out 20
QUEUED
114.116.230.154:6397> exec
1) (integer) -20
2) (integer) 40
监控总结:money值通过 watch监视,如果在一个线程事务中正在执行还未提交,那么被监视的对象被另外一个线程修改了,再提交事务的时候会失败。这种机制符合乐观锁机制,认为什么时候值都不会被改变,之后在提交事务的时候去判断值是否与之前一致,一致成功反之失败。经典cas原理。
如果是悲观锁:那么就是money这个值认为什么时候都有可能被修改,加锁之后只有当前加锁线程可以操作money。其他线程只有等待money锁释放后才能操作。
所以在上面的例子中redis的监控(watch)机制显然是乐观锁机制。
四、jedis
p23