未加星标

如何快速搭建数据服务

字体大小 | |
[数据库(综合) 所属分类 数据库(综合) | 发布者 店小二04 | 时间 2016 | 作者 红领巾 ] 0人收藏点击收藏

今天的主题还是与服务端有关。

上一篇服务端文章「面向中间件的开发模式」中,小说君提到接下来会介绍「服务化」以及「服务划分」。

但是,考虑到不少同学对服务没有一个具体的概念,我们今天先来聊聊如何搭建数据服务。

何谓数据服务?

数据服务通常是一整套解决方案,包括:

一个或一组后端节点,向应用层提供数据访问的功能,其网络环境通常与客户端可以直连的节点物理隔离。

开发规范与数据模型抽象,无缝接入应用节点中的业务逻辑。

接下来,我们分别就上述两部分展开讲一下。

数据服务,物理结构上表现为一个或一组节点。

具体是一个还是一组,还要结合第二个特点来看:向应用层提供数据访问功能。

所谓数据访问,自然包括数据的增删改查与持久化。

很久以前我们写web应用,CGI+mysql,就能处理大部分需求,简单粗暴。

这种情况下,mysql如果不做水平拆分,实际上就只有一个物理节点,扮演了数据服务的角色。

对应地,如果对mysql做了分库分表,那么,

数据服务=mysql节点集合+针对表id如何找分库分表的逻辑集合

后来的发展大家就都知道了,服务器内存越来越不值钱,大家改用memcache,改用redis。memcache和redis跟传统意义上的「数据库」概念重合度并不高,实际上应该算是缓存服务器。

同时,还会选择性地搭配一些数据持久化方案,比如存硬盘,比如存到mysql,或者leveldb。

如此一来,

数据服务=缓存服务器+落地机制+应用层对缓存服务器的API集合

数据服务的方案多种多样,项目不同、人员技术背景不同、架构决策者的个人喜好不同,都会导向不同的方案选型。

不同的方案之间没有本质的优劣之分,只要持续迭代下去,互相借鉴,往往殊途同归。

那么,本篇文章小说君不再纠结技术方案如何确定,打算随便挑两个自认为比较熟悉的,搭一套最简单的数据服务。

redis是一个这几年一直都比较热门的数据结构服务器。游戏行业就不用说了,很多成功游戏都在用。非游戏行业也有微博和人人网这种现在(或曾经)访问量比较高的网站在用。

redis提供了基本的持久化机制,rdb和aofrewrite,前者可以简单理解为定时dump,后者是写event log。但是两者都不能算是完善的持久化方案,我们打算用更可靠的mysql来实现。

再回过来看数据服务。

数据服务需要解决什么问题?

不少同学并没有接触过游戏服务端,所以小说君举一个与游戏无关的比较常见的例子。

考虑一个订单系统,假设一个订单的状态有待支付、已支付未校验、待出货、完成,这么些状态(下文简记为状态A、B、C、D)。

对于客户端发起的大部分业务请求,应用服务器都需要借助数据服务拿到订单状态,而数据服务也会定期将订单状态做一个持久化,以方宕机导致数据丢失。

抽象一下,就如之前所说,我们的数据服务需要支持应用层的数据访问请求,以及能实现数据的持久化。

技术选型确定了,接下来讲讲具体怎么做。

数据访问这块我们用一些redis的client lib已经能够实现基本的增删改查需求。而且缓存服务器的查询语言简单,通常可以直接用逻辑描述,不需要像直接用mysql一样在应用层暴露SQL语句,或者更蛋疼地去写存储过程。

比如一句插入key可以直接写为redis.hset(a,b)。

持久化这块,我们还是先回顾下redis的rdb与aofrewrite机制,虽然这两个机制联用能实现基本的持久化需求,但是有几个弊端:

我们希望把redis当做纯粹的缓存服务器,但是redis的两个不太成熟的持久化机制会拖慢性能,甚至卡IO。

redis的二进制rdb文件要想用可视化工具查询、分析非常困难,要重新开发工具读取、建索引、识别查询语义,以及各种,非常麻烦。

redis的灾备机制不太完善,不适合做持久化存储。

鉴于此,我们会用mysql来替代redis自带的持久化机制。

最初的原型是一个redis实例,一个mysql实例,一个数据同步服务器(下文简称DbSync)。

这是最简单的实现,不过远不能称为「工业级」的解决方案。

由于篇幅原因,高可用相关话题会留在下篇文章探讨。如此一来,我们可以高度简化要实现的数据服务模型。

这样,只要引入sharding(切片),我们就能轻易地做分布式扩展。

订单数据按订单id,sharding到不同的redis节点。由于不再需要考虑高可用,只要同一个订单id的数据只会出现在唯一的redis实例中,就能保证数据的强一致性。

DbSync的作用很简单,就是定期地、异步地从redis上拿出脏数据,存到mysql中。

