i007.cc

i007.cc

优先队列-降维打击

数据库知识与应用

(第一页,开场)

各位同事,下午好,很荣幸能有这样一个机会站在这里和大家分享一些数据库相关的一些知识,虽然说这里是课程,但其实我更希望这里是一次分享和交流的机会,是对我多年关于服务器开发方面,主要是数据库方面的一些经验总结,希望大家能通过这次分享,了解到数据库是什么,在游戏开发中怎么使用,怎么用好,怎样构建性能更为强大、同时又安全可靠可靠的游戏服务器,怎样用数据库技术来提高我们的工作效率和工作质量。希望能起到一个抛砖引玉的作用,谢谢大家(此处应该有掌声)

首先声明一下,我的精力有限,大家的时间也很宝贵,所以呢,在这里我想重点讲一些有点特色的东西,一些不容易在市面上找到的东西,毕竟现在网络这么发达,很多东西如果能很容易的从网上找到,我可能就一带而过,当然,如果大家有疑问,请放心大胆的随时打断我。

 

(第二页,目录)

本次分享的内容呢主要包括五大部分,数据库是什么?为什么要有数据库?数据库的一些基础知识,用实例讲解一下在MMO中的典型应用。最后是我们使用数据库的一些经验分享与坑。

 

(第三页,数据库是什么,抛出问题)

数据库是什么?它有些什么特征?请问下在场的同学们有谁愿意分享下呢?我们放轻松,放心大胆的谈。

 

(第四页,回答数据库是什么)

我不想把这里弄得文绉绉的,弄得满篇幅都是公式、专业术语、深奥难懂的一些概念,让人听了就想睡觉。所以呢,我这里主要总结一下数据库的一些特征,首先它是数据的集合,它把数据归拢到一块儿了,能够以统一的方式访问,有权限控制,未经授权是不能访问的。一个数据库它有一张或者多张表,每张表横向有一个或者多个字段,字段有很多种类型,纵向有多条记录。这都是数据库的特征。

 

(第五页,关系型数据库)

长久以来我们所接触到的都是关系型的数据库(SQL),这类数据库的特征是字段格式固定,使用sql语句进行访问。

这种关系型的数据库多年前比较流行的有Oracle和微软的SQL Server,行业用户用得多(意思就是小公司用得少),在当时对于大量数据的处理能力是比较好的,各种技术支持也比较完备,这类数据库比较重度,庞大,贵,各种贵,License贵,机器贵,人员贵,往往需要客户雇佣专门的DBA,我记得当年还有Oracle DBA的认证,需要学习和考试的。都是要掏钱啊。

mysql是十多年前才火起来的,因为Oracle之类的太贵了,一个License动不动就是几万,几十万,配套服务也要自己买,除了行业用户谁受得了啊,

所以呢,去IOE就来了,就是反抗IBM的服务器,Oracle的数据库,EMC的存储器。不用Oracle可以,但是不用数据库怎么行?数据存哪里?

去Oracle就是要上mysql,开源意味着放心,免费大家都懂了,那我们最关心的是好不好用啊?放心,现在mysql一是行业标准,放心折腾,尽管折腾。

 

(第六页,非关系型数据库)

今年内由于互联网的发展,大家对于海量、高并发的需求越来越强烈,在这种场景下,传统关系型数据库已经不太适合了,这个时候NoSQL出现了。

它的几个特征:不使用SQL作为查询语言,使用键值对进行存储,格式不固定,因为摈弃了关系,可以很容易的通过Hash做垂直扩展,用集群做水平扩展。这个有点意思啊,感觉就是跟sql对着来的,为什么,我后面会专门花点时间谈谈。

 

(第七页,为什么?数据库存在的必要性)

