使⽤redis实现关系型数据库表设计
前⾔
最近有⼀个需求,设计⼀款⽂件系统,⽽该⽂件系统会对不同⽂件进⾏不同的转码操作,如rmvb转码成mp4 mp3 与m3u8格式,ppt⽂件转码为swf pdf与h5。
经过调研以后发现,如果以关系型数据库来实现,单表会出现很多冗余字段,如上述两种⽂件,需要设计6个字段来存储相关转码信息,但对特定的⼀种⽂件来说,只利⽤了三个字段。
⽽如果使⽤分表⽅式可以避免冗余,单表结果变复杂,后去扩容修改都不容易,因此决定选择⼀款nosql作为db,⾄于为什么最后选择redis,有多⽅⾯原因,在此就不多说了
在每⼀个栗⼦中,我都会分别使⽤redis⾃带⼯具的命令与php脚本完成(redis⾃带⼯具在为安装⽬录下的redis-cli,命令不区分⼤⼩写)
⾃增主键设计
我们都知道关系型数据库有主键这个概念,常⽤的都是实数int类型的,为了避免重复,这个主键往往还是⾃增的,那么,⽽redis是使⽤
key=>value存储的格式,如果使⽤redis来设计,那么,这个⾃增主键如何来设计呢?
redis提供⼀个整形⾃增命令 incr ,每次执⾏将对应的key增加1 ,并返回对应key增加后的值格式 incr key
php代码如下:
$redis = new \Redis();
$connect = $redis->connect("127.0.0.1",6379);
$id=$redis->incr('key');
每次运⾏都将得到⼀个唯⼀的id,如此我们就完成了⾃增主键的设计。
字段设计
关系型数据库中,字段是预先定⼀好的,也就是说你不能给⼀个没有预先定义的字段中插⼊数据,⽽在redis中则没有这个限制,即⽤即存就是他的优点
⽐如对所有⽂件来说,filename(⽂件名称),filepath(存储路径),filetype(⽂件类型)等字段是共有的,⽽
对ppt⽂件来说额外的字段为mp4 mp3与m3u8格式
对视频⽂件来说,额外的字段为swf pdf与h5.
查询索引设计
对于关系型数据库来说,每个字段,都可以⽤来作为查询条件,⽽对于redis来说,是不⾏的。为什么呢?上⾯说过,redis的字段是即⽤即存的,也就是说,两条数据,
它们的字段数可能是不⼀样的。如此特性决定了,redis的查询字段都需要预先定义,⽽且需要选择那些所有数据均有的字断做为条件,在这个demo中,我们设计4个查询
字断,分别为主键id  ⽂件所属公司(org_id)  ⽂件所属⼈(user_id) ⽂件类型(type) ,当然你也可以设计更多,这⾥只是demo,所以只设计4个,⽅便理解。
主键id我们已经得到了,那么其他⼏个查询条件的该如何来设计呢?举个栗⼦,
⽐如我现在有A,B,C三个公司 (假设三个公司的id为  100,200,300)
张三,李四,王五三个⽤户(user_id分别111,222,333)
视频,资料其他三种类型⽂件(type分别为1,2,3)
在这⾥需要向⼤家讲解下redis的⼀种数据类型----集合,准确点来说是⽆序集合,集合成员是唯⼀的,这就意味着集合中不能出现重复的数据,
Redis 中集合是通过哈希表实现的,所以添加,删除,查的复杂度都是O(1)。集合中最⼤的成员数为 232 - 1 我们使⽤多个集合来保存符合每种条件的纪录主键信息
⾸先假设 A公司的张三新建了⼀个视频⽂件根据⾃增运算后这条记录的id=1
由于是第⼀次插⼊,并不存在查询条件的集合
我们定义如下公司集合的key为      file_org_公司id  ,那么对应A公司key就为 file_org_100 ,然后我们向这个key对应的集合中插⼊这条纪录的id
集合的插⼊命令为 sadd key  value
php代码
$redis->Sadd('file_org_100',1);//1为这条纪录的主键id
对于⽤户集合⽽⾔我们定义key为  file_user_⽤户id  那么对张三⽽⾔  key为  file_user_111
对于类型集合⽽⾔我们定义key为  fiel_type_类型id  那么对于视频⽂件来说  key为 file_type_1
我们分别将三个集合写⼊完毕,后⾯将详细说明,如何使⽤这三个集合来做为查询条件
数据写⼊
到⽬前为⽌,我们已经⽣成了⾃增主键,查询索引,该轮到数据写⼊了,在这⾥需要再介绍redis的⼀种数据结构哈希(hash)
Redis hash 是⼀个string类型的field和value的映射表,hash特别适合⽤于存储对象。
Redis 中每个 hash 可以存储 232 - 1 键值对(40多亿)
哈希是字段与值的的映射关系,所以,字段是不能重复的,⽽我们刚刚得到了要插⼊纪录的id与信息,那么⼀个字断与值的映射关系也就得到了
为id  映射信息由于id是唯⼀的,所以不会存在字断重复的问题集合上⾯张三要写⼊的这条信息
我们已经知道这条信息的id为1    ⽽写⼊信息也已得知假设为(filename,type,org_id,user_id,add_time,
mp4_url,m3u8_url)等
那么我们就可以向这个集合中写⼊数据了使⽤命令 hset key 字段名字断值这个hash的名字我们就叫做file吧(理解成关系型数据库中的表名)
php代码
$redis->hSet('file',1,json_encode(['id'=>$id,'filename'=>$filename,...]));//为了⽅便后⾯的使⽤,可以将id也存进去
到此,我们⼀条完成的数据写⼊就成功了使⽤ hget key 字段名可以查看刚刚插⼊的那条纪录详细命令为hget file 1
file为表明⽽1是$id=1的字段
事务操作
我们知道,关系型数据库中提供⼀种数据看⽀持,叫做事务
事务是⼀个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执⾏。事务在执⾏的过程中,不会被其他客户端发送来的命令请求所打断。
事务是⼀个原⼦操作:事务中的命令要么全部被执⾏,要么全部都不执⾏。
对上述⽽⾔我们⼀共有5个操作    1.获取⾃增主键  2-4.插⼊查询条件集合  5.写⼊数据哈希
如何保证我们的这五步操作都执⾏成功,⽽不会出现数据写⼊但查询条件没有的情况?
redis的设计者早已想到了这点,所以,redis也是⽀持事务的
命令  multi  开启事务    exec  执⾏事务  discard  放弃事务
与关系型数据不同的是,redis放弃事务并不等同与回滚,我们需要⾃⼰收拾烂摊⼦,也就是说,假设1-4步都执⾏成功第五步失败,此时执⾏discard命令 1-4步的修改已经⽣
效,所以我们需要⼿动删除2-4中插⼊的数据⽽⾃增的这个主键,将永远不会被再次使⽤了,但是,如果我们能合理规范键名,数据格式,完全可以避免这类问题,这也是redis
事务快速的原因。
redis支持的五种数据类型php代码
$redis->multi();//开启
$id=$redis->incr('key');//获取⾃增id
$redis->Sadd('file_org_100',1);//插⼊查询条件
$redis->exec();//执⾏命令$redis->discard();放弃命令
注意 php调⽤exec得到的结果是⼀个数组数据元素为每步操作的返回值(0和1)⽽某些操作正确的返回值为0 ⽐如修改替换所以判断执⾏是否成功需要注意执⾏顺序
条件查询
主键查询我上边已经告诉过⼤家了 hget file 1 下⾯我们来说说说如何来进⾏条件查询
再这之前,你可以先联系联系插⼊与事务,尽量多的给数据库中写⼊数据(ps:就⼀条数据怎么条件查询啊?)
我们已经定义好了三个集合⾼中的数学课程中我们因该有个概念叫做交集并集差集⽽redis就能实现关系型数据库实现不了的求交集并集差集
假设我们现在依旧插⼊了10条数据了  id分别为1-10  其中属于A机构5条 B机构的3条 C机构的2条(⾄于具体id是那个机构的我就随便写了)
那么我们现在会有三个公司集合  key分别为  file_org_100,file_org_200,file_org_300,集合中元素分别为(1,3,5,6,7),(2,4,8), (9,10)
其中是张三写⼊的有4条李四3条王五三条同样的key分别为  file_user_111,file_user_222,file_user_333.集合中元素为(1,2,3,4) ,(5,6,7), (8,9,10)
其中有5个视频  2个⽂件  3个其他  key分别为  file_type_1,file_type_2,file_type_3 元素为 (1,2,3,4,5),(6,7),(8,9,10)
那么假设现在的查询条件要查询 A公司的纪录
1.⾸先我们跟根据A公司的集合查询到A公司的⽂件id为 1,3,5,6,7
2.然后我们去hash⾥⾯拿数据上⾯介绍的是取单条数据的⽅法,我在这⾥介绍下⼀次取多个的⽅法
命令  hmget  key  id1 id2 id3 idn    实际就是  hmget file 1 3 5 6 7
对应php代码
$redis->hmget('file',[1,3,5,6,7]);
返回值为⼀个数组然后处理数组元素即可
2.那么现在我们要查询A公司的视频⽂件有哪些步骤如下
1.⾸先我们跟根据A公司的集合查询到A公司的⽂件id为 1,3,5,6,7
2.查询视频⽂件集合为 1,2,3,4,5
3.将两个集合求交集也就是满⾜即是A公司的还是视频⽂件
命令 sinter  第⼀个集合的key  第⼆个集合的key  第n个集合的key      返回结果为交集的值
对应的php代码
$redis->sinter();
这⾥我推荐先获取各个集合,再使⽤php的array_intersect来获取,这样我们就可以再求完交集后控制每次返回的数据数量(分页)array_intersect()
修改与删除
修改与删除相应简单⼀些
修改就是再次执⾏  hset  key  id值⽤新数据将⽼数据覆盖
删除就是将使⽤  hdel ket id值将hash中的信息删除掉然后再将鸡哥查询集合中对应的数据拿出来剔除掉要删除条的id值后重新写⼊,当然也可以srem key  元素值直接删除元素还可以考虑有序集合,这⾥不多说了
持久化存储配置与备份
关于redis的持久化存储⽹上说过很多两种⽅式分别是rdb与aof
我这⾥使⽤的是aof  理解成mysql的binlog    修改f如下为⽌
appendonly yes  打开aof持久化
appendfsync everysec  每秒刷新到磁盘上
no-appendfsync-on-rewrite yes  在⽇志重写时,不进⾏命令追加操作,⽽只是将其放在缓冲区⾥,避免与命令的追加造成disk io上的冲突auto-aof-rewrite-percentage 100  当前aof⽂件⼤⼩是上次⽇志重写得到的aof⽂件⼤⼩的两倍时,⾃动启动新的⽇志重写数值表⽰百分⽐auto-aof-rewrite-min-size 64mb 当前aof⽂件启动新的⽇志重写过程的最⼩值,避免刚刚启动redis时由于⽂件尺⼨较⼩导致频繁的重写
aof是类似⽇志的格式你的每⼀个操作都会纪录下来恢复时会按照顺序再执⾏⼀边所以较rdb⽅式安全但⽤脑袋想也知道log⽂件会越来越⼤的
好在redis提供了⼀种压缩⽇志的⽅式命令  bgrewriteaof  上⾯最后连个配置就是配置这个命令的触发条件的当然你也可以⼿动执⾏
⽰例代码

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。