redis在游戏服务器中的使用初探(三) 信息存储
摘要:
搭建了服务器环境 有了客户端 我们来假想下以下应用场景:
我们简单举个实例来描述下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等复杂问题。

1 // myRedisCli.cpp: 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 #include <assert.h> 6 #include <winsock2.h> 7 #include <string.h> 8 #include <tuple> 9 #include <iostream> 10 #include <sstream> 11 #include <string> 12 #include <string.h> 13 #include"hiredis/hiredis.h" 14 15 using namespace std; 16 17 class RedisConnect { 18 public: 19 RedisConnect() :redisCon(nullptr), reply(nullptr) {} 20 21 bool Init(const std::string& ip, int port) { 22 if (nullptr != redisCon) { 23 return false; 24 } 25 redisCon = redisConnect(ip.c_str(), port); 26 if (redisCon->err) { 27 std::cerr << "error code : " << redisCon->err << ". " << redisCon->errstr << std::endl; 28 return false; 29 } 30 31 return true; 32 } 33 void freeReply() 34 { 35 if (nullptr != reply) 36 { 37 ::freeReplyObject(reply); 38 reply = nullptr; 39 } 40 } 41 42 template<class T> 43 bool GetVal(const std::string& key, T& value) { 44 return Get(key, value); 45 } 46 47 template<class T, class... Args> 48 bool HashSet(const std::string command, T head, Args... rest) { 49 std::stringstream ss; 50 ss << command << " " << head << " "; 51 return HashSetInner(ss, rest...); 52 } 53 54 template<typename T> 55 bool Set(const std::string & key, const T& value) 56 { 57 bool bret = false; 58 std::stringstream ss; 59 ss << "SET " << key << " " << value; 60 std::string s; 61 getline(ss, s); 62 return Set(s); 63 } 64 65 66 bool InitWithTimeout(const std::string& ip, int port, int seconds) { 67 if (nullptr != redisCon) { 68 return false; 69 } 70 struct timeval tv; 71 tv.tv_sec = seconds; 72 tv.tv_usec = 0; 73 redisCon = redisConnectWithTimeout(ip.c_str(), port, tv); 74 if (redisCon->err) { 75 std::cerr << "error code : " << redisCon->err << ". " << redisCon->errstr << std::endl; 76 return false; 77 } 78 return true; 79 } 80 81 ~RedisConnect() { 82 freeReply(); 83 if (nullptr == redisCon) { 84 redisFree(redisCon); 85 redisCon = nullptr; 86 } 87 } 88 private: 89 void GetString(const std::string & key) 90 { 91 freeReply(); 92 reply = (redisReply*)::redisCommand(redisCon, "GET %s", key.c_str()); 93 } 94 95 bool Get(const std::string& key, std::string & value) { 96 bool bret = false; 97 GetString(key); 98 if (nullptr == reply) { 99 std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl; 100 return bret; 101 } 102 else if (reply->type == REDIS_REPLY_NIL) { 103 std::cout << __FUNCTION__ << ". Key = \"" << key << "\" find nothing" << std::endl << std::endl; 104 return bret; 105 }else if( reply->type != REDIS_REPLY_STRING) { 106 std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl; 107 return bret; 108 } 109 value = reply->str; 110 bret = true; 111 return bret; 112 } 113 114 bool Get(const std::string& key, int & value) { 115 bool bret = false; 116 GetString(key); 117 if (nullptr == reply) { 118 std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl; 119 return bret; 120 } 121 else if (reply->type == REDIS_REPLY_NIL) { 122 std::cout << __FUNCTION__ << ". Key = \"" << key << "\" find nothing" << std::endl << std::endl; 123 return bret; 124 } 125 else if (reply->type != REDIS_REPLY_STRING) { 126 std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl; 127 return bret; 128 } 129 value = ::atoi(reply->str); 130 bret = true; 131 return bret; 132 } 133 134 bool Get(const std::string& key, float & value) { 135 bool bret = false; 136 GetString(key); 137 if (nullptr == reply) { 138 std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl; 139 return bret; 140 } 141 else if (reply->type == REDIS_REPLY_NIL) { 142 std::cout << __FUNCTION__ << ". Key = \"" << key << "\" find nothing" << std::endl << std::endl; 143 return bret; 144 } 145 else if (reply->type != REDIS_REPLY_STRING) { 146 std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl; 147 return bret; 148 } 149 value = ::atof(reply->str); 150 bret = true; 151 return bret; 152 } 153 154 bool HashSetInner(std::stringstream& ss) 155 { 156 std::string data; 157 getline(ss, data); 158 //std::cout << __FUNCTION__ << " " << data << std::endl; 159 bool bret = false; 160 freeReply(); 161 reply = (redisReply*)::redisCommand(redisCon, data.c_str()); 162 163 if (reply->type == REDIS_REPLY_ERROR || 164 (reply->type == REDIS_REPLY_STATUS && _stricmp(reply->str, "OK") != 0)) 165 { 166 if (reply->str != nullptr) { 167 std::cout << reply->str << std::endl; 168 } 169 std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl; 170 return bret; 171 } 172 173 bret = true; 174 175 return bret; 176 } 177 178 template<class T, class... Args> 179 bool HashSetInner(std::stringstream& ss, T head, Args... rest) 180 { 181 ss << head << " "; 182 return HashSetInner(ss, rest...); 183 } 184 185 bool Set(std::string data) 186 { 187 bool bret = false; 188 freeReply(); 189 reply = (redisReply*)::redisCommand(redisCon, data.c_str()); 190 191 if (!(reply->type == REDIS_REPLY_STATUS && _stricmp(reply->str, "OK") == 0)) 192 { 193 std::cout << reply->str << std::endl; 194 std::cout << "Failed to execute " << __FUNCTION__ << std::endl; 195 return bret; 196 } 197 bret = true; 198 return bret; 199 } 200 201 redisContext* redisCon; 202 redisReply * reply; 203 }; 204 205 typedef struct userscore { 206 string userID; 207 int score; 208 }UserScore; 209 210 typedef struct userwinpercent { 211 string userID; 212 float winpercent; 213 }UserWinPercent; 214 215 216 217 int main() 218 { 219 RedisConnect r; 220 221 bool b = r.InitWithTimeout("127.0.0.1", 6379, 5); 222 if (!b) 223 return -1; 224 225 UserScore us{ "user:u76493029:socre",9999 }; 226 UserWinPercent uwp{ "user:u76493029:winPercent",67.3 }; 227 228 if (false == r.Set(us.userID, us.score) || false == r.Set(uwp.userID, uwp.winpercent)) { 229 std::cerr << "operate redis error!" << std::endl; 230 return -1; 231 } 232 int score = 0; 233 float winpercent = 0.0; 234 if (false == r.GetVal(us.userID, score) || false == r.GetVal(uwp.userID, winpercent)) { 235 std::cerr << "operate redis error!" << std::endl; 236 return -1; 237 } 238 else { 239 std::cout << "key = " << us.userID << ". find value = " << score << std::endl; 240 std::cout << "key = " << uwp.userID << ". find value = " << winpercent << std::endl; 241 } 242 243 244 std::cout << "finish !" << std::endl; 245 return 0; 246 }
运行结果如图:
redis原生命令在客户端验证如下:
(此版本redis中文支持不太好)
(2) 第二种方法是这个用户信息对象有多少成员就存成多少个key-value对儿,用用户ID+对应属性的名称作为唯一标识来取得对应属性的值,不需要一次次地设置,可以一次设置多个,但命令信息有些冗余。
比如userid对应玩家的分数 棋牌玩家的游戏场数 玩家的胜率等
如:mset user:u76493029:socre 9999 user:u76493029:gameCount 723 user:u76493029:winPercent 67.3
虽然省去了序列化开销和并发问题,但是用户ID为重复存储,如果存在大量这样的数据,内存浪费还是非常可观的。
使用上一篇的hredis库进行代码操作 代码如下:

