redis学习
Nosql
数据访问量越来越大:
- 单机
- Memcached
数据类型
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> keys * # 查看所有键
(empty array)
127.0.0.1:6379> set age "敖德萨大" # 设置值
OK
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> keys age
1) "age"
127.0.0.1:6379> get age # 获取值
"\xe6\x95\x96\xe5\xbe\xb7\xe8\x90\xa8\xe5\xa4\xa7"
127.0.0.1:6379> EXISTS age # 判断key是否存在
(integer) 1
127.0.0.1:6379> EXISTS agea
(integer) 0
127.0.0.1:6379> move age # 语法错误,需要数据库
(error) ERR wrong number of arguments for 'move' command
127.0.0.1:6379> move age 1 # 移除库1中的age
(integer) 1
# 过期时间设置
127.0.0.1:6379> set name "IOIOIO"
OK
127.0.0.1:6379> keys name
1) "name"
127.0.0.1:6379> get name
"IOIOIO"
127.0.0.1:6379> EXPIRE name 10 # 设置过期时间
(integer) 1
127.0.0.1:6379> ttl name # 查看存活时间
(integer) 6
127.0.0.1:6379> ttl name
(integer) 3
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> get name
(nil)
# 查看类型
127.0.0.1:6379> set age 12
OK
127.0.0.1:6379> type age
string
String
场景:
- string
- 计数器
- 对象缓存存储
- cookie
127.0.0.1:6379> set name "abcdefghijklmn"
OK
127.0.0.1:6379> append name " 123456" # 追加,如果key不存在则创建空并追加
(integer) 21 # 返回append之后的长度
127.0.0.1:6379> get name
"abcdefghijklmn 123456"
127.0.0.1:6379> STRLEN name # 获取长度
(integer) 21
# 加减
127.0.0.1:6379> set views 0
OK
127.0.0.1:6379> type views
string
127.0.0.1:6379> type view # key不存在时类型为none
none
127.0.0.1:6379> incr views # 加 1
(integer) 1
127.0.0.1:6379> type views
string
127.0.0.1:6379> get views
"1"
127.0.0.1:6379> DECR views # 减 1
(integer) 0
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> INCRBY views 98 # 步长98自增
(integer) 98
127.0.0.1:6379> DECRBY views 9
(integer) 89
127.0.0.1:6379> APPEND views io9 # views == "9io9"
(integer) 5
127.0.0.1:6379> incr views # 自增错误,非整数
(error) ERR value is not an integer or out of range
# 获取字符串范围
127.0.0.1:6379> set name "hello, world!"
OK
127.0.0.1:6379> GETRANGE name 0 -1 # -1 代表最后一位
"hello, world!"
127.0.0.1:6379> GETRANGE name 0 100 # 超过本身长度不报错
"hello, world!"
127.0.0.1:6379> GETRANGE name 0 3 # 一般获取
"hell"
127.0.0.1:6379> GETRANGE name 100 -1 # 返回空
""
127.0.0.1:6379> GETRANGE name 100 9
""
127.0.0.1:6379> GETRANGE name 100 900
""
127.0.0.1:6379> GETRANGE name -1 0
""
127.0.0.1:6379> GETRANGE names 0 -1 # key不存在时返回“”
""
# 指定范围替换
127.0.0.1:6379> SETRANGE name 1 xx
(integer) 13
127.0.0.1:6379> get name
"hxxlo, world!"
# setex (set with expire) 设置值的同时过期时间
# setnx (set if not exist) 不存在时才设置
127.0.0.1:6379> setex key3 30 "Wait ...."
OK
127.0.0.1:6379> get key3
"Wait ...."
127.0.0.1:6379> ttl key3
(integer) 18
127.0.0.1:6379> setnx mykey "first" # 不存在,设置成功
(integer) 1
127.0.0.1:6379> setnx mykey "second" # 已存在,设置失败
(integer) 0
127.0.0.1:6379> get mykey
"first"
127.0.0.1:6379> get key3 # 不存在,返回nil
(nil)
# 批量设置
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 # 批量设置
OK
127.0.0.1:6379> mget k1 v1 k2 k3 #批量获取
1) "v1"
2) (nil)
3) "v2"
4) "v3"
127.0.0.1:6379> msetnx k1 OP k9 v9 # 保证一致性,k1已存在,设置失败,k9不再进行设置
(integer) 0
127.0.0.1:6379> keys *
1) "k1"
2) "k3"
3) "k2"
4) "mykey"
127.0.0.1:6379> get k9
(nil)
# 对象保存
127.0.0.1:6379> mset user:1:name zhangsan user:1:age 18
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "zhangsan"
2) "18"
# getset,获取当前的值并设置新值
127.0.0.1:6379> getset db redis
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mysql
"redis"
List
列表:存储是链表(双向?)
|--------------------------------------|
| |---| |---| |---| |
| L | 1 | | 2 | | 3 | .... R |
| |---| |---| |---| |
|--------------------------------------|
- 在两边操作,效率比在中间元素操作高
- 消息队列(lpush, rpop) 栈(lpush,)
# l --> list | left
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"
3) "one"
127.0.0.1:6379> LRANGE list 0 1
1) "three"
2) "two"
# r --> right
127.0.0.1:6379> rpush list right # 向右边(尾部)插入
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
# lpop rpop
127.0.0.1:6379> lpop list # 弹出最左边的元素
"three"
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
3) "right"
127.0.0.1:6379> rpop list # 弹出最右边的元素
"right"
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> lindex list 1 # 通过下标获取
"one"
127.0.0.1:6379> lindex list 0
"two"
127.0.0.1:6379> Llen list # 返回列表长度
(integer) 2
# 移除值
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "three"
3) "three"
4) "two"
5) "one"
127.0.0.1:6379> lrem list 1 one # 移除 1 个 one 元素
(integer) 1
127.0.0.1:6379> lrem list 2 three # 移除 2 个 three 元素
(integer) 2
127.0.0.1:6379> lrem list 2 threeS
(integer) 0
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
# ltrim 截取
127.0.0.1:6379> LRANGE list 0 -1
(empty array)
127.0.0.1:6379> rpush list hello0
(integer) 1
127.0.0.1:6379> rpush list hello1
(integer) 2
127.0.0.1:6379> rpush list hello2
(integer) 3
127.0.0.1:6379> rpush list hello3
(integer) 4
127.0.0.1:6379> ltrim list 2 3 # ltrim key start end (会改变list!)
OK
127.0.0.1:6379> LRANGE list 0 -1
1) "hello2"
2) "hello3"
# rpoplpush 右弹出并左压进其他列表 (弹出最后一个元素并放入其他列表的第一个元素)
127.0.0.1:6379> LRANGE list 0 -1
1) "hello2"
2) "hello3"
127.0.0.1:6379> rpoplpush list newlist # source desition
"hello3"
127.0.0.1:6379> LRANGE list 0 -1
1) "hello2"
127.0.0.1:6379> LRANGE newlist 0 -1
1) "hello3"
# 更新值 lset, 插入值 linsert
127.0.0.1:6379> LRANGE list 0 -1
1) "v0"
2) "v1"
3) "v2"
4) "v3"
5) "v3"
127.0.0.1:6379> lset list 3 VALUE
OK
127.0.0.1:6379> LRANGE list 0 -1
1) "v0"
2) "v1"
3) "v2"
4) "VALUE"
5) "v3"
127.0.0.1:6379> lset list 5 VALUE # 超出范围
(error) ERR index out of range
127.0.0.1:6379> linsert list before v1 INSERT_V1 # 在值v之前插入
(integer) 6
127.0.0.1:6379> linsert list after v2 INSERT_V2 # 在值v2之后插入
(integer) 7
127.0.0.1:6379> LRANGE list 0 -1
1) "v0"
2) "INSERT_V1"
3) "v1"
4) "v2"
5) "INSERT_V2"
6) "VALUE"
7) "v3"
Set
# sadd 添加 smembers 列出所有成员
127.0.0.1:6379> sadd set hello
(integer) 1
127.0.0.1:6379> sadd set hello1
(integer) 1
127.0.0.1:6379> sadd set hello2
(integer) 1
127.0.0.1:6379> SMEMBERS set
1) "hello2"
2) "hello"
3) "hello1"
# sismember 判断是否存在某一元素
127.0.0.1:6379> SISMEMBER set hello # 存在
(integer) 1
127.0.0.1:6379> SISMEMBER set hello12 # 不存在
(integer) 0
# scard 获取集合元素的个数
127.0.0.1:6379> scard set
(integer) 3
# srem 移除元素
127.0.0.1:6379> srem set
(error) ERR wrong number of arguments for 'srem' command
127.0.0.1:6379> srem set hello
(integer) 1
127.0.0.1:6379> SMEMBERS set
1) "hello2"
2) "hello1"
# srandmember set [count] 随机抽取指定个数的元素
# spop set 随机删除元素
# smove source destition value 移动指定元素
# sdiff 差集
# sinter 交集
# sunion 并集
Hash
# hset hget hgetall
127.0.0.1:6379> hset hash k1 v1 k2 v2 k3 v3
(integer) 3
127.0.0.1:6379> hget hash k1
"v1"
127.0.0.1:6379> hgetall hash
1) "k1"
2) "v1"
3) "k2"
4) "v2"
5) "k3"
6) "v3"
# hdel 删除
127.0.0.1:6379> hdel hash k1
(integer) 1
# hlen 长度
127.0.0.1:6379> hgetall hash
1) "k2"
2) "v2"
3) "k3"
4) "v3"
127.0.0.1:6379> HLEN hash
(integer) 2
# hexists 是否存在
127.0.0.1:6379> HEXISTS hash k1 # 不存在
(integer) 0
127.0.0.1:6379> HEXISTS hash k2 # 存在
(integer) 1
# hkeys 只获取key, hvals 只获取value
127.0.0.1:6379> hkeys hash
1) "k2"
2) "k3"
127.0.0.1:6379> HVALS hash
1) "v2"
2) "v3"
# hincrby hsetnx
Zset
有序集合
# zadd zrange
127.0.0.1:6379> zadd myset 1 one
(integer) 1
127.0.0.1:6379> zadd myset 2 two
(integer) 1
127.0.0.1:6379> zadd myset 3 three
(integer) 1
127.0.0.1:6379> ZRANGE myset 0 -1
1) "one"
2) "two"
3) "three"
# zrangebyscore 排序
127.0.0.1:6379> zrangebyscore myset -inf +inf
1) "one"
2) "two"
3) "three"
# 同时输出score
127.0.0.1:6379> zrangebyscore myset -inf 10 withscores
1) "one"
2) "1"
3) "two"
4) "2"
5) "three"
6) "3"
# zrem 移除
127.0.0.1:6379> zrangebyscore myset -inf 10 withscores
1) "one"
2) "1"
3) "two"
4) "2"
5) "three"
6) "3"
# zcard 查看数量
# zcount 获取指定区间的元素数量
127.0.0.1:6379> zcount myset 0 9
(integer) 2
Geospatial
地理位置
底层是zset
# geoadd 添加
127.0.0.1:6379> geoadd china:city 116.40 39.90 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 chongqing
(integer) 1
# geopos 获取
127.0.0.1:6379> geopos china:city beijing
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
# geodist 计算直线距离
127.0.0.1:6379> geodist china:city beijing shanghai m
"1067378.7564"
127.0.0.1:6379> geodist china:city beijing shanghai km
"1067.3788"
# georadious 计算给定坐标给定半径内的key
127.0.0.1:6379> georadius china:city 110 30 1000 km
1) "chongqing"
# 同时限制数量,给出坐标和距离
127.0.0.1:6379> georadius china:city 110 30 2000 km withdist withcoord count 1
1) 1) "chongqing"
2) "341.9374"
3) 1) "106.49999767541885376"
2) "29.52999957900659211"
# groradiousmember 找出给定城市给定距离的城市
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1300 km
1) "shanghai"
2) "beijing"
# geohash 返回地址的hash,失去精度但仍指向同一区域
127.0.0.1:6379> geohash china:city beijing
1) "wx4fbxxfke0"
# zrem 移除 zrange 查看
127.0.0.1:6379> ZREM china:city beijing
(integer) 1
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "chongqing"
2) "shanghai"
Hyperloglog
基数
:两个集合不重复的元素,可以有误差(0.81%的误差)
优点:
- 占用内存固定,264不同的元素,需要12KB
网页的访问数:传统方式使用set保存用户ID来避免同一用户访问造成的多次计数。
# pfadd 添加
127.0.0.1:6379> pfadd key 1 2 3 4 5 6 7 8 9 0
(integer) 1
127.0.0.1:6379> pfadd key1 ! 2 3 $ 5 6 & 8 9 )
(integer) 1
# pfcount 计算基数
127.0.0.1:6379> pfcount key key1
(integer) 14
127.0.0.1:6379> pfcount key
(integer) 10
127.0.0.1:6379> pfcount key1
(integer) 10
# pmerge 合并key和key1到key3
127.0.0.1:6379> PFMERGE key3 key key1
OK
127.0.0.1:6379> pfcount key3
(integer) 14
BitMap
位存储
统计用户活跃|不活跃、登录|未登录、打卡|未打卡……只有两个状态的,可以使用Bitmaps!
Bitmaps位图,数据结构,操作二进制位进行记录
# setbit 设置位
127.0.0.1:6379> setbit sign 5 1 # 第5天的状态
(integer) 0
127.0.0.1:6379> setbit sign 6 0 # 第6天的状态
(integer) 0
127.0.0.1:6379> setbit sign 7 1 # 第7天的状态
(integer) 0
# getbit 获取
127.0.0.1:6379> setbit sign 1 0
(integer) 1
127.0.0.1:6379> getbit sign 1
(integer) 0
# bitcount 计数
127.0.0.1:6379> bitcount sign
(integer) 7
127.0.0.1:6379> bitcount sign 0 -1
(integer) 7
事务
Redis单条命令保证原子性,但事务不保证原子性!
redis的事务:
- 开启事务(multi)
- 命令入队()
- 执行事务(exec)
Redis可以实现乐观锁。
# multi exec
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> get v3
QUEUED
127.0.0.1:6379(TX)> set v3 V3
QUEUED
127.0.0.1:6379(TX)> exec # 执行
1) OK
2) OK
3) (nil)
4) OK
# discard 放弃事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set key6 v6
QUEUED
127.0.0.1:6379(TX)> discard
OK
127.0.0.1:6379> exec
(error) ERR EXEC without MULTI
错误处理
- 代码异常:所有事务不执行
- 运行异常: 其他命令正常执行,错误直接忽略
乐观锁
- 悲观锁:认为什么时候都有问题,无论做什么都会加锁
- 乐观锁: 认为什么都没问题, 不会上锁!更新数据时去判断,在此期间是否有人修改过此数据。
- 获取version
- 更新时比较version
# 正常执行成功
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 # 监视money对象
OK
127.0.0.1:6379> mutil
(error) ERR unknown command `mutil`, with args beginning with:
127.0.0.1:6379> multi # 事务正常结束,期间数据未被改动,执行成功!
OK
127.0.0.1:6379(TX)> decrby money 20
QUEUED
127.0.0.1:6379(TX)> INCRBY out 20
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 80
2) (integer) 20
# 多线程执行失败
# 连接1
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> watch money # 进行监视(加锁)
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> INCRBY money 20
QUEUED
127.0.0.1:6379(TX)> exec # 先去执行连接B
(nil) # 事务执行失败
127.0.0.1:6379> unwatch # 取消监视
OK
127.0.0.1:6379> watch money # 监视新值
OK
# ………………………继续上面的操作(自弦索)
# 连接B
127.0.0.1:6379> get money
"100"
127.0.0.1:6379> DECRBY money 90
(integer) 10
Jedis
Java操作Redis
Jedis方法与redis命令一致
public static void main(String[] args) {
// 连接Jedis
Jedis jedis = new Jedis();
// Jedis 即所有指令
System.out.println(jedis.ping("fdsfds"));
}
}
SpringBoot整合
Spring-Data