数据库已经进入到我们工作生活的方方面面了。你在银行存取款、你用手机支付、你刷微博,你玩游戏,甚至你在公司打卡,这背后都有数据库的支持啊。对于网络游戏来说,数据库更是不可缺少的基础组件之一。数据库用的不好,我们的产品将无法提供良好的用户体验,轻则卡顿、重则无法提供服务、甚至数据丢失、被串改、泄露等,完全可以说是灾难性的后果。如果说要打一个比喻的话,我觉得数据库就好像是我们生活中的水和电,在大家的认知中,好像幸福感从来没有来自于水和电,我有一套房子,我有一辆好车,我有一个美满的家庭,是的,这些都能带来幸福感,但是大家肯定都知道,没有了水和电,人都不存在了,还谈什么房子、车子?还谈什么幸福感?的确,数据库对于软件系统,对于网络游戏就是水和电,它一定得好用,高效,可靠,否则一切的一切都是0。

 

(第八页,Mud)

回到我们的本职工作上,其实第一代的网络游戏(Mud)也没有用数据库,使用的是文件存储方式保存角色数据,

这在当时并没有太大的问题,毕竟当时玩MUD的人是小众,也没有觉得有太大的问题。

毕竟在Mud游戏中,使用的是纯文字的互动方式,互动频率不高,

那个时代的屏幕也比较小,显示的内容不算多,所以呢数据量是比较少,

网速不快,对服务器性能方面的需求不算强烈。

所以那个时候没有使用数据库,而是用文本或者二进制文件的方式存取数据。

当然我觉得最关键的因素是Mud没有商业化,没人对你数据的正确性,响应的及时性负责

 

(第九页,MMORPG)

但是,时代变了,随着网络游戏越来越火爆,网速越来越快,玩家对于卡顿越来越敏感,我们不得不设计了新的服务器架构。

总的原则就是让专门的模块处理专门的业务。

首先是把dbserver独立了出来,让io操作这类耗时的操作有专门的进程来提供。降低游戏服务器的压力。这样能让游戏服务器支撑更多的在线人数。

前面说了文字Mud游戏是以私服的形式存在的,完全免费的,但是如今的MMO基本上都是商业化的,你收了用户的钱把用户的数据弄丢了,弄错了,那你是要承担责任的。

有个领导跟我们说起当年传奇的一些故事,他说玩家数据丢了,玩家也不来闹,因为当时游戏太火了,厂商处于强势地位。

如今可不一样,流量相当的贵,一个MMO用户都是要用好多好多钱买来的,可不能怠慢了。

所以呢,从现在开始,我们得把用户服侍好了,不崩不卡不刷,让他们玩开心了。

还有万一玩家投诉说他的数据错了,丢了,我们需要需要根据历史记录核实情况,看他说的是否属实,如果属实,我们要为他恢复数据,尽量准确的数据,还要给他补偿。

如果不属实,我们要拿出证据给他看,看看是否他记错了。

所以呢,数据库系统为此而生。

 

(第十页,为什么?)

那,我们来总结一下,为什么要有数据库:

1.要方便的,安全的,可靠地存取数据,这方面文本文件肯定比不上二进制文件的速度快,但是二进制文件又不方便查阅数据

2.从游戏服务器分担尽量多的数据方面的压力,提升游戏服务器的承载

本地、远程访问,支持多种语言,有用户权限控制,访问授权。

所有的数据修改都应该有日志,方便查询,排查故障。

定时备份,更新之前备份,如果发生故障了方便回档。

可以导入导出部分数据,方便进行用户事件处理。

 

(第11页,基础知识)

下面我们将花点时间讲讲关系型数据库的典型代表mysql的一些基础知识

还有非关系型数据库的典型代表redis的一些基础知识。

 

(第12页,MySQL)

