# 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
更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

obsidianlyg 微信支付

微信支付