摘要:

搭建了服务器环境 有了客户端 我们来假想下以下应用场景:
我们简单举个实例来描述下Hash的应用场景,比如我们要存储一个用户信息对象数据,包含以下信息:
用户ID,为查找的key,
存储的value用户对象包含姓名userName,密码校验值passwordMD5,手机号码telephoneNum , 微信名称wechatName 等信息.
将如何在redis中存储和操作?

搭建了服务器环境 有了客户端 我们来假想下以下应用场景:

我们简单举个实例来描述下Hash的应用场景,比如我们要存储一个用户信息对象数据,包含以下信息:
           用户ID,为查找的key,
           存储的value用户对象包含姓名userName,密码校验值passwordMD5,手机号码telephoneNum , 微信名称wechatName 等信息.

   如果用普通的key/value结构来存储, 可以使用这种方式来存储:
(1)   第一种方式将用户ID作为查找key,把其他信息封装成一个对象以序列化的方式存储,需要一次次地发送和返回。
           如:set "u76493029" "大咖11523,5623654521236,15823564493,幽游仙水"
           这种方式的缺点是,增加了序列化/反序列化的开销,并且在需要修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护,引入CAS等复杂问题。   

 // myRedisCli.cpp: 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <assert.h>
#include <winsock2.h>
#include <string.h>
#include <tuple>
#include <iostream>
#include <sstream>
#include <string>
#include <string.h>
#include"hiredis/hiredis.h" using namespace std; class RedisConnect {
public:
RedisConnect() :redisCon(nullptr), reply(nullptr) {} bool Init(const std::string& ip, int port) {
if (nullptr != redisCon) {
return false;
}
redisCon = redisConnect(ip.c_str(), port);
if (redisCon->err) {
std::cerr << "error code : " << redisCon->err << ". " << redisCon->errstr << std::endl;
return false;
} return true;
}
void freeReply()
{
if (nullptr != reply)
{
::freeReplyObject(reply);
reply = nullptr;
}
} template<class T>
bool GetVal(const std::string& key, T& value) {
return Get(key, value);
} template<class T, class... Args>
bool HashSet(const std::string command, T head, Args... rest) {
std::stringstream ss;
ss << command << " " << head << " ";
return HashSetInner(ss, rest...);
} template<typename T>
bool Set(const std::string & key, const T& value)
{
bool bret = false;
std::stringstream ss;
ss << "SET " << key << " " << value;
std::string s;
getline(ss, s);
return Set(s);
} bool InitWithTimeout(const std::string& ip, int port, int seconds) {
if (nullptr != redisCon) {
return false;
}
struct timeval tv;
tv.tv_sec = seconds;
tv.tv_usec = ;
redisCon = redisConnectWithTimeout(ip.c_str(), port, tv);
if (redisCon->err) {
std::cerr << "error code : " << redisCon->err << ". " << redisCon->errstr << std::endl;
return false;
}
return true;
} ~RedisConnect() {
freeReply();
if (nullptr == redisCon) {
redisFree(redisCon);
redisCon = nullptr;
}
}
private:
void GetString(const std::string & key)
{
freeReply();
reply = (redisReply*)::redisCommand(redisCon, "GET %s", key.c_str());
} bool Get(const std::string& key, std::string & value) {
bool bret = false;
GetString(key);
if (nullptr == reply) {
std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl;
return bret;
}
else if (reply->type == REDIS_REPLY_NIL) {
std::cout << __FUNCTION__ << ". Key = \"" << key << "\" find nothing" << std::endl << std::endl;
return bret;
}else if( reply->type != REDIS_REPLY_STRING) {
std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl;
return bret;
}
value = reply->str;
bret = true;
return bret;
} bool Get(const std::string& key, int & value) {
bool bret = false;
GetString(key);
if (nullptr == reply) {
std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl;
return bret;
}
else if (reply->type == REDIS_REPLY_NIL) {
std::cout << __FUNCTION__ << ". Key = \"" << key << "\" find nothing" << std::endl << std::endl;
return bret;
}
else if (reply->type != REDIS_REPLY_STRING) {
std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl;
return bret;
}
value = ::atoi(reply->str);
bret = true;
return bret;
} bool Get(const std::string& key, float & value) {
bool bret = false;
GetString(key);
if (nullptr == reply) {
std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl;
return bret;
}
else if (reply->type == REDIS_REPLY_NIL) {
std::cout << __FUNCTION__ << ". Key = \"" << key << "\" find nothing" << std::endl << std::endl;
return bret;
}
else if (reply->type != REDIS_REPLY_STRING) {
std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl;
return bret;
}
value = ::atof(reply->str);
bret = true;
return bret;
} bool HashSetInner(std::stringstream& ss)
{
std::string data;
getline(ss, data);
//std::cout << __FUNCTION__ << " " << data << std::endl;
bool bret = false;
freeReply();
reply = (redisReply*)::redisCommand(redisCon, data.c_str()); if (reply->type == REDIS_REPLY_ERROR ||
(reply->type == REDIS_REPLY_STATUS && _stricmp(reply->str, "OK") != ))
{
if (reply->str != nullptr) {
std::cout << reply->str << std::endl;
}
std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl;
return bret;
} bret = true; return bret;
} template<class T, class... Args>
bool HashSetInner(std::stringstream& ss, T head, Args... rest)
{
ss << head << " ";
return HashSetInner(ss, rest...);
} bool Set(std::string data)
{
bool bret = false;
freeReply();
reply = (redisReply*)::redisCommand(redisCon, data.c_str()); if (!(reply->type == REDIS_REPLY_STATUS && _stricmp(reply->str, "OK") == ))
{
std::cout << reply->str << std::endl;
std::cout << "Failed to execute " << __FUNCTION__ << std::endl;
return bret;
}
bret = true;
return bret;
} redisContext* redisCon;
redisReply * reply;
}; typedef struct userscore {
string userID;
int score;
}UserScore; typedef struct userwinpercent {
string userID;
float winpercent;
}UserWinPercent; int main()
{
RedisConnect r; bool b = r.InitWithTimeout("127.0.0.1", , );
if (!b)
return -; UserScore us{ "user:u76493029:socre", };
UserWinPercent uwp{ "user:u76493029:winPercent",67.3 }; if (false == r.Set(us.userID, us.score) || false == r.Set(uwp.userID, uwp.winpercent)) {
std::cerr << "operate redis error!" << std::endl;
return -;
}
int score = ;
float winpercent = 0.0;
if (false == r.GetVal(us.userID, score) || false == r.GetVal(uwp.userID, winpercent)) {
std::cerr << "operate redis error!" << std::endl;
return -;
}
else {
std::cout << "key = " << us.userID << ". find value = " << score << std::endl;
std::cout << "key = " << uwp.userID << ". find value = " << winpercent << std::endl;
} std::cout << "finish !" << std::endl;
return ;
}