MySQL的特点,我觉得最大的就是开源,免费,开源意味着安全,容易让人产生信任感。免费还是值得说一说的,大家知道阿里巴巴现在是全球排名前几的互联网公司,不知道大家还记不记得几年前阿里倡导去IOE,IOE就是ibm的服务器,Oracle的数据库和EMC的存储,为啥要去掉Oracle,它不想用数据库了么?不,它不是不想用,它是用不起了,向阿里那样的体量,如果用Oracle,成本将是一个天文数字,所以有人说用Oracle,就不会出现阿里巴巴。好了,我们现在退回来,MySQL另外的几个特点,使用c语言编写的,又是开源的,经过这么多人这么长时间的打磨,性能,可靠性都不是问题。它支持多种操作系统,支持事务,可以说现在它就是事实上的行业标准。大家把Mysql学好用好是一件非常值得做的事情。这里给大家推荐一个菜鸟教程,非常适合初学者学习,其实不见得是初学者,我也经常去上面看看,查查资料。

 

(第13页,Mysql数据类型)

不管是谈到一门语言,还是一个数据库,它所支持的数据类型都将是我们非常关心的,mysql提供了非常丰富的数据类型,从各种整数、字符串、文本、二进制数据、时间等一应俱全。这里列举了一些我们经常会用到的数据类型,比如一个字节的tinyint,4个字节的int,8个字节的bigint,表示时间的8字节的datetime,4字节的timestamp,占用空间的不同决定了他们所能表达的范围不同。对于字符串,有定长的char(我们用的比较少),也有变长的varchar(用的多),还有blob可以用来存储变长的二进制数据。

 

(第14页,Mysql的使用)

对于mysql的使用方式,标准姿势是命令行,通过mysql这个命令线,输入用户名密码连接到数据库,然后可以使用show databases查看有些什么数据库,然后选择使用其中一个数据库,在就可以查看有哪些表名,然后查看表中有些什么数据,可以使用desc table_name查看表结构,使用show create table table_name查看建表语句,如果想导入数据可以使用source命令。可以使用dump导出成一个文件,导出了之后就可以拿着这个文件在其他的数据库中恢复。

(第15页,Mysql的使用Navicat)

刚才用命令行可能难住了一些初学者,现在都什么年代了,难道就没有可视化的界面么?答案是有,这里我比较推荐大家使用navicat,我自己是命令行和navicat都用,两个都有各自的使用场景,navicat用来浏览数据库非常方便。

(第16页,Redis基本概念)

借着这个NoSQL的出现,我想延展出来多讲一点,很多人,包括我当初,都认为SQL不挺好的么?为啥还有人研究一个NoSQL出来?这帮人是不是傻?而且叫做NoSQL,都对SQL说No了,也许你看百度百科把NoSQL叫成not only sql,意思就是它是sql,不仅仅是sql,意思就是说它是SQL的超集了?SQL有的功能它都有咯?我个人认为这个叫法有点欠妥当,NoSQL就是No SQL,它抛弃了SQL的一些特征,比如范式,哇,多么高大上的一个词,我的数据库遵循了范式设计,好像比你的就高级,我的就高人一等,说起来多么的理直气壮,大家别感到疑惑,这样的情况现在还在发生,公司里面有项目组目前的数据库就是严格遵循了范式设计,但是出现了问题,玩家登录比较慢,也许一般人不会将这两件事情联系起来,认为登陆慢应该是其他的什么原因吧,但是我在这里带领大家思考一个问题,范式设计最大的优势是什么?插入、更新速度快,占用磁盘空间少,等等,就这两个优势,劣势呢?怎么没人讲?每人讲那就我来说吧,没提到的都是劣势,比如读取速度慢,因为数据拆分到多个字段、多张表之后,磁盘从顺序io变成了随机io,我们知道对于磁盘,特别是机械磁盘,顺序io远远比随机io的速度快,即使现在出现了ssd,随机io已经有了很大的提升,但还是有数倍的差距。想明白了吧,就是因为这个原因,没什么值得遮遮掩掩的,NoSQL就是对SQL说No,它抛弃了范式,因为如今大家更在意读取速度,写入速度慢可以开设线程慢慢写,多用些内存而已,反正现在内存便宜对吧,范式还有个优势就是省空间,但是现在磁盘已经很便宜了,空间再大一倍又如何?它抛弃了范式,带来的是读取性能的大幅提升,还能把数据分散在多个进程,多个物理机上,这样做成了分布式,一台机器搞不定的事情我10台机器搞定它,还不行我20台,比你那哼哧哼哧的sql优化效果好得多。

