接上回,按照我们一期项目的需求,昨天我简单设计了数据库里的数据格式。数据库采用的是 Redis ,我把它看成一个远端的数据结构保存设备。它提供基本的 Key-Value 储存功能,没有层级表。如果需要两层结构,可以在 Value 里保存一组 Hashes 。

这是我第一次实战使用 Redis ,没有什么经验。不过类似的设施几年前自己实现过,区别不大。经过这几年,有了 Redis 这个开源项目,就不需要重造轮子了。但其模式还是比较熟悉的。也就是说,是按我历史经验来使用 Redis 。

一期项目需要比较简单,不打算把数据拆分到不同的数据服务器上。但为日后的拆分需求做好设计准备。以后有需要,可以按 Key 的前缀把数据分到不同的位置。例如,account 信息是最可能独立出去的,因为它和具体游戏无关。

用户系统使用 email 来做用户名,但在数据库中的唯一标识是一个 uid 。用户应该允许修改登陆名(用户很可能更换 email)。用户的身份识别是用 id 来定位的。所以,在数据库中就应该有如下几组 Key :

  • account:count id
  • account:userlist set(id)
  • account:email:[email] id

这里,account:userlist 对应的 value 是一个 set ,里面存放了所有存在的 user id 。用于遍历所有的 user 。这个暂时可能用不上,而且当用户量相当大的时候可能有问题。不过暂时不用考虑这么多问题,等以后改进。

account:count 是一个计数器,可以用来生成唯一 id 。

account:email:[email] 用来标示每个注册的 account 的登陆名。[email] 指登陆用 email 地址。

这里,email 内可能也存在符号 ":" ,为了回避这个问题,许多对 email 进行编码。我的方案是,将字母数字 @ . _ 之外的字符编码为 %XX 的形式。用 lua 干这件事情非常简单:

local function _encode(str)
return string.format("%%%02X",string.byte(str))
end function emailEncode(str)
return string.gsub(str,"([^%w_@.])",_encode)
end

当然,解码回来也很简单

local function _decode(str)
return string.char(tonumber(str,16))
end function emailDecode(str)
return string.gsub(str,"%%(%w%w)",_decode)
end

之后,就是 account 下每个 id 的数据:

  • account:[id]:version number
  • account:[id]:email string
  • account:[id]:password string // md5(password..salt)
  • account:[id]:nickname string
  • account:[id]:lastlogin hashes
    • ip string
    • time string
  • account:[id]:history list(string)
  • account:[id]:available enum(open/locked/delete)

其中,密码不想保存为明文。因为任何可能的数据泄露都会导致用户的损失,我也不想任何人看到用户的密码。所以采用 md5(password .. salt) 的风格。

md5 运算前,加一个 salt 后缀,是因为单纯的文本 md5 值也是有数据库可查的。

lastlogin 下保存了用户最后一次登陆的信息,使用了一张 hashes 表,因为这些信息在未来会进一步扩充。

history 保存了用户登陆的所有历史记录,用一个 string 链表记录。

用户删除自己的账户时,不想把数据从数据库删除,只想在 available 下做一个标记。

考虑到数据库内数据结构有可能发生变化,所以加了 version 域做版本标识。


我不想让各种服务可以直接读写这份数据,所以,会单独写一个认证服务器做处理。

认证服务器提供三项服务:

  1. 用户注册

  2. 用户名 密码 认证 (用于 ssl 连接上的 web 服务)

  3. 用户名 密码 挑战式认证 (用于 client 的认证服务)


下面是基本的场景服务用的数据:

  • account:[id]:avatars set(id)
  • avatar:count id
  • avatar:[id]:version number
  • avatar:[id]:account id
  • avatar:[id]:scene string
  • avatar:[id]:available enum(open/delete)
  • avatar:[id]:data hashes
    • name string
    • figure string
  • world:scene hashes
    • [name] id
  • scene:count id
  • scene:[id]:name string
  • scene:[id]:available enum(open/close/delete)
  • scene:[id]:info hashes
    • time string
    • pc number
  • scene:[id]:pc hashes
    • [id] enum[online/offline]
  • scene:[id]:pc:[id] hashes
    • status string

用户账号下可以有许多游戏角色,列表放在 account:[id]:avatars 下。

每个角色也拥有一个唯一 id 。这个 id 原则上和 account id 是独立体系,但是为了人类好区格,avatar:count 的起点和 account:count 不同。

角色所在场景记录一个字符串的场景名 avatar:[id]:scene ,角色的其它各种数据放在一个 hashes 里。

所有的场景索引方在 world:scene 下。如果日后有多个世界,可以采用 world:[id]:scene 。但目前不必考虑。

scene 下面的所有 pc 的在线状态放在 scene:[id]:pc hashes 中,pc 离线也把它的 id 记录在内,只有 pc 转移场景才移除。

每个 PC 的位置状态信息记录在 scene:[id]:pc:[id] 中,第一个 id 是 scene 的 id ,第二个则是 PC 的 avatar id 。

btw. 这是一份草稿,虽然思考不周,但足够满足项目一期的需求。当然许多欠考虑的地方也并非是考虑不到,而是希望尽量简单,以满足一期需求为目的。这个日后修改的代价并不大。


最后吐槽一下 Redis 的 Windows 版。办公室的 Linux 服务器还没有装好,我暂时在 Windows 下做开发。取了一份 google 搜到的 非官方 Redis 的 Windows 版 。为了图方便,使用的是 luajit ffi 去调用 hiredis 的 dll 。一开始怎么都搞不定。建立不了 socket 连接,出错码也取不到。