运行结果如图:

redis原生命令在客户端验证如下:

(此版本redis中文支持不太好)

(2)   第二种方法是这个用户信息对象有多少成员就存成多少个key-value对儿,用用户ID+对应属性的名称作为唯一标识来取得对应属性的值,不需要一次次地设置,可以一次设置多个,但命令信息有些冗余。
  比如userid对应玩家的分数 棋牌玩家的游戏场数 玩家的胜率等

  如:mset user:u76493029:socre 9999       user:u76493029:gameCount  723    user:u76493029:winPercent 67.3
  虽然省去了序列化开销和并发问题,但是用户ID为重复存储,如果存在大量这样的数据,内存浪费还是非常可观的。
  使用上一篇的hredis库进行代码操作 代码如下:

 // myRedisCli.cpp: 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <assert.h>
#include <winsock2.h>
#include <string.h>
#include <tuple>
#include <iostream>
#include <sstream>
#include <string>
#include <string.h>
#include"hiredis/hiredis.h" using namespace std; class RedisConnect {
public:
RedisConnect() :redisCon(nullptr), reply(nullptr) {} bool Init(const std::string& ip, int port) {
if (nullptr != redisCon) {
return false;
}
redisCon = redisConnect(ip.c_str(), port);
if (redisCon->err) {
std::cerr << "error code : " << redisCon->err << ". " << redisCon->errstr << std::endl;
return false;
} return true;
}
void freeReply()
{
if (nullptr != reply)
{
::freeReplyObject(reply);
reply = nullptr;
}
} template<class T>
bool GetVal(const std::string& key, T& value) {
return Get(key, value);
} template<class T, class... Args>
bool HashSet(const std::string command, T head, Args... rest) {
std::stringstream ss;
ss << command << " " << head << " ";
return HashSetInner(ss, rest...);
} template<typename T>
bool Set(const std::string & key, const T& value)
{
bool bret = false;
std::stringstream ss;
ss << "SET " << key << " " << value;
std::string s;
getline(ss, s);
return Set(s);
} bool InitWithTimeout(const std::string& ip, int port, int seconds) {
if (nullptr != redisCon) {
return false;
}
struct timeval tv;
tv.tv_sec = seconds;
tv.tv_usec = ;
redisCon = redisConnectWithTimeout(ip.c_str(), port, tv);
if (redisCon->err) {
std::cerr << "error code : " << redisCon->err << ". " << redisCon->errstr << std::endl;
return false;
}
return true;
} ~RedisConnect() {
freeReply();
if (nullptr == redisCon) {
redisFree(redisCon);
redisCon = nullptr;
}
}
private:
void GetString(const std::string & key)
{
freeReply();
reply = (redisReply*)::redisCommand(redisCon, "GET %s", key.c_str());
} bool Get(const std::string& key, std::string & value) {
bool bret = false;
GetString(key);
if (nullptr == reply) {
std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl;
return bret;
}
else if (reply->type == REDIS_REPLY_NIL) {
std::cout << __FUNCTION__ << ". Key = \"" << key << "\" find nothing" << std::endl << std::endl;
return bret;
}else if( reply->type != REDIS_REPLY_STRING) {
std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl;
return bret;
}
value = reply->str;
bret = true;
return bret;
} bool Get(const std::string& key, int & value) {
bool bret = false;
GetString(key);
if (nullptr == reply) {
std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl;
return bret;
}
else if (reply->type == REDIS_REPLY_NIL) {
std::cout << __FUNCTION__ << ". Key = \"" << key << "\" find nothing" << std::endl << std::endl;
return bret;
}
else if (reply->type != REDIS_REPLY_STRING) {
std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl;
return bret;
}
value = ::atoi(reply->str);
bret = true;
return bret;
} bool Get(const std::string& key, float & value) {
bool bret = false;
GetString(key);
if (nullptr == reply) {
std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl;
return bret;
}
else if (reply->type == REDIS_REPLY_NIL) {
std::cout << __FUNCTION__ << ". Key = \"" << key << "\" find nothing" << std::endl << std::endl;
return bret;
}
else if (reply->type != REDIS_REPLY_STRING) {
std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl;
return bret;
}
value = ::atof(reply->str);
bret = true;
return bret;
} bool HashSetInner(std::stringstream& ss)
{
std::string data;
getline(ss, data);
//std::cout << __FUNCTION__ << " " << data << std::endl;
bool bret = false;
freeReply();
reply = (redisReply*)::redisCommand(redisCon, data.c_str()); if (reply->type == REDIS_REPLY_ERROR ||
(reply->type == REDIS_REPLY_STATUS && _stricmp(reply->str, "OK") != ))
{
if (reply->str != nullptr) {
std::cout << reply->str << std::endl;
}
std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl;
return bret;
} bret = true; return bret;
} template<class T, class... Args>
bool HashSetInner(std::stringstream& ss, T head, Args... rest)
{
ss << head << " ";
return HashSetInner(ss, rest...);
} bool Set(std::string data)
{
bool bret = false;
freeReply();
reply = (redisReply*)::redisCommand(redisCon, data.c_str()); if (!(reply->type == REDIS_REPLY_STATUS && _stricmp(reply->str, "OK") == ))
{
std::cout << reply->str << std::endl;
std::cout << "Failed to execute " << __FUNCTION__ << std::endl;
return bret;
}
bret = true;
return bret;
} redisContext* redisCon;
redisReply * reply;
}; typedef struct userscore {
string userID;
int score;
}UserScore; typedef struct userwinpercent {
string userID;
float winpercent;
}UserWinPercent; int main()
{
RedisConnect r; bool b = r.InitWithTimeout("127.0.0.1", , );
if (!b)
return -; UserScore us{ "user:u76493029:socre", };
UserWinPercent uwp{ "user:u76493029:winPercent",67.3 }; if (false == r.Set(us.userID, us.score) || false == r.Set(uwp.userID, uwp.winpercent)) {
std::cerr << "operate redis error!" << std::endl;
return -;
}
int score = ;
float winpercent = 0.0;
if (false == r.GetVal(us.userID, score) || false == r.GetVal(uwp.userID, winpercent)) {
std::cerr << "operate redis error!" << std::endl;
return -;
}
else {
std::cout << "key = " << us.userID << ". find value = " << score << std::endl;
std::cout << "key = " << uwp.userID << ". find value = " << winpercent << std::endl;
} std::cout << "finish !" << std::endl;
return ;
}