话说到这里,你可能会想,是不是以后都是NoSQL的天下了,我们干嘛还要在这里学习SQL呢?这是我将跟大家讨论的第二个事情,我希望我的学员在这里学到了不仅仅是知识技能,不仅仅学会了SQL,NoSQL,还要学会一种心态,一个思维方式,那就是任何技术都有适用场景,场景不存在了这项技术存在的必要性就要打个问号了,也就是说技术都有生命周期,凡事我们多问个为什么,不要想当然的认为别人都是傻瓜,也别以为我什么问题都能回答你,大胆假设,小心求证,这种心态将让你受益终身。此处是不是应该有点掌声呢?谢谢大家~~~~~

 

好吧,话说回来,下面就来讲讲NoSQL的典型代表Redis,全程是Remote Dictionary Server,远程字典服务器,他是一个高性能的key-value存储系统。作者是一个意大利人,名字我念不出来,他使用c语言开发了redis,redis支持网络访问,它是一个基于内存的可持久化的日志型的数据库。提供了多种语言的api,它的数据类型比mysql更丰富,他有字符串、哈希、列表、集合、有序集合。用起来非常方便,值得一提的是他这个有序集合,基于跳跃表,可以用来当做排行榜,性能非常不错,插入、查找、删除都是Log(N)的。在这里我打一个广告,我们技术中心制作了一个排行榜服务,学习了Redis的一些技术,我们这个排行榜比起Redis的排行榜更优秀,更易用。大家有兴趣可以了解一下,如果大家觉得好也希望大家能多多宣传和使用我们的产品。

同样的,对于Redis,我推荐大家都去看看菜鸟教程。

(第17页,Redis典型应用场景)

我们刚才说到了Redis是一个基于内存的数据库,速度非常快,所以呢它最典型的应用就是用作缓存,另外也可以用作计数器,比如像微博里面的转发次数、点赞次数这类数据如果用redis来实现就会让整个系统非常的高效。另外Redis的订阅功能非常的好用,所以呢有人就用它做一个消息队列。刚才说的有序集合典型用用就是排行榜。同时,现在SNS的快速发展就依赖于redis或者类似NoSQL存储系统的高性能。

(第18页 Redis缓存)

这是一个典型的使用Redis做高速缓存的例子,当客户端发起请求的时候,我们的应用服务器需要访问数据库反馈给客户端,

我们知道存储的速度是比较慢的,如果按照常规方法,应用程序直接访问存储,那我们服务器的高并发性往往很难实现。

而redis使用的是内存,速度非常快,如果在应用服务器和存储之间加上这样一个高速缓存,情况就不一样了。

程序刚启动的时候,缓存是空的,客户端访问的话在redis里面没找到,然后去存储里面找,找到了之后存入缓存,并反馈给客户端。

但是当下一次请求相同数据的时候,缓存就会命中,redis直接反馈数据,就节省了再去存储中访问数据的开销。

我们知道很多应用是读多写少的,这种场景下,redis做高速缓存的效果会非常好,能够大大提高并发。

(第19页,Redis分布式应用)

前面讲过了,Redis抛弃了SQL的一些特征,它能更容易的实现分布式,也就是把服务部署到多台机器上面,这是解决大并发的常用套路。给大家讲解一下大并发是什么概念,大家都玩过微博吧,细心的人可能会发现,只要哪个明星一出轨,哪个明星闹离婚,微博的服务器就挂了,这是一个典型的因为事件导致大并发。当然微博在这方面屡次成为大家调侃的对象,也许有它深层次的原因,具体原因我当然是不得而知了,不过大并发对于很多应用来说会导致雪崩效应,服务器直接当机,影响是致命的。这种情况下,使用redis,用好redis就显得十分的必要。

