免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 6504 | 回复: 1

[Redis] Redis 数据库结构设计 [复制链接]

论坛徽章:
0
发表于 2012-02-16 18:49 |显示全部楼层
Redis 数据库结构设计








众所周知,网易游戏的几位技术大拿,叮当云风等前段时间刚刚出去组建自己的团队,开始自己的游戏追求。经常看云风博客的同学可以看到新公司一步步地在发展。而最近技术团队已经开始游戏的正式开发,今天云风就发表了新游戏相关的第2篇技术文章,主要介绍了使用Redis进行游戏数据存储方面的设计。

下面上原文:

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

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

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

用户系统使用 email 来做用户名,但在数据库中的唯一标识是一个 uid 。用户应该允许修改登陆名(用户很可能更换 email)。用户的身份识别是用 id 来定位的。所以,在数据库中就应该有如下几组 Key :
  1. •account:count id
  2. •account:userlist set(id)
  3. •account:email:[email] id
复制代码
这里,account:userlist 对应的 value 是一个 set ,里面存放了所有存在的 user id 。用于遍历所有的 user 。这个暂时可能用不上,而且当用户量相当大的时候可能有问题。不过暂时不用考虑这么多问题,等以后改进。

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

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

这里,email 内可能也存在符号 “:” ,为了回避这个问题,许多对 email 进行编码。我的方案是,将字母数字 @ . _ 之外的字符编码为 %XX 的形式。用 lua 干这件事情非常简单:
  1. local function _encode(str)
  2.     return string.format("%%%02X",string.byte(str))
  3. end

  4. function emailEncode(str)
  5.     return string.gsub(str,"([^%w_@.])",_encode)
复制代码
end当然,解码回来也很简单
  1. local function _decode(str)
  2.     return string.char(tonumber(str,16))
  3. end

  4. function emailDecode(str)
  5.     return string.gsub(str,"%%(%w%w)",_decode)
复制代码
end之后,就是 account 下每个 id 的数据:
  1. •account:[id]:version number
  2. •account:[id]:email string
  3. •account:[id]:password string // md5(password..salt)
  4. •account:[id]:nickname string
  5. •account:[id]:lastlogin hashes
  6. ◦ip string
  7. ◦time string
  8. •account:[id]:history list(string)
  9. •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 的认证服务)

--------------------------------------------------------------------------------
下面是基本的场景服务用的数据:
  1. •account:[id]:avatars set(id)
  2. •avatar:count id
  3. •avatar:[id]:version number
  4. •avatar:[id]:account id
  5. •avatar:[id]:scene string
  6. •avatar:[id]:available enum(open/delete)
  7. •avatar:[id]:data hashes
  8. ◦name string
  9. ◦figure string
  10. •world:scene hashes
  11. ◦[name] id
  12. •scene:count id
  13. •scene:[id]:name string
  14. •scene:[id]:available enum(open/close/delete)
  15. •scene:[id]:info hashes
  16. ◦time string
  17. ◦pc number
  18. •scene:[id]:pc hashes
  19. ◦[id] enum[online/offline]
  20. •scene:[id]:pc:[id] hashes
  21. ◦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++ 库还是好太多了。

来源:blog.codingnow.com

论坛徽章:
0
发表于 2012-02-17 22:49 |显示全部楼层
谢谢分享
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP