1、Redis启动
进入redis安装目录,首先双击服务器,然后双击客户端,在客户端进行数据的操作。
2、五大基本数据类型
String类型
-
set和get命令
127.0.0.1:6379> set key1 v1 OK 127.0.0.1:6379> get key1 "v1"
-
keys命令,查看所有key
127.0.0.1:6379> keys * 1) "key1"
-
del命令,删除key
127.0.0.1:6379> keys * 1) "key1" 127.0.0.1:6379> del key1 (integer) 1 127.0.0.1:6379> keys * (empty list or set)
-
exist命令,是否存在某键
127.0.0.1:6379> exists key1 (integer) 1
-
append命令,追加字符串内容,如果要追加的key不存在,则相当于set命令
127.0.0.1:6379> append key1 hello (integer) 7 127.0.0.1:6379> get key1 "v1hello"
-
strlen命令,获取字符串长度
127.0.0.1:6379> strlen key1 (integer) 7
-
incr命令,自增1
127.0.0.1:6379> set views 0 OK 127.0.0.1:6379> get views "0" 127.0.0.1:6379> incr views (integer) 1 127.0.0.1:6379> incr views (integer) 2
-
decr命令,自减1
127.0.0.1:6379> decr views (integer) 1 127.0.0.1:6379> decr views (integer) 0
-
incrby命令,一次性增加n
(integer) 0 127.0.0.1:6379> incrby views 10 (integer) 10
-
decrby命令,一次性减少n
(integer) 10 127.0.0.1:6379> decrby views 10 (integer) 0
-
flushdb命令,清空数据库
127.0.0.1:6379> flushdb OK 127.0.0.1:6379> keys * (empty list or set)
-
getrange命令,截取字符串
127.0.0.1:6379> get key1 "hello,kuangshen" 127.0.0.1:6379> getrange key1 0 3 "hell" 127.0.0.1:6379> getrange key1 0 -1 "hello,kuangshen"
-
setrange命令,替换指定位置开始的字符串
127.0.0.1:6379> set key2 abcdefg OK 127.0.0.1:6379> get key2 "abcdefg" 127.0.0.1:6379> setrange key2 1 xx (integer) 7 127.0.0.1:6379> get key2 "axxdefg"
-
setex(set with expire),设置过期时间
127.0.0.1:6379> setex key3 30 "hello" #设置key3 30秒过期 OK 127.0.0.1:6379> ttl key3 (integer) 10 127.0.0.1:6379> get key3 (nil)
-
setnx(set if not exists),不存在再设置,存在,则设置不成功,在分布式锁中会常常使用
127.0.0.1:6379> setnx mykey "redis" (integer) 1 127.0.0.1:6379> setnx mykey "mongodb" (integer) 0 127.0.0.1:6379> get mykey "redis"
-
mset命令,同时设置多个key
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> 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"
-
mget命令,同时获取多个key的值
127.0.0.1:6379> mget k1 k2 k3 1) "v1" 2) "v2" 3) "v3"
-
msetnx命令,不存在则创建,msetnx是一个原子性的操作,要么一起成功,要么一起失败
127.0.0.1:6379> msetnx k1 vv k4 v4 #mset k1 v1不成功,因为k1已存在,mset k4 v4是成功的,因为k4不存在,但由于msetnx是原子性操作,要么都成功,要么都不成功,所以最终结果是失败的 (integer) 0 127.0.0.1:6379> keys * 1) "k3" 2) "k1" 3) "k2"
-
getset命令,先get再set,若key不存在,则返回nil,然后再设置key;若key存在,则返回值,并设置新值。CAS比较并设置
127.0.0.1:6379> getset db redis (nil) 127.0.0.1:6379> get db "redis" 127.0.0.1:6379> getset db mongodb "redis" 127.0.0.1:6379> get db "mongodb"
-
对象
127.0.0.1:6379> set user:1 {name:zhangsan,age:3} OK
String类型的使用场景:value除了是字符串还可以是数字
- 计数器
- 统计多单位的数量
- 粉丝数
- 对象缓存存储
List
实际上是一个双向链表,可用作栈、消息队列。
所有的list命令都是以l开头的
-
lpush命令,将一个或多个值插入到链表头部
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 1) "three" 2) "two"
-
rpush命令,将一个或多个值插入到链表尾部
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"
-
lpop命令,移除链表最左边元素,rpop命令,移除链表最右边元素
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"
-
lindex命令,通过 下标获取list中的某一个元素
127.0.0.1:6379> lindex list 0 "two"
-
llen命令,返回列表的长度
127.0.0.1:6379> llen list (integer) 2
-
lrem命令,移除链表中指定的值
127.0.0.1:6379> lrange list 0 -1 1) "three" 2) "three" 3) "two" 4) "one" 127.0.0.1:6379> lrem list 1 three #删除list中的1个three (integer) 1 127.0.0.1:6379> lrange list 0 -1 1) "three" 2) "two" 3) "one"
-
ltrim命令,截取列表中的片段
127.0.0.1:6379> lrange mylist 0 -1 1) "hello" 2) "hello1" 3) "hello2" 4) "hello3" 127.0.0.1:6379> ltrim mylist 1 2 OK 127.0.0.1:6379> lrange mylist 0 -1 1) "hello1" 2) "hello2"
-
rpoplpush命令,将一个列表最末尾元素移动到另一个链表的头部
127.0.0.1:6379> lrange mylist 0 -1 1) "hello" 2) "hello1" 3) "hello2" 127.0.0.1:6379> rpoplpush mylist myotherlist "hello2" 127.0.0.1:6379> lrange mylist 0 -1 1) "hello" 2) "hello1" 127.0.0.1:6379> lrange myotherlist 0 -1 1) "hello2"
-
lset命令,设置列表中某个元素的值,前提是该元素存在,否则报错
127.0.0.1:6379> lrange list 0 -1 1) "value1" 127.0.0.1:6379> lset list 0 item OK 127.0.0.1:6379> lrange list 0 0 1) "item" 127.0.0.1:6379> lset list 1 ele (error) ERR index out of range
-
linsert命令,在list某个元素前后插入新的元素
127.0.0.1:6379> lrange mylist 0 -1 1) "hello" 2) "world" 127.0.0.1:6379> linsert mylist before world other (integer) 3 127.0.0.1:6379> lrange mylist 0 -1 1) "hello" 2) "other" 3) "world" 127.0.0.1:6379> linsert mylist after world new (integer) 4 127.0.0.1:6379> lrange mylist 0 -1 1) "hello" 2) "other" 3) "world" 4) "new"
Set
set(集合)中的值不能重复,是无序的
所有的set命令都是以s开头的
-
sadd命令,向集合中添加元素
127.0.0.1:6379> sadd myset "hello" (integer) 1 127.0.0.1:6379> sadd myset "kuangshen" (integer) 1
-
smembers命令,查看set中所有的元素值
127.0.0.1:6379> smembers myset 1) "hello" 2) "kuangshen"
-
sismember命令,判断集合中是否存在某元素
127.0.0.1:6379> sismember myset hello (integer) 1 127.0.0.1:6379> sismember myset world (integer) 0
-
scard命令,获取set中元素个数
127.0.0.1:6379> scard myset (integer) 2
-
srem命令,移除set中的某个元素
127.0.0.1:6379> smembers myset 1) "hello" 2) "kuangshen" 127.0.0.1:6379> srem myset "hello" (integer) 1 127.0.0.1:6379> smembers myset 1) "kuangshen"
-
srandmember命令,从集合中随机选择一个或多个元素
127.0.0.1:6379> smembers myset 1) "new" 2) "world" 3) "kuangshen" 127.0.0.1:6379> srandmember myset "kuangshen" 127.0.0.1:6379> srandmember myset "new" 127.0.0.1:6379> srandmember myset "new" 127.0.0.1:6379> srandmember myset 2 #随机选取2个元素 1) "new" 2) "world"
-
spop命令,从集合中随机删除一些元素
127.0.0.1:6379> smembers myset 1) "new" 2) "world" 3) "kuangshen" 127.0.0.1:6379> spop myset "new" 127.0.0.1:6379> smembers myset 1) "world" 2) "kuangshen"
-
smove命令,将一个指定的值移动到另外一个set集合中
127.0.0.1:6379> smembers myset 1) "world" 2) "kuangshen" 127.0.0.1:6379> smembers myset2 1) "set2" 127.0.0.1:6379> smove myset myset2 "world" (integer) 1 127.0.0.1:6379> smembers myset 1) "kuangshen" 127.0.0.1:6379> smembers myset2 1) "world" 2) "set2"
-
交集sinter、并集sunion、差集sdiff
微博共同关注,交集
127.0.0.1:6379> smembers key1 1) "c" 2) "b" 3) "a" 127.0.0.1:6379> smembers key2 1) "d" 2) "e" 3) "c" 127.0.0.1:6379> sdiff key1 key2 1) "b" 2) "a" 127.0.0.1:6379> sinter key1 key2 1) "c" 127.0.0.1:6379> sunion key1 key2 1) "b" 2) "c" 3) "a" 4) "d" 5) "e"
Hash
所有的命令以h开头
存放键值对
-
hset和hget
127.0.0.1:6379> hset myhash "field1" "kuangshen" (integer) 1 127.0.0.1:6379> hget myhash field1 "kuangshen"
-
hdel命令,删除元素
127.0.0.1:6379> hdel myhash filed2 (integer) 1
-
hlen命令,获取hash表的字段数量
127.0.0.1:6379> hlen myhash (integer) 1
-
exists命令,判断hash表中某个字段是否存在
127.0.0.1:6379> exists myhash field1 (integer) 1
-
hkeys命令,只获取所有的字段
(integer) 127.0.0.1:6379> hkeys myhash 1) "field1"
-
hvals命令,只获取所有的字段值
127.0.0.1:6379> hvals myhash 1) "kuangshen"
-
hincrby命令,自增
127.0.0.1:6379> hset myhash field3 5 (integer) 1 127.0.0.1:6379> hincrby myhash field3 1 (integer) 6
-
hsetnx命令,如果不存在,则设置成功
127.0.0.1:6379> hsetnx myhash field4 world (integer) 1
Zset
有序集合
-
zadd命令,添加元素
127.0.0.1:6379> zadd myset 1 one (integer) 1 127.0.0.1:6379> zadd myset 2 two 3 three (integer) 2 127.0.0.1:6379> zrange myset 0 -1 1) "one" 2) "two" 3) "three"
-
zrangebyscrore命令,返回分数在min和max之间的所有元素,元素从低分到高分排序。
127.0.0.1:6379> zadd salary 2500 xiaohong (integer) 1 127.0.0.1:6379> zadd salary 5000 zhangsan (integer) 1 127.0.0.1:6379> zadd salary 500 kuangshen (integer) 1 127.0.0.1:6379> zrangebyscore salary -inf +inf #inf表示无穷 1) "kuangshen" 2) "xiaohong" 3) "zhangsan" 127.0.0.1:6379> zrangebyscore salary 0 3000 1) "kuangshen" 2) "xiaohong"
-
zrevrange命令,从大到小进行排序
127.0.0.1:6379> zrevrange salary 0 -1 1) "zhangsan" 2) "xiaohong" 3) "kuangshen"
-
zcount命令,获取分数在区间内的元素个数
127.0.0.1:6379> zcount salary 500 3000 (integer) 2
-
zcard命令,获取有序集合中元素个数
127.0.0.1:6379> zcard salary (integer) 3
-
zrem命令,移除元素
127.0.0.1:6379> zrem salary xiaohong (integer) 1 127.0.0.1:6379> zrange salary 0 -1 1) "kuangshen" 2) "zhangsan"
3、三种特殊数据类型
geospatial地理位置
-
geoadd命令,添加地理位置(南北两极无法添加),我们一般会下载城市数据,直接一次性导入!
127.0.0.1:6379> geoadd china:city 116.40 39.9 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 chongqin 114.05 22.52 shenzhen 120.16 30.24 guangzhou 108.96 34.26 xian (integer) 4
-
geopos命令,获取地理位置(坐标)
127.0.0.1:6379> geopos china:city beijing 1) 1) "116.39999896287918" 2) "39.900000091670925"
-
geodist命令,两个城市之间的距离
127.0.0.1:6379> geodist china:city beijing shanghai "1067378.7564" 127.0.0.1:6379> geodist china:city beijing shanghai km #单位设置成km "1067.3788"
-
georadius命令,以给定的经纬度为中心,找出某一半径内的元素
127.0.0.1:6379> georadius china:city 110 30 1000 km #找出以110,30为中心,半径为1000km的所有城市 1) "chongqin" 2) "xian" 3) "shenzhen" 4) "guangzhou" 127.0.0.1:6379> georadius china:city 110 30 500 km withcoord #withcoord打印出城市经纬度 1) 1) "chongqin" 2) 1) "106.49999767541885" 2) "29.529999579006592" 2) 1) "xian" 2) 1) "108.96000176668167" 2) "34.2599996441893" 127.0.0.1:6379> georadius china:city 110 30 500 km withdist #withdist打印出城市距离当前位置的距离 1) 1) "chongqin" 2) "341.9374" 2) 1) "xian" 2) "483.8340" 127.0.0.1:6379> georadius china:city 110 30 500 km count 1 #count限制个数 1) "chongqin"
-
georadiusbymember命令,以给定的城市为中心,找出某一半径内的元素
127.0.0.1:6379> georadiusbymember china:city beijing 1000 km 1) "beijing" 2) "xian"
-
geohash命令,将二维经纬度转化为一维的11位字符串
127.0.0.1:6379> geohash china:city beijing 1) "wx4fbxxfke0"
-
GEO底层的实现原理其实就是Zset,我们可以使用Zset命令来操作geo
127.0.0.1:6379> zrange china:city 0 -1 1) "chongqin" 2) "xian" 3) "shenzhen" 4) "guangzhou" 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) "chongqin" 2) "xian" 3) "shenzhen" 4) "guangzhou" 5) "shanghai"
hyperloglog
基数统计的算法,0.81%错误率!
127.0.0.1:6379> pfadd mykey a b c d
(integer) 1
127.0.0.1:6379> pfcount mykey
(integer) 4
127.0.0.1:6379> pfadd mykey2 c c d e f
(integer) 1
127.0.0.1:6379> pfcount mykey2 #只有一个c添加成功
(integer) 4
127.0.0.1:6379> pfmerge mykey3 mykey mykey2 #并集
OK
127.0.0.1:6379> pfcount mykey3
(integer) 6
bitmaps
位存储
127.0.0.1:6379> setbit sign 0 1 #位置0设置成1
(integer) 0
127.0.0.1:6379> setbit sign 1 0 #位置1设置成0
(integer) 0
127.0.0.1:6379> getbit sign 0 #查看位置0的值
(integer) 1
127.0.0.1:6379> bitcount sign #统计1的个数
(integer) 1
4、事务
Redis事务本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行!
一次性、顺序性、排他性执行一系列的命令!
Redis事务没有隔离级别的概念!
所有的命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行!
Redis单条命令是保证原子性的,但是事务不保证原子性!
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> set k3 v3
QUEUED
127.0.0.1:6379> exec #执行事务
1) OK
2) OK
3) "v2"
4) 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> set k4 v4
QUEUED
127.0.0.1:6379> discard #取消事务
OK
127.0.0.1:6379> get k4 #事务队列中命令都不会被执行
(nil)
编译型异常(代码有问题!),事务中所有的命令都不会被执行
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> setget k2 v2
(error) ERR unknown command 'setget'
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
运行时异常,如果事务队列中存在语法性,那么执行命令的时候,其他命令是可以正常执行的,错误命令抛出异常!
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"
Redis实现乐观锁
-
乐观锁
- 很乐观,认为什么时候都不会出问题,所以不会上锁,更新数据的时候去判断一下,在次期间是否有人修改过这个数据。
- 获取version
- 更新的时候比较version
-
watch实现乐观锁
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 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)
事务执行失败之后,watch会自动解锁,无需手动unwatch解锁