欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Redis键-值数据库 nosql 数据建模(1)------ 如何存储单表数据

程序员文章站 2022-07-12 20:57:33
...

                                                                作者:QQ 14588019 WonderfulLife

关系型数据库用户数据表users(username字段上建立唯一索引):

id    username               password    name     surname
1     aaa@qq.com        pass1          Bob       Smith
2     aaa@qq.com    pass2          Mario     Rossi

3     aaa@qq.com         pass3          Tom       Smith
Redis 数据存储形式一 键名:字串(GET&SET):
Key                                      Value

user:1:username                  aaa@qq.com
user:1:password                   pass1
user:1:name                         Bob
user:1:surname                    Smith
user:2:username                  aaa@qq.com
user:2:password                   pass2
user:2:name                         Mario
user:2:surname                    Rossi
user:3:username                  aaa@qq.com
user:3:password                   pass3
user:3:name                         Tom
user:3:surname                    Smith
user:aaa@qq.com:id            1
user:aaa@qq.com:id      2
user:aaa@qq.com:id             3

Redis 数据存储形式二 键名:json字串(GET&SET):

Key            Value

users:1       {\"id\":1,\"username\":\"aaa@qq.com\",\"password\":\"pass1\",\"name\":\"Bob\",\"surname\":\"Smith\"}
users:2       {\"id\":2,\"username\":\"aaa@qq.com\",\"password\":\"pass2\",\"name\":\"Mario\",\"surname\":\"Rossi\"}
users:3       {\"id\":3,\"username\":\"aaa@qq.com\",\"password\":\"pass3\",\"name\":\"Tom\",\"surname\":\"Smith\"}

================================================================

Redis 数据存储形式一 键名:字串(GET&SET):

如果我们知道任何一个 $id,那么我们就能通过以下4个Key读出一条完整的用户记录,当然也可读出几个您需要的字段值。

user:$id:username,   user:$id:password,   user:$id:name,   user:$id:surname

第1组指令添加以下这条记录:
id    username  	password    name     surname
1     aaa@qq.com      pass1       Bob      Smith

INCR user:id
WATCH user:id
GET user:id
MULTI
SETNX user:aaa@qq.com:id 1
SET user:1:username "aaa@qq.com"
SET user:1:password "pass1"
SET user:1:name "Bob"
SET user:1:surname "Smith"
LPUSH user:list user:1:username
EXEC


第2组指令添加以下这条记录:
id    username  	password    name     surname
2     aaa@qq.com    pass2       Mario    Rossi

INCR user:id
WATCH user:id
GET user:id
MULTI
SETNX user:aaa@qq.com:id 2
SET user:2:username "aaa@qq.com"
SET user:2:password "pass2"
SET user:2:name "Mario"
SET user:2:surname "Rossi"
LPUSH user:list user:2:username
EXEC


第3组指令添加以下这条记录:
id    username  	password    name     surname
3     aaa@qq.com       pass3       Tom      Smith

INCR user:id
WATCH user:id
GET user:id
MULTI
SETNX user:aaa@qq.com:id 3
MSET user:3:username "aaa@qq.com" user:3:password "pass3" user:3:name "Tom" user:3:surname "Smith"
LPUSH user:list user:3:username
EXEC

解释说明:以上3组指令中首条指令就是用INCR user:id 生成记录的主键id值,这个值是可以用来分页或者按主键范围查找包含某个连续范围主键的记录集合,此时的user:id功能相当于实现了一个users:count键名称来存储记录的总条数

但在本范例中,我使用了队列uses:list来存储记录的主键id和username信息,稍后我会演示如何用队列来分页查询

SETNX user:aaa@qq.com:id 1
SETNX user:aaa@qq.com:id 2
SETNX user:aaa@qq.com:id 3
为何需要执行上面3条指令,用途是用来干什么的呢?它们作用就是可以通过Email去找记录的主键,Email是唯一索引,它与主键id是一对一关系,如果没有上面这样的指令,任何一个用户就没办法通过Email找到主键id的值,更没办法找到本条记录的其它字段信息;
WATCH user:id 的作用监测一个key的值是否被其它的程序更改。如果这个key在WATCH和EXEC间被修改,这个 MULTI/EXEC 事务的执行的代码块将会失败(return false),MULTI/EXEC 事务执行一系列命令有原子性保证

动手实践步骤如下:

Redis键-值数据库 nosql 数据建模(1)------ 如何存储单表数据

127.0.0.1:6379> MGET user:1:username user:1:password user:1:name user:1:surname
1) "aaa@qq.com"
2) "pass1"
3) "Bob"
4) "Smith"
127.0.0.1:6379> MGET user:2:username user:2:password user:2:name user:2:surname
1) "aaa@qq.com"
2) "pass2"
3) "Mario"
4) "Rossi"
127.0.0.1:6379> MGET user:3:username user:3:password user:3:name user:3:surname
1) "aaa@qq.com"
2) "pass3"
3) "Tom"
4) "Smith"
127.0.0.1:6379>
如果某个/某些用户从系统中被删除,使用GET user:id 返回值 = 记录总数来分页就不对啦