(第20页 案例分享)

下面我会讲一讲数据库技术在游戏开发中的典型应用,主要是mysql的,前面我们进行了一个问卷调查,大家对于这种案例分享是比较受期待的,所以呢,这里请大家打起精神。

(第21页,Mysql在网络游戏中的典型应用)

我们知道数据库是网络游戏的基础组件之一,网络游戏又对实时性的要求非常的高,所以异步Mysql线程模型是广泛采用的。我们用mysql来存储游戏数据,比如存储用户数据,保证名字唯一性的名字服务器,存储角色数据,存储成就数据和操作日志等。

后面还会讲讲存储过程的使用,还有主从复制和业务分离的使用。

(第22页,AsynMysql组件)

我们知道mysql的api是同步的,文件io是一个比较耗时的操作,为了解决这个问题,在高性能的服务器开发中一般都采用异步的方式操作数据库,这样在mysql操作数据库的时候游戏服务器还可以做一些自己的事情,提高处理效率。这个时候我们需要一个异步的mysql组件。我们知道在网络服务器中,一般主线程里面处理游戏逻辑,比如玩家上线要读取存档,下线要保存存档,这是一个标准的生产者消费者模型,在主线程中生产消息,在db组件开多个线程去消费消息。所以呢,这个组件的模型是这样的,它有两个队列,一个执行队列,主线程把要操作数据库的消息发过来放到这个队列中,db线程获取消息,处理消息,然后把执行结果放入到通知队列中,主线程在循环中不断地查看通知队列是否有消息,如果有就就去执行通知操作。当然一些操作如果不需要通知,可以忽视这条通知消息。

(第23页,存储用户数据)

有了这个线程模型之后,我们就可以创建一张表进行数据库访问了,这是一个典型的存储用户数据的表,我们来看一下这些语句干了些什么,受限了如果已经存在账号表就把它删掉,确保每次表都重新创建。然后就开始了正式的建表,表名为account,使用的存储引擎是InnoDB,现在稍微新一点的Mysql已经默认使用InnoDB了,相比之前的MyIsam引擎,InnoDB使用的行级锁,比起MyIsam的表级锁更高效。字符串缺省的字符集是utf8。

然后看看它的各个字段,平台4字节整数,大家有没有觉得我说的有问题?其实不是4字节整数啦,之前说过了tinyint是一个字节,这是一个很容易犯的错误,虽然并不会导致什么后果。这是一个zerofill,对查询的结果有影响,我目前还不清楚这么写到底有什么用。我们一般来说每个字段都是非空的,然后是用户名称字段使用了varchar类型,存储最长32个字符,注意啦,是32个字符,不是32个字节,因为一个汉字可能占3个字节,这样的话varchar(32)最多可能存储96个字节的数据。

比如现在我们要存储用户数据,每个用户都有一条记录,他的主键是Username,这是一个字符串,我们需要给他指定一个存储字符集,这里用的是utf8,

值得注意的是,如果你不指定字符集,有可能mysql就是用了缺省的latin1作为存储字符集,这个latin1是存不了一些火星文的,这个我们后面会详细讲讲。

用户的一些基本属性会分别存储到各个字段中,比如它是哪个平台的?他身上的货币数量有多少,上次登录的是哪个角色,是不是出于被踢的状态,是不是出于禁言的状态,

整张表使用的是InnoDB,在mysql早期版本缺省的是MyIsam,这两个mysql引擎在性能方面是有比较大的差异的,如今推荐使用InnoDB

zerofill属性

(第24页 名字唯一性)

现在很多游戏中,玩家的名字是唯一的,这时候就需要有一个机制来保证名字的唯一,使用mysql是一个比较明智的选择,可以很方便的完成这个功能。现在我们建一张表,使用的校对字符集是utf8_general_ci,是大小写不敏感的,这样就实现了大写名字和小写名字在一起会认为是重名的功能。就这样实现了存储和判重的功能,是不是很方便?