1 // myRedisCli.cpp: 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 #include <assert.h> 6 #include <winsock2.h> 7 #include <string.h> 8 #include <tuple> 9 #include <iostream> 10 #include <sstream> 11 #include <string> 12 #include <string.h> 13 #include"hiredis/hiredis.h" 14 15 using namespace std; 16 17 class RedisConnect { 18 public: 19 RedisConnect() :redisCon(nullptr), reply(nullptr) {} 20 21 bool Init(const std::string& ip, int port) { 22 if (nullptr != redisCon) { 23 return false; 24 } 25 redisCon = redisConnect(ip.c_str(), port); 26 if (redisCon->err) { 27 std::cerr << "error code : " << redisCon->err << ". " << redisCon->errstr << std::endl; 28 return false; 29 } 30 31 return true; 32 } 33 void freeReply() 34 { 35 if (nullptr != reply) 36 { 37 ::freeReplyObject(reply); 38 reply = nullptr; 39 } 40 } 41 42 template<class T> 43 bool GetVal(const std::string& key, T& value) { 44 return Get(key, value); 45 } 46 47 template<class T, class... Args> 48 bool HashSet(const std::string command, T head, Args... rest) { 49 std::stringstream ss; 50 ss << command << " " << head << " "; 51 return HashSetInner(ss, rest...); 52 } 53 54 template<typename T> 55 bool Set(const std::string & key, const T& value) 56 { 57 bool bret = false; 58 std::stringstream ss; 59 ss << "SET " << key << " " << value; 60 std::string s; 61 getline(ss, s); 62 return Set(s); 63 } 64 65 66 bool InitWithTimeout(const std::string& ip, int port, int seconds) { 67 if (nullptr != redisCon) { 68 return false; 69 } 70 struct timeval tv; 71 tv.tv_sec = seconds; 72 tv.tv_usec = 0; 73 redisCon = redisConnectWithTimeout(ip.c_str(), port, tv); 74 if (redisCon->err) { 75 std::cerr << "error code : " << redisCon->err << ". " << redisCon->errstr << std::endl; 76 return false; 77 } 78 return true; 79 } 80 81 ~RedisConnect() { 82 freeReply(); 83 if (nullptr == redisCon) { 84 redisFree(redisCon); 85 redisCon = nullptr; 86 } 87 } 88 private: 89 void GetString(const std::string & key) 90 { 91 freeReply(); 92 reply = (redisReply*)::redisCommand(redisCon, "GET %s", key.c_str()); 93 } 94 95 bool Get(const std::string& key, std::string & value) { 96 bool bret = false; 97 GetString(key); 98 if (nullptr == reply) { 99 std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl; 100 return bret; 101 } 102 else if (reply->type == REDIS_REPLY_NIL) { 103 std::cout << __FUNCTION__ << ". Key = \"" << key << "\" find nothing" << std::endl << std::endl; 104 return bret; 105 }else if( reply->type != REDIS_REPLY_STRING) { 106 std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl; 107 return bret; 108 } 109 value = reply->str; 110 bret = true; 111 return bret; 112 } 113 114 bool Get(const std::string& key, int & value) { 115 bool bret = false; 116 GetString(key); 117 if (nullptr == reply) { 118 std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl; 119 return bret; 120 } 121 else if (reply->type == REDIS_REPLY_NIL) { 122 std::cout << __FUNCTION__ << ". Key = \"" << key << "\" find nothing" << std::endl << std::endl; 123 return bret; 124 } 125 else if (reply->type != REDIS_REPLY_STRING) { 126 std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl; 127 return bret; 128 } 129 value = ::atoi(reply->str); 130 bret = true; 131 return bret; 132 } 133 134 bool Get(const std::string& key, float & value) { 135 bool bret = false; 136 GetString(key); 137 if (nullptr == reply) { 138 std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl; 139 return bret; 140 } 141 else if (reply->type == REDIS_REPLY_NIL) { 142 std::cout << __FUNCTION__ << ". Key = \"" << key << "\" find nothing" << std::endl << std::endl; 143 return bret; 144 } 145 else if (reply->type != REDIS_REPLY_STRING) { 146 std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl; 147 return bret; 148 } 149 value = ::atof(reply->str); 150 bret = true; 151 return bret; 152 } 153 154 bool HashSetInner(std::stringstream& ss) 155 { 156 std::string data; 157 getline(ss, data); 158 //std::cout << __FUNCTION__ << " " << data << std::endl; 159 bool bret = false; 160 freeReply(); 161 reply = (redisReply*)::redisCommand(redisCon, data.c_str()); 162 163 if (reply->type == REDIS_REPLY_ERROR || 164 (reply->type == REDIS_REPLY_STATUS && _stricmp(reply->str, "OK") != 0)) 165 { 166 if (reply->str != nullptr) { 167 std::cout << reply->str << std::endl; 168 } 169 std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl; 170 return bret; 171 } 172 173 bret = true; 174 175 return bret; 176 } 177 178 template<class T, class... Args> 179 bool HashSetInner(std::stringstream& ss, T head, Args... rest) 180 { 181 ss << head << " "; 182 return HashSetInner(ss, rest...); 183 } 184 185 bool Set(std::string data) 186 { 187 bool bret = false; 188 freeReply(); 189 reply = (redisReply*)::redisCommand(redisCon, data.c_str()); 190 191 if (!(reply->type == REDIS_REPLY_STATUS && _stricmp(reply->str, "OK") == 0)) 192 { 193 std::cout << reply->str << std::endl; 194 std::cout << "Failed to execute " << __FUNCTION__ << std::endl; 195 return bret; 196 } 197 bret = true; 198 return bret; 199 } 200 201 redisContext* redisCon; 202 redisReply * reply; 203 }; 204 205 typedef struct userscore { 206 string userID; 207 int score; 208 }UserScore; 209 210 typedef struct userwinpercent { 211 string userID; 212 float winpercent; 213 }UserWinPercent; 214 215 216 217 int main() 218 { 219 RedisConnect r; 220 221 bool b = r.InitWithTimeout("127.0.0.1", 6379, 5); 222 if (!b) 223 return -1; 224 225 UserScore us{ "user:u76493029:socre",9999 }; 226 UserWinPercent uwp{ "user:u76493029:winPercent",67.3 }; 227 228 if (false == r.Set(us.userID, us.score) || false == r.Set(uwp.userID, uwp.winpercent)) { 229 std::cerr << "operate redis error!" << std::endl; 230 return -1; 231 } 232 int score = 0; 233 float winpercent = 0.0; 234 if (false == r.GetVal(us.userID, score) || false == r.GetVal(uwp.userID, winpercent)) { 235 std::cerr << "operate redis error!" << std::endl; 236 return -1; 237 } 238 else { 239 std::cout << "key = " << us.userID << ". find value = " << score << std::endl; 240 std::cout << "key = " << uwp.userID << ". find value = " << winpercent << std::endl; 241 } 242 243 244 std::cout << "finish !" << std::endl; 245 return 0; 246 }
运行结果如图:
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单线程模型的缘故,这个遍历操作可能会比较耗时,而另其它客户端的请求完全不响应,这点需要格外注意。