例如:删除id = 2 这条记录,我们需要删除与主键id = 2的所有键-值对以保证数据的参照完整性

127.0.0.1:6379> GET user:2:username
"aaa@qq.com"
127.0.0.1:6379> 
127.0.0.1:6379> MGET user:aaa@qq.com:id user:2:username user:2:password user:2:name user:2:surname
1) "2"
2) "aaa@qq.com"
3) "pass2"
4) "Mario"
5) "Rossi"
127.0.0.1:6379> DEL user:aaa@qq.com:id user:2:username user:2:password user:2:name user:2:surname
(integer) 5
127.0.0.1:6379> LRANGE user:list 0 -1
1) "user:3:username"
2) "user:2:username"
3) "user:1:username"
127.0.0.1:6379> LREM user:list 1 user:2:username
(integer) 1
127.0.0.1:6379> LRANGE user:list 0 -1
1) "user:3:username"
2) "user:1:username"
127.0.0.1:6379>
Redis键-值数据库 nosql 数据建模(1)------ 如何存储单表数据

如果使用user:list队列(List类型的数据)存储记录的主键字符串来进行分页:

127.0.0.1:6379> LLEN user:list
(integer) 2
127.0.0.1:6379> LRANGE user:list 0  20
1) "user:3:username"
2) "user:1:username"
127.0.0.1:6379> LRANGE user:list 21 40
(empty list or set)
127.0.0.1:6379> LRANGE user:list 41 60
(empty list or set)
127.0.0.1:6379>

解释说明:
LLEN user:list  # 返回记录总数 在phpredis扩展里面$redis->lsize("user:list")方法会返回记录总数
LRANGE user:list 0  20   # 这是第1页的20条记录主键
LRANGE user:list 21 40   # 这是第2页的20条记录主键
LRANGE user:list 41 60   # 这是第3页的20条记录主键

如果使用SortedSet类型存储记录的主键key字符串进行分页:

127.0.0.1:6379> FLUSHDB # 动手实践前把数据库清空

第1组指令添加以下这条记录:  
id    username      password    name     surname  
1     aaa@qq.com      pass1       Bob      Smith  
  
INCR user:id  
WATCH user:id  
GET user:id  
MULTI  
SETNX user:aaa@qq.com:id 1  
SET user:1:username "aaa@qq.com"  
SET user:1:password "pass1"  
SET user:1:name "Bob"  
SET user:1:surname "Smith"  
ZADD user:zset 1 user:1:username  
EXEC  
  
第2组指令添加以下这条记录:  
id    username      password    name     surname  
2     aaa@qq.com    pass2       Mario    Rossi  
  
INCR user:id  
WATCH user:id  
GET user:id  
MULTI  
SETNX user:aaa@qq.com:id 2  
SET user:2:username "aaa@qq.com"  
SET user:2:password "pass2"  
SET user:2:name "Mario"  
SET user:2:surname "Rossi"  
ZADD user:zset 2 user:2:username 
EXEC  
  
第3组指令添加以下这条记录:  
id    username      password    name     surname  
3     aaa@qq.com       pass3       Tom      Smith  
  