(第25页 存储角色数据)

还可以创建一个角色的表,一般角色id为64位(8字节)的整数,在mysql中是bigint,也是8字节,

一些游戏里面,一个用户可以对应多个角色,所以角色表中会有一个account字段与前面的用户表对应,

然后就是角色名称,职业,等级,经验等等各种属性,一般情况下,我们为了编程方便,会有一个blob字段,用来存储变长的二进制数据,

因为我们在游戏开发过程中,角色属性随时可能增加,角色属性可能会超过1000个,此时如果设计一张表有1000个字段显然不是很方便,

此时我们就用protobuf把多个属性打包成一个属性,使用blob字段来存储它。这样就不用频繁的调整数据库结构了,开发比较方便。

其实这也是关系型数据库的一个痛点,表结构是固定的,必须预先设置好。非关系型数据库往往能解决这个问题。

主键是角色id,同时建立了一个账号的索引,使用的是btree,平衡树。

同样整张表使用的是InnoDB,字符集为utf8,校对字符集使用缺省。

不知道大家有没有发现,这张表里面的整数全是带符号的,即使我们不用到负值,我觉得这是一个好习惯,我们后面会讲到

推荐大家也使用带符号的整数,即便你用来存储性别,等级,经验等非负整数。因为这种方式更方便计算,我们可以更简单的判断合法性

如果不带符号,我们往往无法区分一个unsigned short里面的40000到底是一个正常值还是一个异常值。

这张表,我们可能会有一个疑问,同样是用来表达时间,为什么有些时候用timestamp,有些时候用bigint?

我想问问现场有人能回答这个问题么?

(第26页 存储成就数据)

下面再讲一个典型示例,就是成就,成就可能是比如等级升到多少级了,第一次完成某个任务,过关了,财富数量到多少了等等,

每一项成就就代表了一串数据,里面有成就编号,当前数值,达到的时间这些属性。

我们发现对玩家来说,成就是一个一对多的关系,所以成就表里面的主键是角色id和成就id组合起来的。

也就是说一个角色可以有多个成就id,多个角色可以有一个角色id。

一般来讲,成就表会在角色登陆的时候加载,所以为了方便查询,成就表会给角色id字段加上索引,以加速访问。

同样的使用InnoDB和utf8字符集。

 

(第27页 存储操作日志)

下面再讲一个操作日志,日志这种类型,对程序来讲只是记录事件,方便分析故障,处理客服事件,一般来讲程序是不读的

比如一个公会日志,它有这样三个字段,id,操作者,这应该是一个角色id,也有可能是工会id,

tick时间,这个事件使用的是整数,我自己觉得用timestamp更好,这样的话在数据库中查询出来的就是形如“20190615-12:30:00”,比较容易阅读。

当然作为设计者,他这样设计也可能有他的原因,比如他是不是认为整数的性能更好,不需要转换,这样服务器的性能会更好一些,或者他并不在意是不是容易阅读,它有方法去转换这个整数成为字符串?

这个我不得而知。

然后就是描述desc字段,这是一个255字节的字符串,用来存储一些信息,可以是谁在哪里做了一件什么事情。

 

(28页,存储过程的使用)

这是一个典型的存储过程,它将一些sql操作组合了起来,在通过用户名称查找账号id的时候,如果有这个账号就返回这个账号id,如果没有就创建一个账号,并返回刚创建的这个账号id。现在在游戏开发中,存储过程用的是比较少的,因为存储过程难以调适,难以扩展和移植,难以进行性能优化。存储过程能做的程序都能做,在阿里巴巴的mysql规范中已经禁止使用存储过程。

 

(29页,总从复制和业务分离)

