Redis能做什么?
- 内存存储,持久化,内存中是断电即失,所以说持久化很重要(RDB,AOF)
- 效率高,可用于高速缓存
- 发布订阅系统
- 地图信息分析
- 计时器,计数器(浏览量)
- 。。。。
特性
- 多样的数据库类型
- 持久化
- 集群
- 事务
Redis安装(Linux环境)
下载Redis-7.0.2.tar.gz
移动至/opt
解压 tar -zxvf redis-7.0.2.tar.gz
基本环境安装
# 安装gcc (gcc version)
yum install gcc-c++
# 安装redis环境(server、sentinel等)
make
# 确认安装
make install
# 默认安装到了/usr/local/bin路径
# 在/usr/local/bin目录下创建config目录
mkdir config
# 移动redis.conf文件到/usr/local/bin
mv /opt/redis-7.0.2/redis.conf /usr/local/bin/config
# 修改redis.conf,使之后台运行
vim redis.conf
# daemonize改为yes
daemonize yes
启动Redis服务
# 通过指定的配置文件启动服务
redis-server config/redis.conf
# 使用redis客户端连接
redis-cli -p 6379
# 测试是否连通
ping
PONG
# 查看redis进程
ps -ef | grep redis
# 关闭redis服务
shutdown
# 退出redis-cli
exit
性能测试
redis-benchmark
# 100个并发,100000个请求(如果显示NO AUTH则需要加上 -a 密码 )
redis-benchmark -h 127.0.0.1 -p 6379 -c 100 -n 100000
测试结果
Redis基础
# 清除当前数据库
flushdb
# 清楚全部数据库内容
FLUSHALL
# 查看所有key
keys *
Redis为什么单线程还这么快
redis时将所有数据全部放在内存中的,所以说使用单线程效率是最高的,多线程(CPU上下文切换会比较耗时),对于内存来说,如果没有上下文切换效率就是最高的。多次读写都是在一个CPU上的,在内存情况下就是最佳方案。
Redis五大数据类型
# 查看所有key
keys *
# 设置name
set name zhangsan
# 获取 name
get name
# 移除当前key (1表示数据库)
move name 1
# 设置key的过期时间(10秒)
EXPIRE name 10
# 查看国企剩余时间
ttl name
# 查看当前key的类型
type name
String
# 设置值
set name zhangsan
# 获取值
get name
# 字符串追加
append name ",hello"
# 查看字符串长度
strlen name
自增自减
set age 20
# 自增1
incr age
# 自减1
decr age
# 按步长10自增
incrby age 10
# 按步长自减
decrby age 10
查看字符串范围
127.0.0.1:6379[3]> set name helloworld
OK
127.0.0.1:6379[3]> get name
"helloworld"
# getrange key satrt end
127.0.0.1:6379[3]> getrange name 0 3
"hell"
127.0.0.1:6379[3]> getrange name 0 -1
"helloworld"
替换字符串范围
127.0.0.1:6379[3]> set name abcde
OK
127.0.0.1:6379[3]> get name
"abcde"
127.0.0.1:6379[3]> setrange name 1 222
(integer) 5
127.0.0.1:6379[3]> get name
"a222e"
setex、setnx
# 设置key的值以及过期时间(30秒过期)
setex name "hello" 30
# 如果key不存在,则创建key
127.0.0.1:6379[3]> setnx name "redis"
(integer) 1
# 如果key存在,创建失败
127.0.0.1:6379[3]> setnx name "mongoDB"
(integer) 0
批量赋值mset、mget、msetnx
127.0.0.1:6379[3]> mset a1 aa b1 bb c1 cc
OK
127.0.0.1:6379[3]> keys *
1) "c1"
2) "b1"
3) "a1"
127.0.0.1:6379[3]> mget a1
1) "aa"
127.0.0.1:6379[3]> mget a1 b1 c1
1) "aa"
2) "bb"
3) "cc"
127.0.0.1:6379[3]> msetnx a1 aaaa d1 dd
(integer) 0
操作对象
127.0.0.1:6379[3]> mset user:1:name "zhangsan" user:1:age 30
OK
127.0.0.1:6379[3]> mget user:1:name user:1:age
1) "zhangsan"
2) "30"
getset
先get然后再set
# 如果不存在直接返回nil
127.0.0.1:6379[3]> getset name "zhangsan"
(nil)
127.0.0.1:6379[3]> get name
"zhangsan"
# 如果存在,则获取原来的值,并且设置新的值
127.0.0.1:6379[3]> getset name "wangwu"
"zhangsan"
127.0.0.1:6379[3]> get name
"wangwu"
List
在redis中,我们可以把list用作栈,队列,阻塞队列
将一个或多个插入列表头部
127.0.0.1:6379[3]> lpush list one
(integer) 1
127.0.0.1:6379[3]> lpush list two
(integer) 2
127.0.0.1:6379[3]> lrange list 0 -1
1) "two"
2) "one"
127.0.0.1:6379[3]> lpush list three four
(integer) 4
127.0.0.1:6379[3]> lrange list 0 -1
1) "four"
2) "three"
3) "two"
4) "one"
插值到列表尾部
127.0.0.1:6379[3]> rpush list aaa bbb
(integer) 6
127.0.0.1:6379[3]> lrange list 0 -1
1) "four"
2) "three"
3) "two"
4) "one"
5) "aaa"
6) "bbb"
从列表中移除元素( lpop、rpop )
# 移除list的第一个元素
lpop list
# 移除list的最后一个元素
rpop list
根据索引获取元素
lindex 0
返回列表长度
llen list
lrem移除列表元素
127.0.0.1:6379[3]> lrange list 0 -1
1) "four"
2) "three"
3) "two"
4) "one"
5) "aaa"
6) "bbb"
127.0.0.1:6379[3]> lpush list four
(integer) 7
127.0.0.1:6379[3]> lrem list 2 four
(integer) 2
127.0.0.1:6379[3]> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "aaa"
5) "bbb"
List元素截取(ltrim)- List会改变
127.0.0.1:6379[3]> lpush list a b c d
(integer) 4
127.0.0.1:6379[3]> lrange list 0 -1
1) "d"
2) "c"
3) "b"
4) "a"
127.0.0.1:6379[3]> ltrim list 1 2
OK
127.0.0.1:6379[3]> lrange list 0 -1
1) "c"
2) "b"
rpoplpush
移除列表最后一个元素,将它移动到新的列表中
127.0.0.1:6379[3]> lpush hello hello2 hello3
(integer) 2
127.0.0.1:6379[3]> flushall
OK
127.0.0.1:6379[3]> lpush list hello hello1 hello2
(integer) 3
127.0.0.1:6379[3]> lrange list 0 -1
1) "hello2"
2) "hello1"
3) "hello"
127.0.0.1:6379[3]> lpoplpush list newList
(error) ERR unknown command 'lpoplpush', with args beginning with: 'list' 'newList'
127.0.0.1:6379[3]> rpoplpush list newList
"hello"
127.0.0.1:6379[3]> lrange list 0 -1
1) "hello2"
2) "hello1"
127.0.0.1:6379[3]> lrange newList 0 -1
1) "hello"
lset
将列表中指定下标的值替换为新值
127.0.0.1:6379[3]> lpush list hello1 hello2
(integer) 2
127.0.0.1:6379[3]> lrange list 0 -1
1) "hello2"
2) "hello1"
127.0.0.1:6379[3]> lset list 1 world
OK
127.0.0.1:6379[3]> lrange list 0 -1
1) "hello2"
2) "world"
linsert
将某个具体的值插入到某个元素的前面或者后面
127.0.0.1:6379[3]> lpush list hello hello1 hello2
(integer) 3
127.0.0.1:6379[3]> lrange list 0 -1
1) "hello2"
2) "hello1"
3) "hello"
127.0.0.1:6379[3]> linsert list before "hello1" "world"
(integer) 4
127.0.0.1:6379[3]> lrange list 0 -1
1) "hello2"
2) "world"
3) "hello1"
4) "hello"
127.0.0.1:6379[3]> linsert list after "hello1" "byeworld"
(integer) 5
127.0.0.1:6379[3]> lrange list 0 -1
1) "hello2"
2) "world"
3) "hello1"
4) "byeworld"
5) "hello"
Set
无序不重复
sadd、smembers、sismember
# 添加元素
127.0.0.1:6379[3]> sadd myset "hello" "hello1" "hello2"
(integer) 3
# 查看set中所有元素
127.0.0.1:6379[3]> SMEMBERs myset
1) "hello1"
2) "hello2"
3) "hello"
# 查看set中是否存在某一个元素
127.0.0.1:6379[3]> sismember myset hello
(integer) 1
127.0.0.1:6379[3]> sismember myset helloa
(integer) 0
scard查看set元素数量
127.0.0.1:6379[3]> scard myset
(integer) 3
srem移除某一个元素
127.0.0.1:6379[3]> srem myset hello
(integer) 1
127.0.0.1:6379[3]> smembers myset
1) "hello1"
2) "hello2"
srandmember随机获取某一个元素
# 随机抽出一个元素
127.0.0.1:6379[3]> srandmember myset
"hello1"
# 随机抽出两个元素
127.0.0.1:6379[3]> srandmember myset 2
1) "hello1"
2) "hello4"
spop、smove
# 随机移除2个元素
spop myset 2
# 将一个指定的值移动到另一个set集合
127.0.0.1:6379[3]> smembers myset
1) "hello3"
2) "hello2"
3) "hello"
4) "hello4"
127.0.0.1:6379[3]> smove myset myset2 "hello"
(integer) 1
127.0.0.1:6379[3]> smembers myset
1) "hello3"
2) "hello2"
3) "hello4"
127.0.0.1:6379[3]> smembers myset2
1) "hello"
差集SDIFF、交集SINTER、并集SUNION
127.0.0.1:6379[3]> sadd myset a b c d e
(integer) 5
127.0.0.1:6379[3]> smembers myset
1) "c"
2) "a"
3) "d"
4) "b"
5) "e"
127.0.0.1:6379[3]> sadd myset2 d e f g
(integer) 4
127.0.0.1:6379[3]> smembers myset2
1) "g"
2) "f"
3) "d"
4) "e"
127.0.0.1:6379[3]> sdiff myset myset2
1) "a"
2) "c"
3) "b"
127.0.0.1:6379[3]> sinter myset myset2
1) "d"
2) "e"
127.0.0.1:6379[3]> sunion myset myset2
1) "f"
2) "b"
3) "g"
4) "a"
5) "c"
6) "d"
7) "e"
Hash
map集合,存储key-value
# set一个具体的key-value
127.0.0.1:6379[3]> hset myhash name "zhangsan" age "20"
(integer) 2
# 获取一个字段值
127.0.0.1:6379[3]> hget myhash name
"zhangsan"
127.0.0.1:6379[3]> hmget myhash name age
1) "zhangsan"
2) "20"
127.0.0.1:6379[3]> hmset myhash sex "male"
OK
127.0.0.1:6379[3]> hmget myhash name age
1) "zhangsan"
2) "20"
127.0.0.1:6379[3]> hmget myhash name age sex
1) "zhangsan"
2) "20"
3) "male"
# 获取全部数据
127.0.0.1:6379[3]> hgetall myhash
1) "name"
2) "zhangsan"
3) "age"
4) "20"
5) "sex"
6) "male"
删除hash元素
127.0.0.1:6379[3]> hdel myhash sex
(integer) 1
127.0.0.1:6379[3]> hgetall myhash
1) "name"
2) "zhangsan"
3) "age"
获取hash的元素长度
127.0.0.1:6379[3]> hlen myhash
(integer) 2
hexists、hkeys、hvals
# 判断hash中是否存在该字段
127.0.0.1:6379[3]> hexists myhash name
(integer) 1
# 获取所有的key
127.0.0.1:6379[3]> hkeys myhash
1) "name"
2) "age"
# 获取所有的value
127.0.0.1:6379[3]> hvals myhash
1) "zhangsan"
2) "20"
hincrby、hdecrby、hsetnx
# 自增1
hincrby myhash age 1
# 自减1
hincrby myhash age -1
# 判断字段是否中存在
hsetnx myhash name "lisi"
Zset(有序集合)
通过score字段进行排序
1.获取倒序排名:
ZREVRANGEBYSCORE key min max offset count
2.获取某个用户的score:
ZSCORE key member
3.获取参与排名的总用户数(本期参与人数):
ZCARD key
4.获取某个用户的当前排名(倒序排序,从大到小的排名):
ZREVRANK key member
127.0.0.1:6379[3]> zadd salary 2500 xiaoming
(integer) 1
127.0.0.1:6379[3]> zadd salary 5000 zhangsan
(integer) 1
127.0.0.1:6379[3]> zadd salary 200 wangwu
(integer) 1
# zrangebyscore key min max
127.0.0.1:6379[3]> zrangebyscore salary -inf +inf
1) "wangwu"
2) "xiaoming"
3) "zhangsan"
127.0.0.1:6379[3]> zrangebyscore salary 0 10000
1) "wangwu"
2) "xiaoming"
3) "zhangsan"
# 显示全部用户,并且带上成绩
127.0.0.1:6379[3]> zrangebyscore salary -inf +inf withscores
1) "wangwu"
2) "200"
3) "xiaoming"
4) "2500"
5) "zhangsan"
6) "5000"
# 根据score进行倒叙排列
127.0.0.1:6379[3]> zrevrangebyscore salary 10000 0
1) "zhangsan"
2) "xiaoming"
3) "wangwu"
# 查询所有元素
127.0.0.1:6379[3]> zrange salary 0 -1
1) "wangwu"
2) "xiaoming"
3) "zhangsan"
# 查询集合元素总数量
127.0.0.1:6379[3]> zcard salary
(integer) 3
# 删除元素
127.0.0.1:6379[3]> zrem salary wangwu
(integer) 1
127.0.0.1:6379[3]> zrange salary 0 -1
1) "xiaoming"
2) "zhangsan"
获取指定区间的成员数量
127.0.0.1:6379[3]> zadd myzset 1 hello 2 hello2 3 hello3
(integer) 3
127.0.0.1:6379[3]> zrange myzset 0 -1
1) "hello"
2) "hello2"
3) "hello3"
# 获取指定区间的成员数量
127.0.0.1:6379[3]> zcount myzset 1 3
(integer) 3
127.0.0.1:6379[3]> zcount myzset 1 2
(integer) 2
Redis三种特殊数据类型
geospatial地理位置
geoadd添加指定位置经纬度
127.0.0.1:6379[3]> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379[3]> geoadd china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379[3]> geoadd china:city 106.50 29.53 chongqing
(integer) 1
127.0.0.1:6379[3]> geoadd china:city 114.05 22.52 shenzhen
(integer) 1
geopos获取指定位置的经纬度
127.0.0.1:6379[3]> geopos china:city beijing
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
geodis获取两地之间距离
# 单位:米
127.0.0.1:6379[3]> geodist china:city beijing shanghai
"1067378.7564"
# 单位:千米
127.0.0.1:6379[3]> geodist china:city beijing shanghai km
"1067.3788"
georadius以给定的经纬度为中心,找出某一半径内的元素
# 以110,30这个经纬度为中心,查找方圆1000km范围内的城市
127.0.0.1:6379[3]> georadius china:city 110 30 1000 km
1) "chongqing"
2) "shenzhen"
127.0.0.1:6379[3]> georadius china:city 110 30 500 km
1) "chongqing"
127.0.0.1:6379[3]> georadius china:city 110 30 km withdist
(error) ERR need numeric radius
127.0.0.1:6379[3]> georadius china:city 110 30 500 km withdist
1) 1) "chongqing"
2) "341.9374"
# withcoord显示经纬度
127.0.0.1:6379[3]> georadius china:city 110 30 500 km withdist withcoord count 3
1) 1) "chongqing"
2) "341.9374" # 距离
3) 1) "106.49999767541885376"
2) "29.52999957900659211"
georadiusbymember
# 查找距离北京2000km范围内的成员
127.0.0.1:6379[3]> georadiusbymember china:city beijing 2000 km
1) "chongqing"
2) "shenzhen"
3) "shanghai"
4) "beijing"
geohash
返回一个或多个位置的geohash
将二维的经纬度转换成一维的字符串,如果两个字符串越接近,则距离越近
127.0.0.1:6379[3]> geohash china:city beijing shanghai
1) "wx4fbxxfke0"
2) "wtw3sj5zbj0"
zrange、zrem查看地图中所有成员&删除成员
GEO的底层实现远离其实就是Zset,所以可以使用Zset命令来操作GEO
127.0.0.1:6379[3]> zrange china:city 0 -1
1) "chongqing"
2) "shenzhen"
3) "shanghai"
4) "beijing"
127.0.0.1:6379[3]> zrem china:city beijing
(integer) 1
127.0.0.1:6379[3]> zrange china:city 0 -1
1) "chongqing"
2) "shenzhen"
3) "shanghai"
hyperloglog
基数统计{1,2,3,2}基数结果就是{1,2,3}数量为3
# 创建第一组元素mykey
127.0.0.1:6379[3]> PFadd mykey 1 2 3 4
(integer) 1
# 创建第二组元素mykey2
127.0.0.1:6379[3]> pfadd mykey2 3 4 5 6 7
(integer) 1
# 统计mykey元素的基数数量
127.0.0.1:6379[3]> pfcount mykey
(integer) 4
127.0.0.1:6379[3]> pfcount mykey2
(integer) 5
# 合并两组 mykey mykey2 -> mykey3
127.0.0.1:6379[3]> pfmerge mykey3 mykey mykey2
OK
127.0.0.1:6379[3]> pfcount mykey3
(integer) 7
bitmap
位存储(0,1存储)。Bitmap位图,数据结构,都是操作二进制来进行记录,就只有0,1两个状态
统计用户信息,活跃,不活跃,登录,未登录,一周一月一年打卡情况,都可以使用Bitmaps
127.0.0.1:6379[3]> setbit sign 0 1
(integer) 0
127.0.0.1:6379[3]> setbit sign 1 0
(integer) 0
127.0.0.1:6379[3]> setbit sign 2 1
(integer) 0
127.0.0.1:6379[3]> setbit sign 3 0
(integer) 0
127.0.0.1:6379[3]> setbit sign 4 1
(integer) 0
127.0.0.1:6379[3]> setbit sign 5 0
(integer) 0
127.0.0.1:6379[3]> setbit sign 6 1
(integer) 0
查看某一天是否打卡
127.0.0.1:6379[3]> getbit sign 3
(integer) 0
127.0.0.1:6379[3]> getbit sign 4
(integer) 1
统计打卡天数
127.0.0.1:6379[3]> bitcount sign
(integer) 4
事务
redis的事务
- 开启事务(multi)
- 命令入队
- 执行事务(exec)
执行事务
127.0.0.1:6379[3]> multi #开启事务
OK
127.0.0.1:6379[3](TX)> set k1 v1
QUEUED
127.0.0.1:6379[3](TX)> set k2 v2
QUEUED
127.0.0.1:6379[3](TX)> get k1
QUEUED
127.0.0.1:6379[3](TX)> set k3 v3
QUEUED
127.0.0.1:6379[3](TX)> exec #执行事务
1) OK
2) OK
3) "v1"
4) OK
放弃事务
127.0.0.1:6379[3]> multi
OK
127.0.0.1:6379[3](TX)> set k1 v1
QUEUED
127.0.0.1:6379[3](TX)> set k2 v2
QUEUED
127.0.0.1:6379[3](TX)> DISCARD # 放弃事务
OK
127.0.0.1:6379[3]> get k1
(nil)
- 编译时异常,事务中所有命令均不执行
运行时异常, 错误命令抛出异常,正确命令正常执行。
127.0.0.1:6379[3]> set k1 v1 OK 127.0.0.1:6379[3]> multi OK 127.0.0.1:6379[3](TX)> incr k1 QUEUED 127.0.0.1:6379[3](TX)> set ke v2 QUEUED 127.0.0.1:6379[3](TX)> set k3 v3 QUEUED 127.0.0.1:6379[3](TX)> get k3 QUEUED 127.0.0.1:6379[3](TX)> exec # incr执行失败 1) (error) ERR value is not an integer or out of range 2) OK 3) OK 4) "v3"
悲观锁
很悲观,认为什么时候都会出问题,无论做什么都会被加锁
Watch - 乐观锁
认为什么时候都不会出问题,所以不上锁,更新数据时去判断一下,在此期间是否有人修改过这个数据
乐观锁单线程
127.0.0.1:6379[3]> set money 100
OK
127.0.0.1:6379[3]> set out 0
OK
127.0.0.1:6379[3]> watch money # 监视money对象
OK
127.0.0.1:6379[3]> multi # 事务正常结束,数据期间没有发生变动,这个时候可以正常执行
OK
127.0.0.1:6379[3](TX)> DECRBY money 20
QUEUED
127.0.0.1:6379[3](TX)> INCRBY out 20
QUEUED
127.0.0.1:6379[3](TX)> exec
1) (integer) 80
2) (integer) 20
乐观锁多线程
测试多线程修改值,使用watch可以当作乐观锁操作
127.0.0.1:6379[3]> watch money # 监视money
OK
127.0.0.1:6379[3]> multi
OK
127.0.0.1:6379[3](TX)> DECRBY money 10
QUEUED
127.0.0.1:6379[3](TX)> INCRBY out 10
QUEUED
127.0.0.1:6379[3](TX)> exec # 执行之前,另外一个线程修改了money值,这个时候就会导致事务执行失败
nil
127.0.0.1:6379[3]> unwatch # 如果发现事务执行失败,就先解锁
OK
127.0.0.1:6379[3]> watch money # 获取最新的值,再次监视,类似select version
OK
127.0.0.1:6379[3]> multi
OK
127.0.0.1:6379[3](TX)> decrby money 1
QUEUED
127.0.0.1:6379[3](TX)> incrby out 1
QUEUED
127.0.0.1:6379[3](TX)> exec # 对比监视的值是否发生了变化,如果没有发生变化则可以执行成功,如果变化了执行失败
1) (integer) 59
2) (integer) 41
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。