INCR user:id  
WATCH user:id  
GET user:id  
MULTI  
SETNX user:aaa@qq.com:id 3  
MSET user:3:username "aaa@qq.com" user:3:password "pass3" user:3:name "Tom" user:3:surname "Smith"  
ZADD user:zset 3 user:3:username  
EXEC  

127.0.0.1:6379> ZCARD user:zset           # 返回记录总数
127.0.0.1:6379> ZRANGE user:zset 0  20    # 这是第1页的20条记录主键
1) "user:1:username"
2) "user:2:username"
3) "user:3:username"
	... ... 
20) "user:20:username"
127.0.0.1:6379> ZRANGE user:zset 21 40   # 这是第2页的20条记录主键
127.0.0.1:6379> ZRANGE user:zset 41 60   # 这是第3页的20条记录主键

我们还可以倒序读取分页需要的主键:
127.0.0.1:6379> ZREVRANGE user:zset 0  20   # 这是第1页的20条记录主键
1) "user:60:username"
2) "user:59:username"
3) "user:58:username"
	... ... 
20) "user:51:username"
127.0.0.1:6379> ZREVRANGE user:zset 21 40   # 这是第2页的20条记录主键
127.0.0.1:6379> ZREVRANGE user:zset 41 60   # 这是第3页的20条记录主键

如果某个/某些用户从Redis中被删除,使用GET user:2:username 返回主键对应的Email值
例如:删除id = 2 这条记录,我们需要删除与主键id = 2的有关的所有键-值对以保证数据的参照完整性
127.0.0.1:6379> GET user:2:username
"aaa@qq.com"
127.0.0.1:6379> DEL user:aaa@qq.com:id user:2:username user:2:password user:2:name user:2:surname
(integer) 5
127.0.0.1:6379> ZREM user:zset user:2:username # 第1个参数是有序集合的key,第2个参数是被删除记录的key
(integer) 1
127.0.0.1:6379> ZRANGE user:zset 0  20  WITHSCORES
1) "user:1:username"
2) "1"
3) "user:3:username"
4) "3"
127.0.0.1:6379> ZCARD user:zset
(integer) 2
127.0.0.1:6379>

如果我们使用哈希集合类型HashSet存储1整条记录:

127.0.0.1:6379> FLUSHALL   # 清空Redis已存储的所有数据
第1组指令添加以下这条记录:
id    username  	password    name     surname
1     aaa@qq.com      pass1       Bob      Smith

MULTI
SETNX user:aaa@qq.com:id 1
HMSET user:1:hset id  1  username "aaa@qq.com"  password "pass1"  name "Bob"  surname "Smith"
LPUSH user:list user:1:hset
EXEC

第2组指令添加以下这条记录:
id    username  	password    name     surname
2     aaa@qq.com    pass2       Mario    Rossi

MULTI
SETNX user:aaa@qq.com:id 2
HMSET users:2:hset id  2  username "aaa@qq.com"  password "pass2"  name "Mario"  surname "Rossi"
LPUSH user:list user:2:hset
EXEC


第3组指令添加以下这条记录:
id    username  	password    name     surname
3     aaa@qq.com       pass3       Tom      Smith

MULTI
SETNX user:aaa@qq.com:id 3
HMSET users:3:hset id  3  username "aaa@qq.com"  password "pass3"  name "Tom"  surname "Smith"
LPUSH user:list user:3:hset
EXEC

127.0.0.1:6379> LRANGE user:list 0 -1  # 查看队列user:list中所有主键信息
1) "user:3:hset"
2) "user:2:hset"
3) "user:1:hset"

如果使用队列List类型存储记录的主键user:list字符串来进行分页:
127.0.0.1:6379> LLEN user:list    # 返回记录总数
127.0.0.1:6379> LRANGE user:list 0  20   # 这是第1页的20条记录主键
127.0.0.1:6379> LRANGE user:list 21 40   # 这是第2页的20条记录主键
127.0.0.1:6379> LRANGE user:list 41 60   # 这是第3页的20条记录主键

如果我们使用队列(列表类型)存储所有的记录:

