# redis 测试
# 测试: 100 个并发连接 100000 请求 | |
redis-benchmark -h localhost -p 6379 -c 100 -n 100000 |
# 五大数据类型
redis 是单线程的,可以用作数据库,缓存和消息中间件 MQ
官方表示:redis 是基于内存操作的,CPU 不是 redis 性能瓶颈,redis 的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,就使用单线程了!
为什么默认端口是 6379:粉丝效应
redis 默认有 16 个数据库,默认使用的是第 0 个数据库
可以使用 select 进行切换
官方文档
# Redis-key
root[/usr/local/bin]: redis-cli -p 6379 | |
127.0.0.1:6379> select 3 | |
OK | |
127.0.0.1:6379[3]> DBSIZE #查看 DB 大小 | |
(integer) 0 | |
127.0.0.1:6379[3]> keys * #查看所有的 key |
127.0.0.1:6379[3]>flushdb #清除当前数据库 |
127.0.0.1:6379[3]>flushall #清空所有数据库 |
127.0.0.1:6379[3]>EXISTS age #判断 age 键是否存在,有为 1,否则为 0 | |
(integer) 1 | |
127.0.0.1:6379> exists age1 | |
(integer) 0 |
127.0.0.1:6379> move age 1 #移除 age , 1 代表当前数据库 |
127.0.0.1:6379>expire age 10 # 10 秒后过期 | |
127.0.0.1:6379>ttl age #还剩多少时间过期 |
127.0.0.1:6379>type age #查看当前数据类型 | |
string |
# String(字符串)
127.0.0.1:6379> set key1 v1 | |
OK | |
127.0.0.1:6379> append key1 hell # 存在则追加,不存在则新建 | |
(integer) 6 | |
127.0.0.1:6379> get key1 | |
"v1hell" | |
127.0.0.1:6379> strlen key1 # 获取字符串的长度 | |
(integer) 6 |
127.0.0.1:6379> set views 0 | |
OK | |
127.0.0.1:6379> incr views # 自动加 1 | |
(integer) 1 | |
127.0.0.1:6379> decr views # 自动减 1 |
127.0.0.1:6379> incrby views 10 # 增加指定数值 同理 decrby | |
(integer) 11 |
# 字符串范围 range | |
127.0.0.1:6379> set key1 "hello,kuangshen" | |
OK | |
127.0.0.1:6379> GETRANGE key1 0 3 # 截取 [0,3] | |
"hell" | |
127.0.0.1:6379> GETRANGE key1 0 -1 # 取所有 | |
"hello,kuangshen" | |
# 替换 | |
127.0.0.1:6379> SETRANGE key2 1 xx # 从 1 开始替换字符串 | |
(integer) 7 | |
127.0.0.1:6379> GETRANGE key2 0 -1 | |
"axxdefg" | |
################################################################################## | |
# setex (set with expire) # 设置过期时间 | |
# setnx (set if not exist) # 不存在设置 (在分布式锁中会常用) | |
127.0.0.1:6379> setex key3 30 "sds" # key3 30 秒存在时间 | |
OK | |
127.0.0.1:6379> ttl key3 | |
(integer) 26 | |
127.0.0.1:6379> setnx mykey "hello" # 设置成功返回 1 否则返回 0 如果不存在则创建,如果存在就创建失败 | |
(integer) 1 | |
127.0.0.1:6379> setnx mykey "Mongo" | |
(integer) 0 | |
127.0.0.1:6379> get mykey | |
"hello" | |
################################################################################## | |
# 同时设置(获取)多个键值对 | |
mset | |
mget | |
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" | |
同样的 msetnx msetex 同理(原子性操作,要么一起成功要么一起失败) | |
# 关于对象 | |
set user:1 {name:zhangsan, age:3} # 这是一个 user:1 对象 值为 json 字符来保存对象! | |
# 这里 key 的设计: user:{id}:{filed} | |
# 冒号:代表一个层级,方便查看和管理 | |
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 mongodb # 如果存在则返回当前值,并修改 | |
"redis" | |
127.0.0.1:6379> get db | |
"mongodb" | |
127.0.0.1:6379> |
数据结构是想通的!
String 类型使用场景:value 除了是字符串还可以是数字
- 计数器
- 统计多单位数量
- 粉丝数
- 对象缓存存储
# List(列表)
在 redis 的 list 中,可以做成栈,队列,阻塞队列!redis 不区分大小写命令
list 内部可以存在重复值
注:只有 push 和 pop 才分左右,其余前置 l 均为 list 的意思
################################################################################## | |
127.0.0.1:6379> LPUSH list one # lpush 中的 l 指的是 left,从左边插入,同理有 rpush,相当于压栈 | |
(integer) 1 | |
127.0.0.1:6379> LPUSH list teo | |
(integer) 2 | |
127.0.0.1:6379> LPUSH list three | |
(integer) 3 | |
127.0.0.1:6379> LRANGE list 0 -1 # 查所有, 都是从零开始计算的 | |
1) "three" | |
2) "teo" | |
3) "one" | |
127.0.0.1:6379> LRANGE list 0 1 | |
1) "three" | |
2) "teo" | |
################################################################################## | |
移除命令,同理有lpop和rpop | |
################################################################################## | |
获取下标值index,也是分lindex和rindex | |
127.0.0.1:6379> lrange list 0 -1 | |
1) "three" | |
2) "teo" | |
3) "one" | |
4) "four" | |
127.0.0.1:6379> lindex list 0 | |
"three" | |
################################################################################## | |
获取list长度,llen | |
127.0.0.1:6379> llen list | |
(integer) 4 | |
################################################################################## | |
移除指定的值 | |
lrem (list remove) | |
因为list内部可以存在重复值,所以出现lrem key count value | |
127.0.0.1:6379[1]> lrange list 0 -1 | |
1) "three" | |
2) "three" | |
3) "two" | |
4) "one" | |
127.0.0.1:6379[1]> lrem list 1 one | |
(integer) 1 | |
127.0.0.1:6379[1]> lrange list 0 -1 | |
1) "three" | |
2) "three" | |
3) "two" | |
################################################################################## | |
只保留一部分值 | |
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" | |
127.0.0.1:6379> | |
################################################################################## | |
rpoplpush # 移除列表最后一个元素,并添加到新的列表中 | |
rpoplpush source destination | |
127.0.0.1:6379> lrange mylist 0 -1 | |
1) "hello1" | |
2) "hello2" | |
3) "hello" | |
127.0.0.1:6379> rpoplpush mylist myotherlist | |
"hello" | |
127.0.0.1:6379> lrange myotherlist 0 -1 | |
1) "hello" | |
127.0.0.1:6379> lrange mylist 0 -1 | |
1) "hello1" | |
2) "hello2" | |
127.0.0.1:6379> | |
################################################################################## | |
lset 更新元素(前提:该元素存在,否则会报错) | |
127.0.0.1:6379> lpush list value1 | |
(integer) 1 | |
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 -1 | |
1) "item" | |
################################################################################## | |
linsert 从列表中具体的值前面(或后面)插入值 | |
linsert key before|after pivot value | |
127.0.0.1:6379> lrange list 0 -1 | |
1) "value2" | |
2) "item" | |
127.0.0.1:6379> linsert list before item before | |
(integer) 3 | |
127.0.0.1:6379> lrange list 0 -1 | |
1) "value2" | |
2) "before" | |
3) "item" |
小结
- 实际上是一个链表
- key 不存在就创建,存在则新增
- 如果移除了所有值,也就是空链表,则代表不存在
- 在两边插入或改值时,效率最高,中间元素效率会低一点。
# Set(集合)
set 是一个无序不重复集合
################################################################################## | |
127.0.0.1:6379> sadd myset lovekuangshen # 建立集合,增加值 | |
(integer) 1 | |
127.0.0.1:6379> smembers myset # 查看集合内容 | |
1) "lovekuangshen" | |
2) "kuangshen" | |
3) "hello" | |
127.0.0.1:6379> sismember myset hello # 判断 hello 是否存在于 myset 中 | |
(integer) 1 | |
127.0.0.1:6379> | |
################################################################################## | |
127.0.0.1:6379> scard myset # 查看集合中元素的个数 | |
(integer) 3 | |
################################################################################## | |
srem key value # 移除集合中的元素 | |
################################################################################## | |
SRANDMEMBER key [count] 随机筛选元素,默认筛选个数为1 | |
################################################################################## | |
随机删除key,指定删除key | |
spop随机移除元素 | |
################################################################################## | |
将一个指定的值移动到另外一个set中 | |
smove source destination member | |
127.0.0.1:6379> smove myset myset2 hello | |
(integer) 1 | |
127.0.0.1:6379> SMEMBERS myset | |
1) "lovekuangshen" | |
2) "kuangshen" | |
127.0.0.1:6379> SMEMBERS myset2 | |
1) "hello" | |
2) "set2" | |
127.0.0.1:6379> | |
################################################################################## | |
微博,B站,共同关注!(并集) | |
数字集合类: | |
- 差集 | |
- 交集 | |
- 并集 | |
127.0.0.1:6379> sadd set a | |
127.0.0.1:6379> sadd set b | |
127.0.0.1:6379> sadd set c | |
127.0.0.1:6379> sadd set2 c | |
127.0.0.1:6379> sadd set2 d | |
127.0.0.1:6379> sadd set2 e | |
127.0.0.1:6379> SDIFF set set2 # 找不同,也就是差集 | |
1) "a" | |
2) "b" | |
127.0.0.1:6379> SINTER set set2 # 交集 可以实现共同好友 | |
1) "c" | |
127.0.0.1:6379> SUNION set set2 # 并集 | |
1) "a" | |
2) "c" | |
3) "b" | |
4) "e" | |
5) "d" | |
127.0.0.1:6379> |
微博,A 用户将所有关注的人放在一个 set 集合中!将它的粉丝也放在一个集合中!
共同关注,共同爱好,二度好友,推荐好友!(六度分割理论)
# Hash(哈希)
map 集合,key-map
################################################################################## | |
127.0.0.1:6379> hset hash filed1 kuangshen # 设置值 | |
(integer) 1 | |
127.0.0.1:6379> hget hash filed1 # 获取值 | |
"kuangshen" | |
127.0.0.1:6379> hmset hash filed2 k2 filed3 k3 filed4 k4 # 同时设置多个值 | |
OK | |
127.0.0.1:6379> hmget hash filed1 filed2 filed3 # 同时获取多个值 | |
1) "kuangshen" | |
2) "k2" | |
3) "k3" | |
127.0.0.1:6379> hgetall hash # 获取全部数据 | |
1) "filed1" | |
2) "kuangshen" | |
3) "filed2" | |
4) "k2" | |
5) "filed3" | |
6) "k3" | |
7) "filed4" | |
8) "k4" | |
127.0.0.1:6379> hdel hash filed1 # 删除指定字段 | |
(integer) 1 | |
127.0.0.1:6379> hgetall hash | |
1) "filed2" | |
2) "k2" | |
3) "filed3" | |
4) "k3" | |
5) "filed4" | |
6) "k4" | |
################################################################################## | |
127.0.0.1:6379> hlen hash # 获取长度 | |
(integer) 3 | |
################################################################################## | |
HEXISTS hash filed2 # 判断元素是否存在 | |
################################################################################## | |
127.0.0.1:6379> hkeys hash # 获取所有 key | |
1) "filed2" | |
2) "filed3" | |
3) "filed4" | |
127.0.0.1:6379> hvals hash # 获取所有值 | |
1) "k2" | |
2) "k3" | |
3) "k4" | |
################################################################################## | |
HINCRBY hash f1 1 # f1 自增 1 HINCRBY hash f1 -1 相当于自减 | |
127.0.0.1:6379> hsetnx hash f1 5 # 如果不存在则可以设置,存在则不行 | |
(integer) 0 | |
127.0.0.1:6379> hsetnx hash f2 5 | |
(integer) 1 |
hash 存变更数据 user name age,尤其是用户信息之类的,经常变动的信息,hash 更适合于对象的存储,String 更适合字符串的存储!
# zset(有序集合)
127.0.0.1:6379> zadd myset 1 one 2 two 3 three # 添加三个值,数字是排序标志 | |
(integer) 3 | |
127.0.0.1:6379> zrange myset 0 -1 | |
1) "one" | |
2) "two" | |
3) "three" | |
127.0.0.1:6379> zadd salary 2500 xiaohong 5000 zhangsan 500 kuangshen | |
(integer) 3 | |
################################################################################## | |
排序 | |
# ZRANGEBYSCORE key min max withscores (选填,附带成绩等排序标志) | |
127.0.0.1:6379> ZRANGEBYSCORE myset (-1 (3 # (后两个是区间,有括号表示不包含该数字) | |
1) "one" | |
2) "two" | |
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf # 全部用户从小到大排序 (范围) | |
1) "kuangshen" | |
2) "xiaohong" | |
3) "zhangsan" | |
127.0.0.1:6379> ZREVRANGE myset 0 -1 # 从大到小排列 | |
1) "three" | |
2) "two" | |
3) "one" | |
127.0.0.1:6379> ZRANGEBYSCORE salary -inf 2500 withscores # 2500 以内 | |
1) "kuangshen" | |
2) "500" | |
3) "xiaohong" | |
4) "2500" | |
################################################################################## | |
zrem 移除元素 | |
127.0.0.1:6379> zrem salary xiaohong | |
(integer) 1 | |
127.0.0.1:6379> zrange salary 0 -1 | |
1) "kuangshen" | |
2) "zhangsan" | |
################################################################################## | |
zcard key 查看元素个数 | |
zcount key min max 统计区间值的个数 | |
127.0.0.1:6379> zcount myset 1 2 | |
(integer) 2 | |
################################################################################## |
案例思路: set 排序,存储班级成绩表,工资排序表,排行榜
普通消息, 1, 重要消息, 2, 带权重进行判断!
# 三种特殊数据类型
# geospatial(地理位置)
朋友定位,附近的人,打车距离计算
经纬度查询 (获取测试数据)
geoadd (添加地理位置)
################################################################################## | |
# 规则:地球两极无法添加, 一般直接通过 java 程序一次性导入 | |
geoadd key 经度 纬度 名字 | |
127.0.0.1:6379> geoadd china:city 116.23128 40.22077 beijing | |
(integer) 1 | |
127.0.0.1:6379> geoadd china:city 121 31 shanghai | |
(integer) 1 | |
127.0.0.1:6379> geoadd china:city 106 29 chongqing | |
(integer) 1 |
geopos (获取)
127.0.0.1:6379> geopos china:city xian | |
1) 1) "108.00000160932540894" | |
2) "34.00000062127011091" |
geodist (获取两点距离)
单位:
- m for meters.
- km for kilometers.
- mi for miles.
- ft for feet.
127.0.0.1:6379> geodist china:city beijing chongqing | |
"1557966.1279" | |
127.0.0.1:6379> geodist china:city beijing chongqing km | |
"1557.9661" |
georadius
附近的人 (获取所有附近的人的地址,定位!)
georadius 经度 纬度 半径 单位
withcoord 显示他人的定位信息
127.0.0.1:6379> georadius china:city 110 30 1000 km | |
1) "chongqing" | |
2) "xian" | |
3) "shenzhen" | |
4) "hangzhou" | |
127.0.0.1:6379> georadius china:city 110 30 1000 km count 2 # 限制显示的数量 | |
1) "chongqing" | |
2) "xian" | |
127.0.0.1:6379> georadius china:city 110 30 1000 km withdist# 显示到 (110,30) 中心的距离 | |
1) 1) "chongqing" | |
2) "402.8557" | |
2) 1) "xian" | |
2) "483.2155" | |
127.0.0.1:6379> georadius china:city 110 30 1000 km withcoord | |
1) 1) "chongqing" | |
2) 1) "106.00000172853469849" | |
2) "28.99999952347345555" | |
2) 1) "xian" | |
2) 1) "108.00000160932540894" | |
2) "34.00000062127011091" |
georadiusbymember
# 找出位于指定元素周围的其他元素 | |
127.0.0.1:6379> georadiusbymember china:city chongqing 1000 km | |
1) "chongqing" | |
2) "xian" |
geohash - 返回一个或多个位置元素的个 geohash 表示
该命令返回 11 个字符的 geohash 字符串
# 将二维的经纬度转换成一维的字符串,,如果两个字符串越接近,那么距离越近。 | |
127.0.0.1:6379> geohash china:city beijing chongqing | |
1) "wx4sucvncn0" | |
2) "wm5kup6c5p0" |
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 hangzhou # 移除 geo 建立的元素 | |
(integer) 1 | |
127.0.0.1:6379> zrange china:city 0 -1 | |
1) "chongqing" | |
2) "xian" | |
3) "shenzhen" | |
4) "shanghai" | |
5) "beijing" |
# Hyperloglog
基数(一个集合中不重复的元素)
简介:用于做基数统计的算法
优点:占用内存固定, 2^64 个不同的元素的基数,只需要废 12KB 的内存,从内存角度看 Hyperloglog 是首选
0.81% 错误率!统计 UV 任务是可以忽略不计的。
127.0.0.1:6379> pfadd mykey a b c d e f g h i j # 添加 | |
(integer) 1 | |
127.0.0.1:6379> PFCount mykey # 计数,统计基数数量 | |
(integer) 10 | |
127.0.0.1:6379> PFCOUNT mykey2 | |
(integer) 9 | |
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2 # 合并 | |
OK | |
127.0.0.1:6379> PFCOUNT mykey3 | |
(integer) 15 |
允许容错,一定可以使用 Hyperloglog
不允许容错,就使用 set 或者自己的数据类型即可
# Bitmaps
按位存储
统计疫情感染人数:000000110101010
统计用户信息:活跃,不活跃!登录,未登录!365 打卡!两个状态的都可以使用 bitmaps
Bitmaps 位图,数据结构!都是操作二进制位来进行记录的,只有 0 和 1 两种状态
测试
127.0.0.1:6379> setbit sign 0 1 # 设置七天的打卡状态,0-6 > 周一到周天 | |
127.0.0.1:6379> setbit sign 1 0 | |
127.0.0.1:6379> setbit sign 2 0 | |
127.0.0.1:6379> setbit sign 3 1 | |
127.0.0.1:6379> setbit sign 4 1 | |
127.0.0.1:6379> setbit sign 5 0 | |
127.0.0.1:6379> setbit sign 6 0 | |
127.0.0.1:6379> getbit sign 3 # 确认周四有没有打卡 | |
(integer) 1 | |
# 统计的操作,统计打卡天数,默认全部 | |
127.0.0.1:6379> bitcount sign | |
(integer) 3 |
# redis 的事务
redis 的单条命令可以保证原子性(要么同时成功,要么同时失败),但是 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> set k3 v3 | |
QUEUED | |
127.0.0.1:6379> getset k3 | |
(error) ERR wrong number of arguments for 'getset' command | |
127.0.0.1:6379> exec | |
(error) EXECABORT Transaction discarded because of previous errors. | |
127.0.0.1:6379> get k1 | |
(nil) |
运行时异常(例如:1/0),如果事务队列中存在语法性错误,那么执行命令的时候,其他命令是可以正常执行的。错误命令会抛出异常。
127.0.0.1:6379> set k1 "v1" | |
OK | |
127.0.0.1:6379> multi | |
OK | |
127.0.0.1:6379> incr k | |
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 k3 | |
QUEUED | |
127.0.0.1:6379> exec # 虽然第一条错了,但是后面仍然执行成功 | |
1) (error) ERR value is not an integer or out of range | |
2) OK | |
3) OK | |
4) "v3" | |
127.0.0.1:6379> get k2 | |
"v2" |
监控 watch
悲观锁:
- 很悲观,认为什么时候都会出现问题,无论做什么都会加锁!
乐观锁:
- 很乐观,认为什么时候都不会出现问题,所以不会上锁!更新数据的时候去判断一下,在此期间是否有人修改过这个数据。
- 获取 version,更新的时候去比较 version
监视测试
127.0.0.1:6379> set money 100 | |
127.0.0.1:6379> set out 0 | |
127.0.0.1:6379> watch money # 监视 money 对象 | |
127.0.0.1:6379> multi # 事务正常结束,数据期间没有发生变动,这个时候正常执行成功!(一旦事务执行后,监视会自动取消) | |
OK | |
127.0.0.1:6379> DECRBY money 20 | |
QUEUED | |
127.0.0.1:6379> INCRBY out 20 | |
QUEUED | |
127.0.0.1:6379> exec | |
1) (integer) 80 | |
2) (integer) 20 |
测试多线程修改值,使用 watch 相当于 redis 的乐观锁操作
127.0.0.1:6379> WATCH money | |
127.0.0.1:6379> multi | |
127.0.0.1:6379> DECRBY money 10 | |
QUEUED | |
127.0.0.1:6379> INCRBY out 10 | |
QUEUED | |
127.0.0.1:6379> exec # 执行之前。另外一个线程修改了这个值,这个时候,会导致事务执行失败!(无论失败成功 exec 都会自动解锁) | |
(nil) | |
127.0.0.1:6379> unwatch # 停止监视,之后再重新监视执行 | |
OK | |
127.0.0.1:6379> watch money | |
OK |