一个游戏在运行过程中,因为玩家的活动,我们需要不断的读取数据、保存数据,同时另外一方便,作为公司运营方面的需要,需要从数据库中获取一些数据,这个操作可能会是非常耗时的,一条SQL语句可能运行几十秒甚至几分钟。这个时候会给数据库服务器产生很大的压力,可能会导致玩家登录不上,因为数据库卡住了,如何解决这个问题,有一种方法是使用主从复制,主数据库负责角色数据的存储和读取,从服务器用来给我们提供数据分析。两个库分离,各跑各的,这样即使卡顿也不会对玩家的登录产生影响。同时因为主从分离,把一部分的业务放在了从服务器上,减轻了主服务器的压力,也是的整个游戏运行得更为高效。主从复制功能使用了binlog,binlog在主服务器上产生,每当主服务器有数据更新的时候,会把binlog发送给从服务器,从服务器执行了这个binlog之后完成数据同步。这样保证主从服务器数据的一致性。

(第30页,经验与坑)

下面讲一讲我们使用数据库碰到的一些坑和注意事项,我们希望能够分享给大家,避免再次入坑,或者说入坑了之后能立马爬出来。

(第31页,数据保存策略)

我们知道服务器在运行状态下,根据玩家的行为,会在内存中生成各种各样的对象,角色、装备、道具、公会,

这是典型的有状态的服务,我们知道内存中的数据是易逝的,断电之后就没了,为了在服务器重启之后还能恢复数据,我们需要把数据序列化到数据库中,

对于这种内存中的数据保存,我们可能会想当然的做成需要的时候立刻从数据库读取,变化的时候立刻保存到数据库中。

在某些场景下,这种策略是不错的,但是在网络游戏服务器中,特别是角色数据这种方案是有风险的,前面我们说到了一个角色身上可能属性超过1000个,它的变化频率是很快的,

会造成数据保存压力特别大。数据库撑不住啊。

所以呢,我们通常采取定时保存的方法,这样能把db的压力降下来。

但是,这种方案也不是银弹解决一切问题,既然是定时,那么一定会有保存的间隔,也就是说并不是每个状态都会保存下来,这样服务器在崩溃的时候就有短暂回档的风险。

重要操作之后,或者获取到高价值物品之后立刻保存一下。毕竟这种操作不是太频繁,对服务器性能的影响也不会太大。

当然,我在这里再次强调,这种定时保存不是银弹,每种策略的应用场景不一样。

我们在实际开发的时候可以动动脑静想一想,数据变化的频率如果不高,可能立刻保存的性能还会更好一些,对不对?

(第32页,注意事项)

几个注意事项:

1.字段长度,可能大家觉得这太简单了,谁会犯这样的错误,但是我还是想在这里提一下,毕竟当初有人犯过这样的错误,把一长串字符串放入到255字节的varchar里面,结果数据丢失了一部分,造成了玩家刷道具,后果很严重。

还有一个blob字段,最大64k,可能很多人没注意,用来保存角色数据,也发生过回档的事情。

我们在设计的时候应该尽量考虑得长远一些,余量留得大一些。至少预留8倍以上的余量

2.我们使用mysql的c语言api的时候,如果执行了一个查询操作,要记得调用free_result()函数回收内存,

要不然你的程序会发生内存泄露。当然最好是把api封装一下,在析构的时候自动调用

3.读取与保存的顺序,大家可能会以为这不小儿科么?该读的时候读,该写的时候写,为啥还要考虑顺序?

事实上,因为我们的系统会用到异步的技术,读和写的顺序可能并不是我们想象中的那样规整,有可能一个写操作会在几秒钟之后执行,如果这几秒钟之类执行了读操作,就会发生严重的回档问题。

 

(第33页 优化性能)

有时候我们开发的程序在自己的机器上跑得好好的,到了生产服务器上各种问题,最常见的就是卡顿

就是常见的性能问题,这个时候我们可以要求运维提供慢查询日志,通过这个日志可以很明确的知道到底是哪一条SQL操作导致服务器卡顿的,对于排查问题非常有效。

