1. 数据类型
redis是键值对的数据库,有5中主要数据类型:
字符串类型(string),散列类型(hash),列表类型(list),集合类型(set),有序集合(sortedset)。
命令参考 : http://doc.redisfans.com/
可以在本地下载RedisDesktopManager管理db,操作key。
客户端命令flushall 清空整个Redis服务器的数据。
在游戏中做了好友模块、抽奖信息播放、排行榜。
好友模块:前端展示主要有四个标签,我的关注、我的粉丝、黑名单、推荐关注,我的关注中可以给好友赠送友情币,每天赠送次数有上限,且隔天清空,关注数量也有上限,如果互关则有展示。我的粉丝可以接收友情币,粉丝数有上限,隔天清空接收次数。黑名单可以拉黑任意好友,也有取消拉黑功能。推荐关注,获取本服中除自己外所有在线玩家,发送给前端列表进行展示。
本文主要关乎redis数据类型的使用,因此功能部分不在剖根揭底。
推荐列表、关注、粉丝、黑名单中数据主要用set,SADD key member [member ...],遍历的话用SMEMBERS key, 这里使用key 为玩家id。
添加关注的好友要从推荐列表移除用到SREM key member [member ...],取消关注、取消拉黑同样使用srem移除即可。
添加关注,通过SISMEMBER key member,查看是否在好友的黑名单列表中,如果在不能关注。
赠送、接收主要用到hash,使用前先用HEXISTS key field,判断是否存在,没有使用HSET key field value存放赠送接收数据,field 和value分别为赠送的或接收的玩家id和当前时间。接收有数量限制使用HLEN key查看次数。
我的关注标签:SCARD key得出列表数量,SINTER key [key ...]查看是否互相关注。HGET key field查看粉丝是否赠送过友情币,得到时间判断是否跨天。
以上是好友部分主要用到set 、 hash。
排行榜:主要用到sortedset,ZADD key score member [[score member] [score member] ...]添加榜单数据,ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]递减排序,得到前多少名。ZREVRANK key member得到自己的排名。
抽奖信息播放用到list,LPUSH key value [value ...]存抽奖信息, 存记录条数LTRIM key start stop。
2. 存储方式
一种是RDB持久化(原理是将Reids在内存中的数据库记录定时dump到磁盘上),另外一种是AOF持久化(原理是将Reids的操作日志以追加的方式写入文件)。
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
Rdb:对于灾难恢复是不错选择。可以将单独的文件压缩后转移到其他存储介质,保证性能最大化,fork出子程序,由子程序完成持久化工作,避免服务进程执行IO操作。相比AOF数据集比较大时,RDB的启动效率会更高。但如果定时持久化之前宕机则数据将消失,数据集大时服务器停止服务几百毫秒甚至一秒。
Aof:可以带来更大的数据安全性,三种策略:每秒同步(效率高)、每修改同步(效率低)、不同步。采用append机制,写入过程出现宕机不影响之前的内容,日志过大启用rewrite机制,会产生新的文件记录哪些修改命令被执行。
3. 与其他数据库比较
redis :单线程 使用内存的非关系型数据库 保存在内存并持久化到硬盘 功能:发布订阅、主从复制、持久化、存储过程。
memcached: 多线程的带CAS算法 使用内存的非关系型数据库 只有string一种存储方式 多线程服务器为提高性能。
mysql:关系型数据库 建立数据库可包含多个表 语句:select、insert、update、delete、内置函数、存储过程等 支持acid、主从复制等。
MongoDB:使用磁盘的非关系型数据库 数据库中有多个表,表包含多个schema的bson文档 支持map-reduce、主从复制、分片等。
4. 缓存的雪崩以及穿透
缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。
解决办法:一个查询返回的数据为空,把空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
缓存雪崩:如果缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩。
解决办法:可以加锁或队列保证缓存的单线程写,避免大量并发请求;也可以做二级缓存或双缓存策略,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。
5. 注意
判断是否互相关注:
sinter执行复杂度较高的命令,会消耗更多的 CPU 资源,会阻塞主线程,所以使用两个sismember 的&来判断
Redis.use().sismember(FRIEND_INFO + "focus:" + playerId, focusPlayerId) && Redis.use().sismember(FRIEND_INFO + "focus:" + focusPlayerId, playerId);
用scan代替是smembers,smembers一次性扫描所有记录,如果数据量非常大,会影响redis性能。redis2.8版本以后有了一个新命令scan,可以用来分批次扫描redis记录,会导致整个查询消耗的总时间变大,但不会影响redis服务卡顿,影响服务使用。