摘要:

搭建了服务器环境 有了客户端 我们来假想下以下应用场景:
我们简单举个实例来描述下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. c# list批量添加 对象 到数据库的sql

    public void AddList(List<ProModule.Model.pro_manifest_item> list) { Hashtable SQLStringList = ...

  2. day28 网络协议OSI 及socket模块

    反射都是操作对象中的__dict__的内容 class Student: def study(self): print("正在学习....") stu = Student() if ...

  3. 通过DataTable获得表的主键

    转载http://www.cnblogs.com/hobe/archive/2005/10/07/249940.html 通过DataTable获得表的主键 很多情形下我们需要知道表的主键是什么.在A ...

  4. glove

    glove - 必应词典 美[ɡlʌv]英[ɡlʌv] v.给戴手套:作…的手套 n.(分手指的)手套 网络分指手套:拳套:棒球之爱 变形复数:gloves:现在分词:gloving:过去分词:glo ...

  5. TZOJ 3305 Hero In Maze II(深搜)

    描述 500年前,Jesse是我国最卓越的剑客.他英俊潇洒,而且机智过人^_^.突然有一天,Jesse心爱的公主被魔王困在了一个巨大的迷宫中.Jesse听说这个消息已经是两天以后了,他急忙赶到迷宫,开 ...

  6. java 线程Thread 技术--方法演示生产与消费模式

    利用wait 与notifyAll 方法进行演示生产与消费的模式的演示,我们两个线程负责生产,两个线程消费,只有生产了才能消费: 在effective Java 中有说过: 1. 在Java 中 ,使 ...

  7. pthreads v3在centos7下的安装与配置

    我的centos版本是7.4.1708,php的版本是7.2.4(注意要是线程安全版),如下图所示: 首先我们在如下网址下载好pthreads的源码: http://pecl.php.net/pack ...

  8. windows7 Cygwin 下安装 YouCompleteMe 插件

    原创文章,欢迎指正!转载请注明~ 从上周就开始想在cygwin上安装YouCompleteMe插件,按照GITHUB上的官方教程安装,由于自己的理解失误,一直搞不清是按照在windows上安装还是按照 ...

  9. Vue 快速原型开发

    快速原型开发 注意: 是:serve 而不是 server 通过使用 vue serve 和 vue build 命令对单个 *.vue 文件进行快速原型开发,不过这需要先额外安装一个全局的扩展 go ...

  10. Ubuntu安装UFW防火墙

    sudo apt-get install ufw 一般用户,只需如下设置:sudo apt-get install ufw sudo ufw enable sudo ufw default deny ...