摘要:

搭建了服务器环境 有了客户端 我们来假想下以下应用场景:
我们简单举个实例来描述下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. spark streaming集成kafka接收数据的方式

    spark streaming是以batch的方式来消费,strom是准实时一条一条的消费.当然也可以使用trident和tick的方式来实现batch消费(官方叫做mini batch).效率嘛,有 ...

  2. hibernate ORA-17059 无法转换为内部表示

    参考 https://jingyan.baidu.com/article/2fb0ba40a25a2b00f2ec5fc7.html 数据库里的字段类型与Java实体类中的对应字段属性类型不匹配

  3. python--第七天总结

    引言 面向过程:根据业务逻辑从上到下写垒代码 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可 面向对象:对函数进行分类和封装,让开发“更快更好更强...” [面向对象编程(Obj ...

  4. rancher2 HA部署注意事项

    参考: https://rancher.com/docs/rancher/v2.x/en/installation/ha-server-install/ https://www.cnblogs.com ...

  5. LeetCode第20题

    LeetCode20题不多说上代码 public boolean isValid(String s){ Stack<Character> stack = new Stack<Char ...

  6. Gym - 101911C Bacteria (规律题)

    传送门:点我 Time limit2000 ms Memory limit262144 kB Recently Monocarp has created his own mini-laboratory ...

  7. Unity2017五子棋大战_人机_双人_UNET联网

    五子棋大战源码工程基于Unity2017.2进行开发,分为人机.双人.UNET网络三种对战方式,配有案例讲解视频, 其中人机五子棋AI有三种开发难度,欢迎有兴趣的同学加入学习! . 目录 000-展示 ...

  8. word 2016 加载 mathtype

    1.加载wold: 首先打开word,选择选项-------加载项------管理(A),选择word加载项,点击转到 这儿显示的是我已经添加过的所以显示的有. 2.点击添加,找到你的mathtype ...

  9. sql语句Order by 报错列名不明确

    select top 10 column1,column2,column3 from table1 where table1.id not in(select top 0 table1.id from ...

  10. 安装sql server 2008 提示错误 SQL Server 2005 Express 工具。 失败

    安装sql server 2008 management,提示错误:Sql2005SsmsExpressFacet 检查是否安装了 SQL Server 2005 Express 工具. 失败,已安装 ...