运行结果如图:

redis原生命令在客户端验证如下:

(3)第三个,如果key下的value很多,而且各自的属性值互相是独立的。例如玩家包里的道具,那么使用上面的方法存储搜索起来其实是一种效率很低的方案。
Redis提供的Hash很好的解决了这个问题,Redis的Hash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口

如:hmset user:u76493029  itemID  78335267  itemType 武器  itemName 刀  itemCount 1  itemValue 4       
   也就是说,Key仍然是用户ID, value是一个Map,这个Map的key是成员的属性名,value是属性值,这样对数据的修改和存取都可以直接通过其内部Map的Key(Redis里称内部Map的key为field), 也就是通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题。很好的解决了问题。
这里同时需要注意,Redis提供了接口(hgetall)可以直接取到全部的属性数据,但是如果内部Map的成员很多,那么涉及到遍历整个内部Map的操作,由于Redis单线程模型的缘故,这个遍历操作可能会比较耗时,而另其它客户端的请求完全不响应,这点需要格外注意。

 // myRedisCli.cpp: 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <assert.h>
#include <winsock2.h>
#include <string.h>
#include <tuple>
#include <iostream>
#include <sstream>
#include <string.h>
#include <string>
#include <assert.h>
#include"hiredis/hiredis.h" using namespace std; class RedisConnect {
public:
RedisConnect() :redisCon(nullptr), reply(nullptr) {} bool Init(const std::string& ip, int port) {
if (nullptr != redisCon) {
return false;
}
redisCon = redisConnect(ip.c_str(), port);
if (redisCon->err) {
std::cerr << "error code : " << redisCon->err << ". " << redisCon->errstr << std::endl;
return false;
} return true;
}
void freeReply()
{
if (nullptr != reply)
{
::freeReplyObject(reply);
reply = nullptr;
}
} template<class T>
bool GetVal(const std::string& key, T& value) {
return Get(key, value);
} template<class T, class... Args>
bool HashSet(const std::string command, T head, Args... rest) {
std::stringstream ss;
ss << command << " " << head << " ";
return HashSetInner(ss, rest...);
} bool HGetAll(const string& key) {
bool bret = false;
freeReply();
string commandStr = "hgetall ";
commandStr += key;
reply = (redisReply*)::redisCommand(redisCon, commandStr.c_str());
if (nullptr == reply || == reply->elements ) {
std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl;
return bret;
}
std::cout << "HGETALL key = " << key << std::endl;
for (int i = ; i < reply->elements; i++) {
redisReply* p = reply->element[i];
std::cout << "element[" << i + << "] = " << p->str << std::endl;
} bret = true;
return bret;
} template<typename T>
bool Set(const std::string & key, const T& value)
{
bool bret = false;
std::stringstream ss;
ss << "SET " << key << " " << value;
std::string s;
getline(ss, s);
return Set(s);
} bool InitWithTimeout(const std::string& ip, int port, int seconds) {
if (nullptr != redisCon) {
return false;
}
struct timeval tv;
tv.tv_sec = seconds;
tv.tv_usec = ;
redisCon = redisConnectWithTimeout(ip.c_str(), port, tv);
if (redisCon->err) {
std::cerr << "error code : " << redisCon->err << ". " << redisCon->errstr << std::endl;
return false;
}
return true;
} ~RedisConnect() {
freeReply();
if (nullptr == redisCon) {
redisFree(redisCon);
redisCon = nullptr;
}
}
private:
void GetString(const std::string & key)
{
freeReply();
reply = (redisReply*)::redisCommand(redisCon, "GET %s", key.c_str());
} bool Get(const std::string& key, std::string & value) {
bool bret = false;
GetString(key);
if (nullptr == reply) {
std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl;
return bret;
}
else if (reply->type == REDIS_REPLY_NIL) {
std::cout << __FUNCTION__ << ". Key = \"" << key << "\" find nothing" << std::endl << std::endl;
return bret;
}else if( reply->type != REDIS_REPLY_STRING) {
std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl;
return bret;
}
value = reply->str;
bret = true;
return bret;
} bool Get(const std::string& key, int & value) {
bool bret = false;
GetString(key);
if (nullptr == reply) {
std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl;
return bret;
}
else if (reply->type == REDIS_REPLY_NIL) {
std::cout << __FUNCTION__ << ". Key = \"" << key << "\" find nothing" << std::endl << std::endl;
return bret;
}
else if (reply->type != REDIS_REPLY_STRING) {
std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl;
return bret;
}
value = ::atoi(reply->str);
bret = true;
return bret;
} bool Get(const std::string& key, float & value) {
bool bret = false;
GetString(key);
if (nullptr == reply) {
std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl;
return bret;
}
else if (reply->type == REDIS_REPLY_NIL) {
std::cout << __FUNCTION__ << ". Key = \"" << key << "\" find nothing" << std::endl << std::endl;
return bret;
}
else if (reply->type != REDIS_REPLY_STRING) {
std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl;
return bret;
}
value = ::atof(reply->str);
bret = true;
return bret;
} bool HashSetInner(std::stringstream& ss)
{
std::string data;
getline(ss, data);
//std::cout << __FUNCTION__ << " " << data << std::endl;
bool bret = false;
freeReply();
reply = (redisReply*)::redisCommand(redisCon, data.c_str()); if (reply->type == REDIS_REPLY_ERROR ||
(reply->type == REDIS_REPLY_STATUS && _stricmp(reply->str, "OK") != ))
{
if (reply->str != nullptr) {
std::cout << reply->str << std::endl;
}
std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl;
return bret;
} bret = true; return bret;
} template<class T, class... Args>
bool HashSetInner(std::stringstream& ss, T head, Args... rest)
{
ss << head << " ";
return HashSetInner(ss, rest...);
} bool Set(std::string data)
{
bool bret = false;
freeReply();
reply = (redisReply*)::redisCommand(redisCon, data.c_str()); if (!(reply->type == REDIS_REPLY_STATUS && _stricmp(reply->str, "OK") == ))
{
std::cout << reply->str << std::endl;
std::cout << "Failed to execute " << __FUNCTION__ << std::endl;
return bret;
}
bret = true;
return bret;
} redisContext* redisCon;
redisReply * reply;
};
//=======================================================================
//用户数据结构
typedef struct iteminfo {
string itemID;
string itemType;
string itemName;
int itemCount;
string itemValue; //DEF:23 防御力23 ATT:4 攻击力4 MED:6 医疗6
}ItemInfo;
//================================================================= int main()
{
RedisConnect r; bool b = r.InitWithTimeout("127.0.0.1", , );
if (!b)
return -; string userID = "user:u76493029";
ItemInfo itemInfo{ "","武器","绝影",,"ATT:4" }; if (false == r.HashSet("hmset",userID, "itemID", itemInfo.itemID, "itemType", itemInfo.itemType, "itemName", itemInfo.itemName,
"itemCount", itemInfo.itemCount, "itemValue", itemInfo.itemValue)) {
std::cerr << "operate redis error ! " << std::endl;
return -;
}
//todo HGETALL HGET KEY FIELD
b = r.HGetAll(userID); std::cout << "finish !" << std::endl;
return ;
}

