某游戏应用的redis 数据库结构设计(转)
add by zhj: 搜了一下作者,原来之前是网易的大牛。2011年的文章,有些老了,很多地方可以将string类型键转为hash类型,这样更节省内存,将key聚合在一起,也更简练。
原文:http://blog.codingnow.com/2011/11/dev_note_2.html
接上回,按照我们一期项目的需求,昨天我简单设计了数据库里的数据格式。数据库采用的是 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 域做版本标识。
我不想让各种服务可以直接读写这份数据,所以,会单独写一个认证服务器做处理。
认证服务器提供三项服务:
用户注册
用户名 密码 认证 (用于 ssl 连接上的 web 服务)
用户名 密码 挑战式认证 (用于 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++ 库还是好太多了。
某游戏应用的redis 数据库结构设计(转)的更多相关文章
- [转至云风的博客]开发笔记 (2) :redis 数据库结构设计
接上回,按照我们一期项目的需求,昨天我简单设计了数据库里的数据格式.数据库采用的是 Redis ,我把它看成一个远端的数据结构保存设备.它提供基本的 Key-Value 储存功能,没有层级表.如果需要 ...
- Redis 数据库结构设计
Redis设计参考资料: http://my.oschina.net/fsmwhx/blog/152130 http://my.oschina.net/1123581321/blog/164288 h ...
- Redis数据库云端最佳技术实践
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由腾讯云数据库 TencentDB发表于云+社区专栏 邹鹏,腾讯高级工程师,腾讯云数据库Redis负责人,多年数据库.网络安全研发经验. ...
- redis数据库-VUE创建项目
redis数据库 ''' 关系型数据库: mysql, oracle 非关系型数据库(nosql): redis,mongodb (没有表的概念) key-value mongodb: json 数据 ...
- 运维实践-最新Nginx二进制构建编译lua-nginx-module动态链接Lua脚本访问Redis数据库读取静态资源隐式展现
关注「WeiyiGeek」公众号 设为「特别关注」每天带你玩转网络安全运维.应用开发.物联网IOT学习! 希望各位看友[关注.点赞.评论.收藏.投币],助力每一个梦想. 本章目录 目录 0x0n 前言 ...
- MySQL、MongoDB、Redis数据库Docker镜像制作
MySQL.MongoDB.Redis数据库Docker镜像制作 在多台主机上进行数据库部署时,如果使用传统的MySQL的交互式的安装方式将会重复很多遍.如果做成镜像,那么我们只需要make once ...
- Spring + Jedis集成Redis(集群redis数据库)
前段时间说过单例redis数据库的方法,但是生成环境一般不会使用,基本上都是集群redis数据库,所以这里说说集群redis的代码. 1.pom.xml引入jar <!--Redis--> ...
- 超强、超详细Redis数据库入门教程
这篇文章主要介绍了超强.超详细Redis入门教程,本文详细介绍了Redis数据库各个方面的知识,需要的朋友可以参考下 [本教程目录] 1.redis是什么2.redis的作者何许人也3.谁在使用red ...
- 深入浅出Redis02 使用Redis数据库(String类型)
一 String类型 首先使用启动服务器进程 : redis-server.exe 1. Set 设置Key对应的值为String 类型的value. 例子:向 Redis数据库中插入一条数据类型为S ...
随机推荐
- (3)Smali系列学习之Smali语法详解
数据类型 Dalvik字节码只有两种格式:基本类型和引用类型.对象和数组属于引用类型 语法 含义 V void,只用于返回值类型 Z boolean B byte S short C char I i ...
- EM5-PE2B
1. vocabulary once adv. 一次,曾经 They cocktailed once every week. He once lived in shanghai. twice adv ...
- linux之grub的使用
一.引言 昨天在家里在自己的电脑上安装CentOS7,一切都顺利,结果安装好重启,无法进入win7了,在grub的配置文件也添加了win7的条目,却提示 unkown command chainloa ...
- PHP学习笔记(1)数组函数
1.数组的键值操作函数: $arr = array("小明" => 98, "小红" => 76, "小黑" => 66, ...
- 跟着百度学PHP[14]-PDO-优化驱动
使用方法设置预定义变量 PDO的方法/属性 PDO::beginTransaction — Initiates a transaction PDO::commit — Commits a transa ...
- 从git中更新本地需要填写的正则
<IfModule mod_rewrite.c> Options +FollowSymlinks RewriteEngine On RewriteCond %{REQUEST_FILENA ...
- tp三级联动
<script type="text/javascript">$(document).ready(function(){ $("#province" ...
- python笔记4 - 函数
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性,和代码的重复利用率.你已经知道Python提供了许多内建函数,比如print().但你也可以自己创建函数,这 ...
- 请写出一段表单提交的HTML代码,表单名称为form1,提交方式为post,提交地址为submit.asp
请写出一段表单提交的HTML代码,表单名称为form1,提交方式为post,提交地址为submit.asp 解答: <form name=”form1” method=”post” action ...
- 蓝桥杯 C/C++参考题目 取球概率(数学题,概率)
口袋中有5只红球,4只白球.随机从口袋中取出3个球,则取出1个红球2个白球的概率是多大?类似这样的数学问题,在计算的时候往往十分复杂.但如果通过计算机模拟这个过程,比如进行100000次取球模拟,统计 ...