对比了源代码,发现修改版把 C Struct 结构改了,前面增加了几个域,而我以 hiredis 官方标准来定义的接口。

改好后,能够正确取出出错码了。发现万恶的 Windows socks api 需要调用 WSAStartup 才可以用。而 hiredis 的 Windows 修改版居然没有去调用。让我大费周折才改好,前后折腾了一个多小时。

再吐槽一下 hiredis 的 API 设计,居然依赖 C Struct 的布局。良好的 C 库的接口设计不会这么干的吧。比如 lua ,又比如 zmq 。唉,用这种东西有点小不爽。不过比 C++ 库还是好太多了。

[转至云风的博客]开发笔记 (2) :redis 数据库结构设计的更多相关文章

  1. [转至云风的博客]谈谈陌陌争霸在数据库方面踩过的坑( Redis 篇)

    « 谈谈陌陌争霸在数据库方面踩过的坑(芒果篇) | 返回首页 | linode 广告时间 » 谈谈陌陌争霸在数据库方面踩过的坑( Redis 篇) 注:陌陌争霸的数据库部分我没有参与具体设计,只是参与 ...

  2. Django 博客开发教程目录索引

    Django 博客开发教程目录索引 本项目适合 0 基础的 Django 开发新人. 项目演示地址:Black & White,代码 GitHub 仓库地址:zmrenwu/django-bl ...

  3. 亚马逊云架设WordPress博客

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 这篇文章介绍如何在亚马逊云架设WordPress博客.最强的云,加上最流行的建站工 ...

  4. Padrino 博客开发示例

    英文版出处:http://www.padrinorb.com/guides/blog-tutorial 楼主按 拿作者自己的话说:Padrino(谐音:派骓诺)是一款基于Sinatra的优雅的Web应 ...

  5. Django博客开发实践,初学者开发经验

    python,Django初学者,开发简易博客,做了一下笔记,记录了开发的过程,功力浅薄,仅供初学者互相 交流,欢迎意见建议.具体链接:Django博客开发实践(一)--分析需求并创建项目 地址:ht ...

  6. Django个人博客开发 | 前言

    本渣渣不专注技术,只专注使用技术,不是一个资深的coder,是一个不折不扣的copier 1.前言 自学 Python,始于 Django 框架,Scrapy 框架,elasticsearch搜索引擎 ...

  7. django 简易博客开发 5 markdown支持、代码高亮、gravatar头像服务

    上一篇博客介绍了comments库使用及ajax支持,现在blog已经具备了基本的功能,但是只能发表文字,不支持富文本编辑.今天我们利用markdown添加富文本支持. markdown语法说明: h ...

  8. django 简易博客开发 4 comments库使用及ajax支持

    首先还是贴一下源代码地址  https://github.com/goodspeedcheng/sblog 上一篇文章我们介绍了静态文件使用以及如何使用from实现对blog的增删改,这篇将介绍如何给 ...

  9. django 简易博客开发 3 静态文件、from 应用与自定义

    首先还是贴一下源代码地址  https://github.com/goodspeedcheng/sblog 上一篇博客我们介绍了 django 如何在views中使用templates以及一些常用的数 ...

随机推荐

  1. HTML5 API 是什么

    HTML5 API 是什么 一.总结 1.html5有很多好的api可以用:例如绘图的canvas,获取地理位置的,获取手机电池信息的等等,后面用的时候可以百度 2.html5 API是什么:html ...

  2. 修改shm,oracle11g需要扩大共享内存

    作者:david_zhang@sh [转载时请以超链接形式标明文章] 链接:http://www.cnblogs.com/david-zhang-index/archive/2012/04/26/24 ...

  3. ThinkPHP5.0---方法异常格式

    public function test(){ try{ // 获取到ThinkPHP的内置异常时,直接向上抛出,交给ThinkPHP处理 }catch (\think\Exception\HttpR ...

  4. "C:\Program Files\Internet Explorer\iexplore.exe" -extoff 无加载项启动IE 浏览器打开时全屏模式

    "C:\Program Files\Internet Explorer\iexplore.exe" -extoff 无加载项启动IE浏览器打开时全屏模式

  5. 原生js大总结九

    81.ES6的Symbol的作用是什么?   ES6引入了一种新的原始数据类型Symbol,表示独一无二的值   82.ES6中字符串和数组新增了那些方法   字符串       1.字符串模板    ...

  6. ajax的get请求与编码

    window.onload = function(){ document.getElementById('username').onblur = function(){ var name = docu ...

  7. [Vue + TS] Write a Vue Component as a Class in TypeScript

    Starter app: https://github.com/alexjoverm/Vue-Typescript-Starter Writing Vue components as plain ob ...

  8. C#委托与事件(生动故事)

    [委托] 1,工人Peter按工作步骤向老板报告的程序. 程序: using System; using System.Collections.Generic; using System.Linq; ...

  9. poj 3100 (zoj 2818)||ZOJ 2829 ||ZOJ 1938 (poj 2249)

    水题三题: 1.给你B和N,求个整数A使得A^n最接近B 2. 输出第N个能被3或者5整除的数 3.给你整数n和k,让你求组合数c(n,k) 1.poj 3100 (zoj 2818) Root of ...

  10. 【习题5-1 UVA - 1593】Alignment of Code

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 模拟题,每一列都选最长的那个字符串,然后后面加一个空格就好. 这个作为场宽. 模拟输出就好. [代码] #include <b ...