运行结果如图:

redis原生命令在客户端验证如下:

参考

https://blog.csdn.net/chenjiayi_yun/article/details/18887647

http://www.cnblogs.com/stephen-liu74/archive/2012/04/16/2370212.html

redis在游戏服务器中的使用初探(三) 信息存储的更多相关文章

  1. redis在游戏服务器中的使用初探(一) 环境搭建

    这里我们尝试在游戏服务器中的数据处理中使用redis 通过该系列文章能够学习 redis的基本操作 源码编译 客户端开源库的编译和使用 以及在游戏服务器中的缓存使用 作为初次摸索 尽量使得环境简单  ...

  2. redis在游戏服务器中的使用初探(四) redis应用

    文章系列先介绍环境搭建 介绍redis操作和代码编写运行  这是典型的实战工程过程.那么我们为何要使用redis而不是常规的数据库比如 mysql呢? 因为KV内存数据库最大的优势所有数据全部存储在内 ...

  3. redis在游戏服务器中的使用初探(二) 客户端开源库选择

    上文提到 搭建完成后 我们选择客户端的开源库进行连接 有以下三种选择 1 acl-redis 原因是支持VC 国产  作者博客   acl 框架库简介  用 acl 库编写高效的 C++ redis ...

  4. Redis在游戏服务器中的应用

    排行榜游戏服务器中涉及到很多排行信息,比如玩家等级排名.金钱排名.战斗力排名等.一般情况下仅需要取排名的前N名就可以了,这时可以利用数据库的排序功能,或者自己维护一个元素数量有限的top集合.但是有时 ...

  5. 游戏服务器菜鸟之C#初探三游戏服务

    在经过上述2番折腾之后,最后决定使用TCP进行通信,所以在一次进行重构 主要重构的要点 1.将过来的HTPP请求,重构为TCP请求: 2.使用组件FluenScheduler进行怪物的定时刷新,和定时 ...

  6. 游戏服务器菜鸟之C#初探四游戏服务

    经过多次折腾之后,在一次进行了一次重大的重构,去解决问题 主要重构如下 1.将原来的单一协议修改多协议进行,一些查询.认证的功能都采用HTTP进行,避免全部采用TCP链接资源的消耗: 2.原来单一的部 ...

  7. 游戏服务器菜鸟之C#初探一游戏服务

    本人80后程序猿一枚,原来搞过C++/Java/C#,因为工作原因最后选择一直从事C#开发,因为读书时候对游戏一直比较感兴趣,机缘巧合公司做一个手游的项目,我就开始游戏服务器的折腾之旅. 游戏的构架是 ...

  8. [Unity游戏开发]向量在游戏开发中的应用(三)

    本文已同步发表在CSDN:http://blog.csdn.net/wenxin2011/article/details/51088236 在上一篇博客中讲了利用向量点乘在游戏开发中应用的几种情景.本 ...

  9. 游戏服务器菜鸟之C#初探二游戏服务

    经过短时间的折腾,为了解决上述问题,我对游戏进行一些简单的重构,以便能解决当前的瓶颈 添加了缓存服务器进行处理一些及时数据和配置数据,来缓解数据库的压力和IO的压力: 只能说解决当前的暂时性问题,但是 ...

