Redis 核心原理与应用实践

第一章 基础和应用篇

1.1 Redis 数据结构

Redis 中基础的数据结构

分别是string list hash set zset

1、String

String 基本命令

127.0.0.1:6379> set name cyf
OK
127.0.0.1:6379> get name
"cyf"
127.0.0.1:6379> exites name
(error) ERR unknown command 'exites'
127.0.0.1:6379> exists name kkk
(integer) 1
127.0.0.1:6379> exists name 
(integer) 1
127.0.0.1:6379> get name
"cyf"
127.0.0.1:6379> del name
(integer) 1
127.0.0.1:6379> mset name1 kkk name2 hhh
OK
127.0.0.1:6379> mget name1 name2
1) "kkk"
2) "hhh"

#设置过期时间
127.0.0.1:6379> expire name1 4
(integer) 1
127.0.0.1:6379> get name1
(nil)
127.0.0.1:6379> setex name1 2 hhh
OK
127.0.0.1:6379> get name1
(nil)
127.0.0.1:6379> set age 30
OK
127.0.0.1:6379> incr age  #自增长
(integer) 31
127.0.0.1:6379> incr age
(integer) 32
127.0.0.1:6379> incr age
(integer) 33

2、list
list 相当于java里面的LinkedList 双向链表,底层是一个称为快速链表(quicklist)的一个结构

#左进左出 相当于队列
127.0.0.1:6379> rpush books python java golang
(integer) 3
127.0.0.1:6379> llen books
(integer) 3
127.0.0.1:6379> lpop books
"python"
127.0.0.1:6379> lpop books
"java"
127.0.0.1:6379> 
127.0.0.1:6379> lpop books
"golang"
127.0.0.1:6379> lpop books
(nil)
#右进左出 相当于栈

127.0.0.1:6379> rpush books python java golang
(integer) 3
127.0.0.1:6379> rpop books
"golang"
127.0.0.1:6379> rpop books
"java"
127.0.0.1:6379> rpop books
"python"
127.0.0.1:6379> rpop books
(nil)

3、hash

类似于HashMap

#HashMap中rehash是个耗时的操作,Redis为了追求性能,采用渐进式rehash策略
#渐进式rehash 会在rehash的同时,保留新旧两个hash结构,查询会同时查询两个hash结构,在后续会循序渐进地将旧hash的内容一点点迁移至新的hash结构中,当搬迁完成后,就使用新的hash
127.0.0.1:6379> hset books java "think in java"
(integer) 1
127.0.0.1:6379> hset books python "python book"
(integer) 1
127.0.0.1:6379> hset books golang "concurrency in go"
(integer) 1
127.0.0.1:6379> hgetall books
1) "java"
2) "think in java"
3) "python"
4) "python book"
5) "golang"
6) "concurrency in go"
127.0.0.1:6379> hlen books
(integer) 3
127.0.0.1:6379> hget books java
"think in java"
127.0.0.1:6379> hset books golang "learning go programming"
(integer) 0
127.0.0.1:6379> hget books golang
"learning go programming"
127.0.0.1:6379> hmset books java "learning java" python "hello python"
OK
127.0.0.1:6379> hgetall
(error) ERR wrong number of arguments for 'hgetall' command
127.0.0.1:6379> hgetall books
1) "java"
2) "learning java"
3) "python"
4) "hello python"
5) "golang"
6) "learning go programming"
127.0.0.1:6379> hincrby user-cyf age 24
(integer) 24
127.0.0.1:6379> hincrby user-cyf age 1
(integer) 25
127.0.0.1:6379> hincrby user-cyf age 1
(integer) 26
#### hash结构一般用来存储用户信息,可以对用户结构中的每个字段单独存储。当获取用户信息时可以进行部分获取

4、set

## 相当于HashSet  set可以用来去重,例如存储活动中奖id
127.0.0.1:6379> sadd books java
(integer) 1
127.0.0.1:6379> sadd books java
(integer) 0
127.0.0.1:6379> sadd books java python
(integer) 1
127.0.0.1:6379> smembers books
1) "python"
2) "java"
127.0.0.1:6379> sismember books java
(integer) 1
127.0.0.1:6379> sismember books hh
(integer) 0
127.0.0.1:6379> scard books
(integer) 2
127.0.0.1:6379> spop books
"python"
127.0.0.1:6379> spop books
"java"
127.0.0.1:6379> spop books
(nil)

5、zset

## zset是个有序的set 可以用来排序 内存存储信息分为value 和 score
127.0.0.1:6379> zadd books 9.0 "think in java"
(integer) 1
127.0.0.1:6379> zadd books 8.8 "java concurrency"
(integer) 1
127.0.0.1:6379> zadd books 7.0 "java cookbook"
(integer) 1
127.0.0.1:6379> zrange books 0 -1
1) "java cookbook"
2) "java concurrency"
3) "think in java"
127.0.0.1:6379> zrange books  0 -1
1) "java cookbook"
2) "java concurrency"
3) "think in java"
127.0.0.1:6379> zrevrange books 0 -1
1) "think in java"
2) "java concurrency"
3) "java cookbook"
127.0.0.1:6379> zcard books
(integer) 3
127.0.0.1:6379> zscore books "java cookbook:
Invalid argument(s)
127.0.0.1:6379> zscore books "java cookbook"
"7"
127.0.0.1:6379> zrank books "java cookbook"
(integer) 0
127.0.0.1:6379> zrangebyscore books 0 8.9
1) "java cookbook"
2) "java concurrency"
127.0.0.1:6379> zrem books "java cookbook"
(integer) 1
127.0.0.1:6379> zrange books 0 -1
1) "java concurrency"
2) "think in java"

zset内部的排序功能是通过跳跃列表的 数据结构实现的。

1.2分布式锁

127.0.0.1:6379> set lock:codehole true ex 5 nx
OK
#do something...
127.0.0.1:6379> del lock:codehole
(integer) 1

会产生一个问题,当业务执行逻辑超过设定时间 锁会超时(所以适应在操作时间不长的场景)

可以在加锁前生成一个随机的value 解锁时判断是否是同一个value然后再删除key

但是判断和删除不是一个原子性操作

可以使用lua脚本保证原子性