然后就是要合理的设计表结构,预估数据量,如果记录数量过多,mysql性能会下降。我们可以考虑分库分表,把一个表hash一下,分成8个或者更多。这个叫做水平划分。还有一种垂直划分,就是按照功能,把一部分字段放入一张表,剩下的放入另外一张表。这样也能减少单张表大小。提升mysql性能。

然后就是合理增加索引,我们这里知道索引能极大地提升性能,如果你的where语句后面的字段没有带索引,数据量稍微大一些你的数据库都会卡到你怀疑人生,这个时候我们就加索引,基本上立竿见影,立刻就能解决问题

但是我们也要注意一件事情,我这里说的是合理使用索引,不是尽可能多的使用索引,并不是索引越多越好,索引只是在把查询的cpu时间放在了插入的时候。过多的索引会导致插入或者更新速度变慢。所以我们这里说的是合理增加索引。

然后就是必要的地方要使用多线程和异步技术,让游戏服务器在io操作的同时还能做一些事情,提升处理性能。

(第34页 SQL注入)

我们知道sql是基于文本的,我们在拼接字符串的时候,如果不使用escape_string()进行转移,可能会导致用户输入的一些数据和mysql语法混在一块儿,轻则导致sql语句语法错误,严重的导致执行了一些我们并不期望的sql语句,比如数据丢失、错乱,泄露等,后果非常严重。解决方法很简单,就是对于sql语句中的字符串类型的字段都使用escape_string()转义一下,将其中的一些特殊字符比如回车,引号之类的转成另外的字符,避免这个情况的发生

 

(第35页 Mysql字符集)

mysql数据库缺省使用latin1的存储字符集,这个情况下是无法存储中文还有一些火星文的,通常我们在建表的时候特别指定为utf8,然后就是校对字符集,mysql缺省使用的是大小写不敏感的,要特别注意是不是符合你的需求,使用错误的话会导致数据没记上,或者串号。会是非常严重的运营事故。

 

(第36页 校验字符集)

下面举一个例子,有人建表的时候没有指定校验字符集,本来呢功能要求大小写敏感,结果却使用的是系统缺省设置,是大小写不敏感的,导致串号。也就是说一个人的操作记录到其他人身上了,这在游戏运营中是非常严重的事故。当然这是一款老游戏,在架构设计上有些陈旧,玩家与玩家之间使用角色名称这个字符串作区分,使用字符串的都要注意一下这个问题。现在新的游戏设计基本上都使用了角色id作为唯一标识

 

(第37页 校验字符集,名字服务器表结构)

我们也可以利用这种大小写不敏感的特性来做一些事情,最典型的是名字服务器,因为一般情况下我们的邮件地址是大小写不敏感的。

通常名字服务器为了提高区分度,是大小写不敏感的,也就是说A和a会被系统判断为重名,而创建失败。

此时我们可以设定校验字符集名称带ci的,带bin的是二进制,大小写敏感。

 

(第38页 emoji表情)

最后说一说emoji,emoji现在用的越来越多了,iPhone里面都在使用,如果我们的游戏支持,我相信我们的玩家就多了一种选择,不失为一件好事

但是很不幸的,一个emoji表情占用4个字节,但是mysql的utf8最多只有三个字节,这个时候可以使用utf8bm4,有表情了,我们的游戏是不是更加生动了呢?

 

(第39页,感谢)

好了,对于数据库的这份课程基本上就到这里了,我们先后讲解了数据库是什么,为什么要有数据库,如何使用数据库,如何使用mysql和redis,数据库技术在游戏开发过程中的典型应用,我们需要注意的一些坑,如何提升性能、可靠性、安全性。如何使用数据库开发更为优秀的游戏产品,在公司的精品战略的指导下,发挥自己的聪明才智,体现和展示自身的价值,如何与公司做到双赢,我相信今天的课程只是一个起点,以后的路很长,有很多的问题亟待我们去解决,大家要积极努力的拓展自己的各项知识,终身学习,这是我这趟课程最大的期待。谢谢大家!

发表回复