127.0.0.1:6379> FLUSHALL   # 清空Redis已存储的所有数据
127.0.0.1:6379> LPUSH users:list "{\"id\":1,\"username\":\"aaa@qq.com\",\"password\":\"pass1\",\"name\":\"Bob\",\"surname\":\"Smith\"}"
127.0.0.1:6379> LPUSH users:list "{\"id\":2,\"username\":\"aaa@qq.com\",\"password\":\"pass2\",\"name\":\"Mario\",\"surname\":\"Rossi\"}"
127.0.0.1:6379> LPUSH users:list "{\"id\":3,\"username\":\"aaa@qq.com\",\"password\":\"pass3\",\"name\":\"Tom\",\"surname\":\"Smith\"}" 

如果使用队列List类型存储json记录来进行分页:
127.0.0.1:6379> LLEN users:list    # 返回记录总数  
127.0.0.1:6379> LRANGE users:list 0  20   # 这是第1页的20条记录主键
1) "{\"id\":3,\"username\":\"aaa@qq.com\",\"password\":\"pass3\",\"name\":\"Tom\",\"surname\":\"Smith\"}"
2) "{\"id\":2,\"username\":\"aaa@qq.com\",\"password\":\"pass2\",\"name\":\"Mario\",\"surname\":\"Rossi\"}"
3) "{\"id\":1,\"username\":\"aaa@qq.com\",\"password\":\"pass1\",\"name\":\"Bob\",\"surname\":\"Smith\"}"
127.0.0.1:6379> LRANGE users:list 21 40   # 这是第2页的20条记录主键
(empty list or set)
127.0.0.1:6379> LRANGE users:list 41 60   # 这是第3页的20条记录主键
(empty list or set)
127.0.0.1:6379>

Redis 数据存储形式二 键名:json字串(GET&SET):

第1组指令添加以下这条记录:  
id    username      password    name     surname  
1     aaa@qq.com      pass1       Bob      Smith  

SET user:id  1    
WATCH user:id    
GET user:id    
MULTI    
SETNX user:aaa@qq.com:id 1    
SET user:1  "{\"id\":1,\"username\":\"aaa@qq.com\",\"password\":\"pass1\",\"name\":\"Bob\",\"surname\":\"Smith\"}" 
ZADD user:zset 1 user:1:username    
EXEC 

第2组指令添加以下这条记录:  
id    username      password    name     surname  
2     aaa@qq.com    pass2       Mario    Rossi  
  
SET user:id  2
WATCH user:id  
GET user:id  
MULTI  
SETNX user:aaa@qq.com:id 2  
SET user:2  "{\"id\":2,\"username\":\"aaa@qq.com\",\"password\":\"pass2\",\"name\":\"Mario\",\"surname\":\"Rossi\"}"  
ZADD user:zset 2 user:2:username  
EXEC  
  
  
第3组指令添加以下这条记录:  
id    username      password    name     surname  
3     aaa@qq.com       pass3       Tom      Smith  
  
SET user:id  3 
WATCH user:id  
GET user:id  
MULTI  
SETNX user:aaa@qq.com:id 3  
SET user:3  "{\"id\":3,\"username\":\"aaa@qq.com\",\"password\":\"pass3\",\"name\":\"Tom\",\"surname\":\"Smith\"}" 
ZADD user:zset 3 user:3:username 
EXEC  

127.0.0.1:6379> ZCARD user:zset           # 返回记录总数  
127.0.0.1:6379> ZRANGE user:zset 0  20    # 这是第1页的20条记录主键  
1) "user:1:username"  
2) "user:2:username"  
3) "user:3:username"  
    ... ...   
20) "user:20:username"  
127.0.0.1:6379> ZRANGE user:zset 21 40   # 这是第2页的20条记录主键  
127.0.0.1:6379> ZRANGE user:zset 41 60   # 这是第3页的20条记录主键  
  
我们还可以倒序读取分页需要的主键:  
127.0.0.1:6379> ZREVRANGE user:zset 0  20   # 这是第1页的20条记录主键  
1) "user:60:username"  
2) "user:59:username"  
3) "user:58:username"  
    ... ...   
20) "user:51:username"  
127.0.0.1:6379> ZREVRANGE user:zset 21 40   # 这是第2页的20条记录主键  
127.0.0.1:6379> ZREVRANGE user:zset 41 60   # 这是第3页的20条记录主键
本篇讲解完毕 未经许可,不得商用出版发行!