随机推荐

  1. ORA-01578 ORACLE data block corrupted (file # 29, block # 2889087)

    BW数据库后台报错如下:F:\oracle\SBP\saptrace\diag\rdbms\sbp\sbp\trace ORA-01578: ORACLE data block corrupted ( ...

  2. 生产环境nginx上传文件报错413 Request Entity Too Large

    修改nginx配置文件/etc/nginx/nginx.conf 在http{}中添加 client_max_body_size 100m; 意思是设置上传文件大小

  3. 【scrapy】爬虫中报Forbidden by robots.txt

    需要在setting.py里找到ROBOTSTXT_OBEY并设为false 来源:https://blog.csdn.net/yimingsilence/article/details/521197 ...

  4. pytest 学习笔记二:兼容unittest、执行方式、生成报告

    1.官方文档上说pytest兼容unittest时,不支持setUpModule 和 tearDownModule,但实际验证是可以的. 验证的场景是py文件中,只有一个测试类, 经验证有多个测试类, ...

  5. TCL脚本语言基础介绍

    Tcl简介(一):Tcl 语法 Tcl 语法 Tcl是一种很通用的脚本语言,它几乎在所有的平台上都可以释运行,其强大的功能和简单精妙的语法会使你感到由衷的喜悦,这片文章对 Tcl有很好的描述和说明.如 ...

  6. TOJ5398: 签到大富翁(简单模拟) and TOJ 5395: 大于中值的边界元素(数组的应用)

    Python代码!!! 5395 传送门:http://acm.tzc.edu.cn/acmhome/problemdetail.do?&method=showdetail&id=53 ...

  7. 151. Reverse Words in a String (String)

    思路: 本题考查的目的并不是使用字符串的函数.方法是两次reverse,先对每个单词先做一次翻转,然后对整个字符串做一次翻转. 需要注意的是去除extra space,并且对全space字符串.以及最 ...

  8. JavaScript oop proto与prototype原型图

    [_proto_与prototype] 1.prototype(函数的原型):函数才有prototype.prototype是一个对象,指向了当前构造函数的引用地址. 2._proto_(对象的原型对 ...

  9. monobehaviour生命周期完整版

  10. TZOJ 1911 A Plug for UNIX(最大流)

    描述 You are in charge of setting up the press room for the inaugural meeting of the United Nations In ...