1 // myRedisCli.cpp: 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 #include <assert.h> 6 #include <winsock2.h> 7 #include <string.h> 8 #include <tuple> 9 #include <iostream> 10 #include <sstream> 11 #include <string.h> 12 #include <string> 13 #include <assert.h> 14 #include"hiredis/hiredis.h" 15 16 using namespace std; 17 18 class RedisConnect { 19 public: 20 RedisConnect() :redisCon(nullptr), reply(nullptr) {} 21 22 bool Init(const std::string& ip, int port) { 23 if (nullptr != redisCon) { 24 return false; 25 } 26 redisCon = redisConnect(ip.c_str(), port); 27 if (redisCon->err) { 28 std::cerr << "error code : " << redisCon->err << ". " << redisCon->errstr << std::endl; 29 return false; 30 } 31 32 return true; 33 } 34 void freeReply() 35 { 36 if (nullptr != reply) 37 { 38 ::freeReplyObject(reply); 39 reply = nullptr; 40 } 41 } 42 43 template<class T> 44 bool GetVal(const std::string& key, T& value) { 45 return Get(key, value); 46 } 47 48 template<class T, class... Args> 49 bool HashSet(const std::string command, T head, Args... rest) { 50 std::stringstream ss; 51 ss << command << " " << head << " "; 52 return HashSetInner(ss, rest...); 53 } 54 55 bool HGetAll(const string& key) { 56 bool bret = false; 57 freeReply(); 58 string commandStr = "hgetall "; 59 commandStr += key; 60 reply = (redisReply*)::redisCommand(redisCon, commandStr.c_str()); 61 if (nullptr == reply || 0 == reply->elements ) { 62 std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl; 63 return bret; 64 } 65 std::cout << "HGETALL key = " << key << std::endl; 66 for (int i = 0; i < reply->elements; i++) { 67 redisReply* p = reply->element[i]; 68 std::cout << "element[" << i + 1 << "] = " << p->str << std::endl; 69 } 70 71 72 bret = true; 73 return bret; 74 } 75 76 template<typename T> 77 bool Set(const std::string & key, const T& value) 78 { 79 bool bret = false; 80 std::stringstream ss; 81 ss << "SET " << key << " " << value; 82 std::string s; 83 getline(ss, s); 84 return Set(s); 85 } 86 87 88 bool InitWithTimeout(const std::string& ip, int port, int seconds) { 89 if (nullptr != redisCon) { 90 return false; 91 } 92 struct timeval tv; 93 tv.tv_sec = seconds; 94 tv.tv_usec = 0; 95 redisCon = redisConnectWithTimeout(ip.c_str(), port, tv); 96 if (redisCon->err) { 97 std::cerr << "error code : " << redisCon->err << ". " << redisCon->errstr << std::endl; 98 return false; 99 } 100 return true; 101 } 102 103 ~RedisConnect() { 104 freeReply(); 105 if (nullptr == redisCon) { 106 redisFree(redisCon); 107 redisCon = nullptr; 108 } 109 } 110 private: 111 void GetString(const std::string & key) 112 { 113 freeReply(); 114 reply = (redisReply*)::redisCommand(redisCon, "GET %s", key.c_str()); 115 } 116 117 bool Get(const std::string& key, std::string & value) { 118 bool bret = false; 119 GetString(key); 120 if (nullptr == reply) { 121 std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl; 122 return bret; 123 } 124 else if (reply->type == REDIS_REPLY_NIL) { 125 std::cout << __FUNCTION__ << ". Key = \"" << key << "\" find nothing" << std::endl << std::endl; 126 return bret; 127 }else if( reply->type != REDIS_REPLY_STRING) { 128 std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl; 129 return bret; 130 } 131 value = reply->str; 132 bret = true; 133 return bret; 134 } 135 136 bool Get(const std::string& key, int & value) { 137 bool bret = false; 138 GetString(key); 139 if (nullptr == reply) { 140 std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl; 141 return bret; 142 } 143 else if (reply->type == REDIS_REPLY_NIL) { 144 std::cout << __FUNCTION__ << ". Key = \"" << key << "\" find nothing" << std::endl << std::endl; 145 return bret; 146 } 147 else if (reply->type != REDIS_REPLY_STRING) { 148 std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl; 149 return bret; 150 } 151 value = ::atoi(reply->str); 152 bret = true; 153 return bret; 154 } 155 156 bool Get(const std::string& key, float & value) { 157 bool bret = false; 158 GetString(key); 159 if (nullptr == reply) { 160 std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl; 161 return bret; 162 } 163 else if (reply->type == REDIS_REPLY_NIL) { 164 std::cout << __FUNCTION__ << ". Key = \"" << key << "\" find nothing" << std::endl << std::endl; 165 return bret; 166 } 167 else if (reply->type != REDIS_REPLY_STRING) { 168 std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl; 169 return bret; 170 } 171 value = ::atof(reply->str); 172 bret = true; 173 return bret; 174 } 175 176 bool HashSetInner(std::stringstream& ss) 177 { 178 std::string data; 179 getline(ss, data); 180 //std::cout << __FUNCTION__ << " " << data << std::endl; 181 bool bret = false; 182 freeReply(); 183 reply = (redisReply*)::redisCommand(redisCon, data.c_str()); 184 185 if (reply->type == REDIS_REPLY_ERROR || 186 (reply->type == REDIS_REPLY_STATUS && _stricmp(reply->str, "OK") != 0)) 187 { 188 if (reply->str != nullptr) { 189 std::cout << reply->str << std::endl; 190 } 191 std::cout << "Failed to execute " << __FUNCTION__ << std::endl << std::endl; 192 return bret; 193 } 194 195 bret = true; 196 197 return bret; 198 } 199 200 template<class T, class... Args> 201 bool HashSetInner(std::stringstream& ss, T head, Args... rest) 202 { 203 ss << head << " "; 204 return HashSetInner(ss, rest...); 205 } 206 207 bool Set(std::string data) 208 { 209 bool bret = false; 210 freeReply(); 211 reply = (redisReply*)::redisCommand(redisCon, data.c_str()); 212 213 if (!(reply->type == REDIS_REPLY_STATUS && _stricmp(reply->str, "OK") == 0)) 214 { 215 std::cout << reply->str << std::endl; 216 std::cout << "Failed to execute " << __FUNCTION__ << std::endl; 217 return bret; 218 } 219 bret = true; 220 return bret; 221 } 222 223 redisContext* redisCon; 224 redisReply * reply; 225 }; 226 //======================================================================= 227 //用户数据结构 228 typedef struct iteminfo { 229 string itemID; 230 string itemType; 231 string itemName; 232 int itemCount; 233 string itemValue; //DEF:23 防御力23 ATT:4 攻击力4 MED:6 医疗6 234 }ItemInfo; 235 //================================================================= 236 237 238 239 int main() 240 { 241 RedisConnect r; 242 243 bool b = r.InitWithTimeout("127.0.0.1", 6379, 5); 244 if (!b) 245 return -1; 246 247 string userID = "user:u76493029"; 248 ItemInfo itemInfo{ "78335267","武器","绝影",1,"ATT:4" }; 249 250 if (false == r.HashSet("hmset",userID, "itemID", itemInfo.itemID, "itemType", itemInfo.itemType, "itemName", itemInfo.itemName, 251 "itemCount", itemInfo.itemCount, "itemValue", itemInfo.itemValue)) { 252 std::cerr << "operate redis error ! " << std::endl; 253 return -1; 254 } 255 //todo HGETALL HGET KEY FIELD 256 b = r.HGetAll(userID); 257 258 std::cout << "finish !" << std::endl; 259 return 0; 260 }
运行结果如图:
redis原生命令在客户端验证如下:
参考
https://blog.csdn.net/chenjiayi_yun/article/details/18887647
http://www.cnblogs.com/stephen-liu74/archive/2012/04/16/2370212.html
欢迎转帖 请保持文本完整并注明出处
技术博客 http://www.cnblogs.com/itdef/
B站算法视频题解
https://space.bilibili.com/18508846
qq 151435887
gitee https://gitee.com/def/
欢迎c c++ 算法爱好者 windows驱动爱好者 服务器程序员沟通交流
如果觉得不错,欢迎点赞,你的鼓励就是我的动力


【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
2016-08-30 操作系统学习笔记(一) 段权限转移规则