什么,用redis替换mysql?疯了吧!
拒绝总得有理有据:
- redis作为内存数据库在体量上是容纳不了磁盘数据库的,完全替换是不可能也没必要
- 业务服务通常逻辑复杂,如何实现复杂的sql查询。磁盘数据库一句sql查询搞定的事,我得准备多少key/value的数据关系才能实现查询。
- 以上都不是问题的情况下,一旦发生数据变更,key/value的数据关系如何同步
以上三个问题足以让我对redis替换mysql望而却步了。如此多的业务关系时时变更,同样作为内存数据库,我宁愿选择mongodb,至少它在某种层面上支持类sql的条件查询。即使只有两三张表的数据的业务服务,完全用redis替换mysql的工作量也不能小觑。完全手写是不可能的,所以任务要求是通过ezorm自动生成代码的方式来实现redis替换mysql。难度再度提升,代码必须通用化才能用工具生成。
其实,到我开始编写代码的那一刻,我都不知道支持redis的orm会做成啥样。当然,还是从最基础的增删改查做起(这是大部分orm提供的主要功能)减少程序员重复的编码工作。
当然我也不是完全从零开始,我已经有了基础ezorm工具,它已经支持 mssql、mysql和mongo了。所以,增加redis支持是理所当然的了。当然如果只是对象的增删改查,难度到也不大。
第一版本
不同于关系型数据库,增删改查的接口是:insert/update/delete/select,redis的增删改查只需要提供两个接口就好了,set/get。为了简化代码,我提供了一套object接口,只要实现了object接口的对象可以直接通过反射操作进行对象的redis存取操作,这样一来,我只需要让对象实现接口函数即可。这就是我第一版做的事情。
当然这个版本,我还提供了一个关键的对象定义属性,就是 dbs 属性。之前的orm版本中,一个对象定义只能存在一个db属性定义,对于redis对象而言,完全可能和关系型数据对象具有相同的结构。如果再去生成一套对象结构定义显然是不合理的。
第一个版本还实现查询接口,支持按索引查询。虽然粗糙了点,至少看起来和关系数据库提供的接口接近了。
第二版本
很明显,在第一版本中没有发挥orm的优势就自动生成代码将一切影响效率的操作迁移至编译阶段。而对象的反射显然除了加快了开发进度,简化代码。在关键的在执行效率上大打折扣。于是乎第二版本我移除了所有反射操作,通过orm模板的关键特性自动生成对象字段的读写操作。于是生成的代码就变成了以下形式:
第三版本
虽然在前两个版本,我就针对redis提供的数据结构进行功能划分:
通用对象存储(object)
对象关系存储(relation)
其中,对象的存储在redis里可以使用以下结构
<key/value> pair 用于json对象存储
hash用于需要独立设置字段的对象
而对于对象关系的存储redis提供几种类型都可以支持:
<key/value> pair
set 集合结构
zset有序集合结构
geo地理位置结构
list列表结构
其实在前两个版本中关系对象(relation)的存储该如何定义一直是件困扰我的事,既需要能过通过某种形式定义出来,又不能让用户随意定义。
通用对象的定义用户是可以完全自定义对象的列(Field)的,但关系对象(Relation)如何定义呢?先看一下,redis中几种类型的具体操作命令:
前两个版本的做法是同样利用之前通用对象的定义方法让用户定义字段,但其实redis对set/zset/geo/list都有严格的特殊要求,除了key和value外,在zset中增加的score,在geo中增加了longtitude和latitude。前两版本的做法就是去验证用户定义关系对象的字段数与字段名称,显然是很傻的做法,在实际应用中必定坑洼不断。
所以在这个版本中对关系对象类型的定义进行了抽象,既然所有关系对象结构均有key和value字段,而key字段显然都是string类型,我只需要增加一个新的定义属性:valuetype类型。通过这个属性结合模型的storetype来定义关系对象即可:
有了valuetype后,我考虑到如果能够直接通过关系对象找到对象岂不完美,因为value对于使用者而言还必须进行一次转化。所以这时我又增加了modeltype类型,对于关系对象的定义中valuetype刚好是modeltype定义的主键类型的话,那么直接在生成代码的时候就生成相应接口岂不妙哉。
第四版本
其实完成了第三个版本后,我就跃跃欲试想小试牛刀了。但是问题也就来了,主要是:
- 基础对象的数据准备
- 关系对象的数据准备
同时,虽然我可以定义以上两种对象,但是实际如何替换现有查询语句我仍然一头雾水。基础对象的查询很简单,只要有id就可以查询和关系型orm操作接口类似。
基础对象的数据准备也很简单,对象只有dbs同时支持mysql和redis,就可以通过mysql对象提供的查询接口将所有对象取出来,再枚举调用redis对象的set接口就可以完成基础对象的数据准备。
而对于关系对象的数据准备,肯定最好的方式也是从数据库直接导入最直接。所以就想到了一个新的属性就是:importSQL属性。
这个属性一度让我兴奋不已。因为它不但帮我实现了对象的数据准备问题,还帮我解决了对象的数据同步问题。增加这个属性后,对象相应增加了以下接口:
现在,可以真正开始使用ezorm的redis功能了,看看实际使用的代码吧。
总结
接下来,我们应该做什么呢?请关注新项目redis-orm. 我们将完成索引的模拟。提前告知下模拟索引的功能: