MySQL是一个更好的NoSQL数据库。当考虑到NoSQL的使用案例,比如对Key/Value键值存储来讲,MySQL在性能、易用性和稳定性方面更有意义。MySQL毕竟是一款成熟稳定的产品,在互联网上有大量的在线教程,范围从操作到失败案例,从主从复制到其它不同模式的应用,不一而足。基于这个原因,MySQL相比其他新兴并没有经过多年洗礼的NoSQL来讲,确实有一定的优势。
近些年来,NoSQL慢慢成为了主流。许多开发者把这些NoSQL数据库,比如MongoDB、Cassandra、Redis或者Hadoop等,当作他们构建应用的数据库首选,而把老旧的传统数据库废弃不用。
选用NoSQL数据库,经常是建立在其不实或者夸大的宣传,和对传统关系型数据库性能不佳的假定上。开发者在选择数据库时,会特别看重操作成本,以及稳定性和成熟性。更多的不同NoSQL和关系型数据库的局限对比,可以参考Aphyr上Jepsen的一些列文章。
这篇文章会解释给大家为什么我们发现MySQL对于键值存储场景来说,比大多数专有NoSQL引擎还要好。另外,本文也会提供给大家在MySQL中如此应用的参考。
当用户点击一个链接到Wix网站时,他/她的浏览器会发送一个带有网站地址的HTTP请求给Wix的服务器。无论是自定义域名(比如:domain.com)请求一个Wix的优质地址,还是一个在Wix域名下的免费的子域名(比如:user.wix.com/site),这个HTTP请求都会发生。服务器不得不在网站地址中执行键值查询来处理用户对某个地址的请求。我们用路由来表示URL,进行下面的讨论。
路由表用于将站点地址解析为一个站点对象。因为站点可以暴露在多个路由中,所以是多对一的关系。一旦网站被发现,则应用将其加载以备使用。站点对象本身具有复杂的结构,其中包涵两个子列表,列表中的对象表示了站点使用的不同服务。下面是一个对象的模型图,假设使用了标准SQL数据库和归一化模型:
当使用传统的归一化模型更新网站时,我们需要一个事务来更新多表,以确保保持数据一致性(注意,这里的事务使用的是数据库级别的锁来避免并发写,有些时候用来避免在受影响的表中并发读)。继续使用这一模型,我们可能会有每张表的串行键、外键以及在路由表中对于URL列的索引。
然而,这里有几个使用归一化模型带来的问题:
-
数据库锁限制了对表的访问,所以在高吞吐量的场景下,我们的性能可能会受一些影响
-
读取对象涉及几个SQL查询(在本例中使用了4个)或联接——再次影响了延迟
-
串行键施加锁和再次限制写入吞吐量
这些问题相当于限制了我们使用MySQL(或者其它任何SQL引擎)时的吞吐量和并发。因为这些短板,外加事实上我们需要的是键值存储,所以许多开发者倾向于使用NoSQL作为解决方案以提供更高的吞吐和更多的并发,甚至是更好的稳定性、一致性和高可用。
在Wix,我们发现,当我们“有创造性的”使用MySQL作为键值存储时,能够提供比上面提到的使用归一化数据模型或者其它大多数NoSQL数据库引擎更好的性能。简单的使用MySQL来作为NoSQL引擎,我们现有的系统,无论是在伸缩性、吞吐量、并发和延迟指数上,相较NoSQL都具有令人惊艳的性能。下面列举一些数据:
-
横跨三个数据中心的异地多活
-
吞吐量在200,000RPM的数量级
-
路由表记录在100,000,000的数量级,10GB的存储
-
站点表记录在100,000,000的数量级,200GB的存储
-
读延迟平均在1.0-1.5毫秒(事实上,在一个数据中心,延迟为0.2-03毫秒)
值得注意的是,对于绝大多数的键值引擎,无论是开源数据库还是云数据库,1.0毫秒的延迟被认为是相当令人惊讶的水平。然而我们用MySQL就实现了这一壮举(考虑到还使用了基本的SQL引擎)
这是我们实际使用的模型:
任何未被当做查询条件的字段,都被放置在一个单一的blob字段(上面的site_data字段)。其中包含子对象表,和其他表本身的字段。另外注意,我们并未使用串行键,相反的,我们使用了一个varchar(50)的字段,用于存储客户端生成的GUID值——关于这部分,详见下一章节。
下面是我们使用的一个查询,具备高吞吐的同时,还具备了低延迟:
工作原理是这样的,首先使用唯一索引在路由表上执行查询,应该尽的到一条记录。接着使用这条记录的主键,在站点表执行查询,返回的记录也是一条。
嵌套的查询语法可以确保这两个 SQL查询仅在数据库记录中查询一次。
上面的结果显示,平均延迟在1毫秒以下,并且在高流量和高更新率的情况下能保证一致性。虽然没有使用事务,但是update却是半事务的。因为我们在一条插入语句中输入了完整的网站地址,直到进入路由表,这条记录才会被发现。
所以如果我们首先输入了站点网址,然后是路由,我们就能确保有一个一致性的状态,即使是在最边缘的情况下,我们也仅仅是在站点表中有一些“孤儿”数据。
使用从上面例子(或者在Wix的其它案例)中的到的经验,我们简要的列举出了一个使用MySQL当做NoSQL引擎使用的参考。
当使用MySQL当做NoSQL引擎使用时,首先要记住的一点是避免使用数据库级别的锁或者是复杂查询:
不要使用事务,因为事务引入了锁。相反的,使用应用来控制事务
不要使用串行键。串行键引入了锁和其它敷在的启动配置
使用客户端生成唯一键,我们使用了GUID
当为优化读设计模型时,鞋面是额外的一些经验仅供参考:
-
不使用归一化模型
-
所有的字段在被索引时才有必要存在。如果字段在查询时不需要,则将其放到一个blob/text字段中(如JSON或者XML)
-
不要使用外键
-
设计你的模型,来确保查询时仅读取单独的一行
-
不要在表上使用alter命令。修改表的命令引入了锁和停机时间,相反的,尝试使用实时迁移
当查询时:
-
使用主键或者索引查询记录
-
避免使用表连接
-
避免使用聚合函数
-
尽量在从库上执行较为复杂的查询(如BI,数据研究等),避免在master数据库上执行上述操作
我们打算另外写一篇文章来介绍有关实时迁移和应用控制事务的更多内容。
总结
最值得在这篇文章中看到的是如何打破思维尝试不同的思考。使用MySQL来当做NoSQL引擎,看起来是不错的,虽然MySQL最开始并不是为此而设计的。本文中演示的,是一个使用MySQL而不是NoSQL引擎来构建键值访问。在Wix,MySQL是我们的键值存储场景的选择是因为操作简单、使用简单,并且MySQL本身有极好的生态。此外,MySQL还能提供额外的低延迟、高吞吐和数据一致性保证,即使不能超越其它NoSQL引擎,至少也算打个平手吧。
来源:解放号杰微刊
译者:张帆
本文来自云栖社区合作伙伴"DBAplus",原文发布时间:2016-06-23