• 欢迎访问我的博客

浅谈 Redis 数据库的键值设计

redis iJiaxin 1年前 (2017-07-12) 577次浏览 1个评论 扫描二维码

丰富的数据结构使得 redis 的设计非常的有趣。不像关系型数据库那样,DEV 和 DBA 需要深度沟通,review 每行 sql 语句,也不像 memcached 那样,不需要 DBA 的参与。redis 的 DBA 需要熟悉数据结构,并能了解使用场景。

下面举一些常见适合 kv 数据库的例子来谈谈键值的设计,并与关系型数据库做一个对比,发现关系型的不足之处。

用户登录系统

记录用户登录信息的一个系统, 我们简化业务后只留下一张表。

关系型数据库的设计

user_id 表的主键,name 表示用户名,login_times 表示该用户的登录次数,每次用户登录后,login_times 会自增,而 last_login_time 更新为当前时间。

REDIS 的设计

关系型数据转化为 KV 数据库,我的方法如下:

key 表名:主键值:列名

value 列值

一般使用冒号做分割符,这是不成文的规矩。比如在 php-admin for redis 系统里,就是默认以冒号分割,于是 user:1 user:2 等 key 会分成一组。于是以上的关系数据转化成 kv 数据后记录如下:

这样在已知主键的情况下,通过 get、set 就可以获得或者修改用户的登录次数和最后登录时间和姓名。

一般用户是无法知道自己的 id 的,只知道自己的用户名,所以还必须有一个从 name 到 id 的映射关系,这里的设计与上面的有所不同。

这样每次用户登录的时候业务逻辑如下(python 版),r 是 redis 对象,name 是已经获知的用户名。

如果需求仅仅是已知 id,更新或者获取某个用户的最后登录时间,登录次数,关系型和 kv 数据库无啥区别。一个通过 btree pk,一个通过 hash,效果都很好。

假设有如下需求,查找最近登录的 N 个用户。开发人员看看,还是比较简单的,一个 sql 搞定。

DBA 了解需求后,考虑到以后表如果比较大,所以在 last_login_time 上建个索引。执行计划从索引 leafblock 的最右边开始访问 N 条记录,再回表 N 次,效果很好。

过了两天,又来一个需求,需要知道登录次数最多的人是谁。同样的关系型如何处理?DEV 说简单

DBA 一看,又要在 login_time 上建立一个索引。有没有觉得有点问题呢,表上每个字段上都有素引。

关系型数据库的数据存储的的不灵活是问题的源头,数据仅有一种储存方法,那就是按行排列的堆表。统一的数据结构意味着你必须使用索引来改变 sql 的访问路径来快速访问某个列的,而访问路径的增加又意味着你必须使用统计信息来辅助,于是一大堆的问题就出现了。

没有索引,没有统计计划,没有执行计划,这就是 kv 数据库。

redis 里如何满足以上的需求呢? 对于求最新的 N 条数据的需求,链表的后进后出的特点非常适合。我们在上面的登录代码之后添加一段代码,维护一个登录的链表,控制他的长度,使得里面永远保存的是最近的 N 个登录用户。

这样需要获得最新登录人的 id,如下的代码即可

另外,求登录次数最多的人,对于排序,积分榜这类需求,sorted set 非常的适合,我们把用户和登录次数统一存储在一个 sorted set 里。

这样假如某个用户登录,额外维护一个 sorted set,代码如此

那么如何获得登录次数最多的用户呢,逆序排列取的排名第 N 的用户即可

可以看出,DEV 需要添加 2 行代码,而 DBA 不需要考虑索引什么的。

TAG 系统

tag 在互联网应用里尤其多见,如果以传统的关系型数据库来设计有点不伦不类。我们以查找书的例子来看看 redis 在这方面的优势。

关系型数据库的设计

两张表,一张 book 的明细,一张 tag 表,表示每本的 tag,一本书存在多个 tag。

tag 表自关联 2 次再与 book 关联,这个 sql 还是比较复杂的,如果要求即 ruby,但不是 web 方面的书籍呢?

关系型数据其实并不太适合这些集合操作。

REDIS 的设计

首先 book 的数据肯定要存储的,和上面一样。

tag 表我们使用集合来存储数据,因为集合擅长求交集、并集

那么,即属于 ruby 又属于 web 的书?

即属于 ruby,但不属于 web 的书?

属于 ruby 和属于 web 的书的合集?

简单到不行阿。

从以上 2 个例子可以看出在某些场景里,关系型数据库是不太适合的,你可能能够设计出满足需求的系统,但总是感觉的怪怪的,有种生搬硬套的感觉。

尤其登录系统这个例子,频繁的为业务建立索引。放在一个复杂的系统里,ddl(创建索引)有可能改变执行计划。导致其它的 sql 采用不同的执行计 划,业务复杂的老系统,这个问题是很难预估的,sql 千奇百怪。要求 DBA 对这个系统里所有的 sql 都了解,这点太难了。这个问题在 oracle 里尤其严 重,每个 DBA 估计都碰到过。对于 MySQL 这类系统,ddl 又不方便(虽然现在有 online ddl 的方法)。碰到大表,DBA 凌晨爬起来在业务低峰期操作,这事我没少干过。而这种需求放到 redis 里就很好处理,DBA 仅仅对容量进行预估即可。

未来的 OLTP 系统应该是 kv 和关系型的紧密结合。

转自:http://www.oschina.net/question/12_27517


iJiaxin 个人博客 , 版权所有丨如未注明 , 均为原创丨转载请注明原文链接:浅谈 Redis 数据库的键值设计

喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
(1)个小伙伴在吐槽
  1. v5
    明天你好2017-07-12 18:43 Reply
150 queries in 0.516 seconds, using 11.27MB memory