判断脏数据的方法有很多,一种比较简单的实现是:hook住应用节点修改数据的逻辑,在修改逻辑执行之前(或者利用redis的事务机制),插入一条更新记录到redis实例的元数据结构(比如可以用一个zset)中。由于数据落地行为是幂等的,可以保证只要更新发生了,DbSync就会至少执行一次落地操作。

可以画一个简单的架构图:


如何快速搭建数据服务

现在,我们的数据服务满足了文章开头提到的第一个特点,现在只能算是「可以用」。互联网开发讲究小步快跑,快速迭代,其实很多产品一开始都会采用如此简单的模型,之后需求上来了再做扩展。

在本文中,我们还是希望能在应用层中写出更优雅的代码,比如不要这里一句redis.hset,那里一句redis.hdel;以及希望能摆脱低级的重复代码,比如要给订单数据结构加个字段,就得在存储的时候加一行,读取的时候加一行,甚至DbSync的持久化逻辑都要改。

所以,我们继续看文章开头提的第二个特点:

数据服务,需要提供开发规范与数据模型抽象,无缝接入应用节点中的业务逻辑。

前述几个「痛点」,其实归根结底是同一个问题:那就是数据服务首先是一个服务,那么就要有服务级别的复用抽象程度。

企业应用开发中有ORM的概念,Object-Relation-Mapping,对象模型到关系模型的映射。不同语言中也有类似的库,比如JAVA的hibernate,.Net的EntityFramework,python的SQLAlchemy等等。

试想,如果我们在业务层操作的完全是对象,而对对象如何映射成redis中的物理结构key、对象如何映射成mysql中的物理结构表完全不知情,那写业务的时候就可以专注于业务。

而且,web中的业务对象通常远没有企业应用中业务对象那么复杂,我们在自己开发映射层的时候不需要考虑复杂的跨表引用关系。

如此一来,我们借鉴ORM的思路设计的对象-kv-关系映射系统就完全是业务无关的。

也就是说,数据服务是普适的,而不同的业务可以用不同的数据结构描述自己的领域模型,然后数据服务的配套工具会自动生成数据访问层API、redis中cache关系以及mysql中的table schema。

同样的数据服务,在项目A中引用并定义了Player结构,就会自动生成LoadPlayer的API;在项目B中定义User同理生成LoadUser的API。

了解了原理之后,实现上就简单了,最关键的还是思路的转换。

简单贴一段代码,定义一个model:

[Serialize] [MainCache(CacheName = "account")] [Persistence(DbTableName = "mem_account")] public class Account
{ [Key] public string User; [Field]
public ulong Pid; [Field] public ulong[] TestArr; [Field] public Dictionary<ulong, uint[]> TestDict; [Field] public List<List<uint>> TestList; }

针对不同数据层,生成各自的访问API,贴一下redis的updateAPI(当然还可以生成update个别字段的API):

public void UpdateAccount(Account account) { var ctx = adaptor.Update(dataId); ctx
.WriteField("User", account.User)
.WriteField("Pid", account.Pid)
.WriteField("TestArr", account.TestArr)
.WriteField("TestDict", account.TestDict)
.WriteField("TestList", account.TestList)
; updateQueue.Add(ctx);
}

WriteField方法内部会对基本的数据结构做一次序列化,打包为字节流之后想必各位已经之后接下来该怎么做了。

框架的具体实现通常会由于各种原因做一些hack,比如为了让应用层写起来更优雅,就需要加入额外的抽象层等等,所以小说君就不贴代码了,有兴趣的同学可以后台回复 「 数据 」 ,参考下实际的代码。

实际上,数据服务除去缓存基础设施的部分,都属于外围机制。

在有些设计中,缓存服务器与业务节点之间还有中间层。这种中间层的意义通常是做一次RPC到具体缓存协议API的转换,但是在前文所述实现中,由于已经自动生成了数据访问API,因此不需要这种中间层。

到目前为止,我们的数据服务已经基本可用,而且能让业务层以一种比较优雅的方式来写数据增删改查逻辑。但是,我们在设计过程中规避了一些redis sharding会遇到的典型问题,比如状态不一致。

而且,一个工业级的数据服务,需要在可用性、一致性、容错性之间做权衡,由于篇幅,我们并没有这方面的考虑。

这些都留待后续的服务端系列文章中再做探讨。

公众号:gamedev101「说给开发游戏的你」新开通,专注日常开发技术分享,如果对小说君的文章感兴趣,欢迎长按下方二维码识别关注或分享给你的朋友。


如何快速搭建数据服务

本文数据库(综合)相关术语:系统安全软件

分页:12
转载请注明
本文标题:如何快速搭建数据服务
本站链接:http://www.codesec.net/view/481152.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 数据库(综合) | 评论(0) | 阅读(40)