Redis 核心数据结构:String 类型深度解析与 C++ 实战

Redis 核心数据结构:String 类型深度解析与 C++ 实战

Redis 核心数据结构:String 类型深度解析与 C++ 实战

前言

在当今数据驱动的世界里,Redis 以其卓越的性能和丰富的数据结构,已成为内存数据库领域的翘楚。无论是作为高速缓存、消息队列,还是分布式锁的实现方案,Redis 的身影无处不在。而在 Redis 提供的所有数据结构中,String 类型无疑是基石中的基石。它不仅是构建其他复杂结构的基础,其自身强大的命令集也足以应对各种复杂的业务场景。

本文将以广受欢迎的 C++ Redis 客户端库 redis-plus-plus 为实战工具,系统性地、由浅入深地剖析 Redis String 类型的核心命令。我们将从最基础的 SETGET 操作讲起,逐步探索包括过期时间设置、条件更新、批量操作、子字符串处理以及原子计数器在内的各种高级用法。

本文旨在为您提供一份不仅包含“如何做”,更解释“为什么这么做”的详尽指南。我们将深入探讨 redis-plus-plus 如何利用现代 C++ 的特性(如 std::optionalstd::chrono、迭代器等)来提供一个既安全又高效的编程接口,并结合完整的代码示例、逐行分析以及最佳实践,帮助您在 C++ 项目中将 Redis String 的威力发挥到极致。


第一章:基础奠基 —— SETGET

SETGET 是 Redis 世界的 “Hello, World!”。SET 用于将一个字符串值关联到一个键上,而 GET 则用于根据键获取其关联的字符串值。如果键已经存在,SET 会无条件地覆盖旧值。

image.png

1.1 基本读写操作

让我们从一个简单的 C++ 函数开始,它演示了最基本的设置、读取和更新操作。

#include<iostream>#include<sw/redis++/redis.h>// 引入 chrono 字面量,例如 10susingnamespace std::chrono_literals;voidtest1_basic_set_get(sw::redis::Redis& redis){ std::cout <<">> 1. 演示 SET 和 GET 基础操作"<< std::endl;// 清空当前数据库,确保测试环境纯净// 警告:这是一个危险操作,切勿在生产环境随意使用! redis.flushall();// 1. 首次设置键值对// 调用 SET key "111" std::cout <<"设置 'key' 的值为 '111'"<< std::endl; redis.set("key","111");// 2. 读取键的值// 调用 GET keyauto value = redis.get("key");// 3. 安全地处理返回值if(value){// .value() 从 std::optional 对象中提取实际的值 std::cout <<"获取到 'key' 的值: "<< value.value()<< std::endl;}else{ std::cout <<"'key' 不存在"<< std::endl;} std::cout <<"\n--------------------------\n"<< std::endl;// 4. 更新操作// 再次调用 SET 命令会覆盖旧值 std::cout <<"更新 'key' 的值为 '222'"<< std::endl; redis.set("key","222"); value = redis.get("key");if(value){ std::cout <<"更新后获取到 'key' 的值: "<< value.value()<< std::endl;}}
代码深度解析
  1. sw::redis::Redis& redis: 函数通过引用的方式接收一个 Redis 连接对象。这是一种高效的做法,避免了不必要的对象拷贝,确保所有操作都在同一个 TCP 连接上进行。
  2. redis.flushall(): 此命令对应 Redis 的 FLUSHALL,会清空整个 Redis 实例中所有数据库的数据。在编写测试用例时,它能确保每次测试都在一个可预测的、干净的环境中开始。再次强调,此命令在生产环境中具有极高的风险,必须谨慎使用。
  3. redis.set("key", "111"): 这是对 Redis SET 命令的直接封装。它是一个原子操作,当此命令执行时,Redis 会确保在设置值的过程中不会被其他命令打断。
  4. auto value = redis.get("key"): redis-plus-plusget 方法并未直接返回 std::string,而是返回一个 std::optional<std::string>。这是一个非常精妙和安全的现代 C++ 设计。
核心概念:std::optional 的威力

std::optional (C++17) 是一个模板类,用于表示一个“可能存在,也可能不存在”的值。这完美地映射了 GET 命令的行为:

  • 如果键存在,Redis 返回对应的值,std::optional 对象内部会包含这个 std::string 值。
  • 如果键不存在,Redis 返回 nilstd::optional 对象则处于“空”状态。

这种设计的巨大优势在于类型安全。它在编译时就强制你处理“值可能不存在”的情况,从而避免了传统 C 语言风格(如返回空指针或特殊字符串)可能导致的运行时错误(如空指针解引用)。

  • 检查是否存在: if (value)if (value.has_value()) 是检查 optional 是否包含值的标准方法。
  • 安全获取值: value.value() 是获取内部值的推荐方式。但请注意:如果在一个空的 optional 对象上调用 .value(),程序会抛出 std::bad_optional_access 异常。因此,必须先检查再访问,正如示例代码所做的那样。
  • 带默认值的获取: value.value_or("default_string") 是一个更便捷的方法,如果值存在则返回它,否则返回提供的默认值。

1.2 错误处理最佳实践

在实际应用中,与 Redis 的交互可能会因为网络中断、服务器宕机或配置错误而失败。redis-plus-plus 在遇到这类问题时会抛出 sw::redis::Error 异常。因此,健壮的代码应该将所有 Redis 操作包裹在 try-catch 块中。

voidrobust_redis_operations(sw::redis::Redis& redis){try{ redis.set("key","some_value");auto val = redis.get("key");if(val){ std::cout <<"Success: "<< val.value()<< std::endl;}}catch(const sw::redis::Error &e){ std::cerr <<"Redis command failed: "<< e.what()<< std::endl;// 此处可以添加重连逻辑或错误上报}}

第二章:掌控时间 —— 为键设置过期策略

在许多场景下,我们存入 Redis 的数据并不需要永久保存,例如用户会话信息、页面缓存、验证码等。为这些“临时”数据设置一个自动过期时间(Time-To-Live, TTL),是 Redis 作为缓存服务器的核心功能之一。

redis-plus-plus 利用 C++11 引入的 <chrono> 库,提供了一种类型安全且语义清晰的方式来设置过期时间。

image.png

2.1 SET 的过期时间选项

SET 命令本身就支持在设置键值对的同时原子性地指定过期时间。

voidtest2_set_with_expiration(sw::redis::Redis& redis){ std::cout <<"\n>> 2. 演示 SET 带有过期时间的操作"<< std::endl; redis.flushall();// 设置 'key' 的值为 '111',并指定 10 秒后过期// std::chrono::seconds(10) 创建了一个表示 10 秒的时间段对象// 也可以使用 using namespace std::chrono_literals; 后的 10s std::cout <<"设置 'key',10 秒后过期"<< std::endl; redis.set("key","111", std::chrono::seconds(10));// 检查刚设置后的剩余时间longlong ttl = redis.ttl("key"); std::cout <<"设置后,'key' 的剩余过期时间 (TTL): "<< ttl <<" 秒"<< std::endl;// 线程休眠 5 秒,模拟时间流逝 std::cout <<"程序休眠 5 秒..."<< std::endl; std::this_thread::sleep_for(std::chrono::seconds(5));// 再次获取剩余过期时间 ttl = redis.ttl("key"); std::cout <<"5 秒后,'key' 的剩余过期时间 (TTL): "<< ttl <<" 秒"<< std::endl;// 验证键是否存在if(redis.exists("key")){ std::cout <<"此时 'key' 仍然存在"<< std::endl;}// 再休眠 6 秒,确保键已过期 std::cout <<"程序再休眠 6 秒..."<< std::endl; std::this_thread::sleep_for(std::chrono::seconds(6));if(!redis.exists("key")){ std::cout <<"总共 11 秒后,'key' 已按预期自动删除"<< std::endl;}}
代码深度解析
  1. redis.set("key", "111", std::chrono::seconds(10)): 这是 set 方法的一个重载版本。第三个参数接收一个 std::chrono::duration 对象。
    • std::chrono 的优势: 直接传入整数 10 可能会有歧义(是秒、毫秒还是分钟?)。而 std::chrono::seconds(10)10s 这样的写法,在编译时就明确了时间单位,大大提高了代码的可读性和健壮性。你也可以使用 std::chrono::milliseconds, std::chrono::minutes 等。
  2. long long time = redis.ttl("key"): 此调用对应 Redis 的 TTL 命令,用于获取一个键的剩余过期时间(以秒为单位)。
    • 返回值 > 0: 键存在,且返回值是剩余的秒数。
    • 返回值为 -1: 键存在,但没有设置过期时间(永久有效)。
    • 返回值为 -2: 键不存在。
应用场景
  • 缓存: Web 页面的片段、数据库查询结果等可以缓存几分钟,到期自动失效,强制应用重新从数据源获取最新数据。
  • 会话管理: 网站用户的登录状态可以存储在 Redis 中,并设置一个合理的过期时间(如 30 分钟)。用户长时间无操作,会话自动过期,需要重新登录。
  • 分布式锁: 为锁设置一个过期时间是一种重要的安全机制,可以防止因持有锁的客户端崩溃而导致死锁。

第三章:精细控制 —— 条件化 SET

在某些分布式场景下,我们不希望 SET 操作总是无条件地覆盖旧值。我们可能需要实现“仅当键不存在时才设置”或“仅当键存在时才更新”的逻辑。这正是 SET 命令的 NXXX 选项的用武之地。

  • NX (if Not eXists): 只在键不存在时,才对键进行设置操作。
  • XX (if eXists): 只在键已经存在时,才对键进行设置操作。

redis-plus-plus 通过 sw::redis::UpdateType 枚举来支持这些选项。

voidtest3_conditional_set(sw::redis::Redis& redis){ std::cout <<"\n>> 3. 演示 SET 的 NX 和 XX 选项"<< std::endl; redis.flushall();// 观察:在 redis-plus-plus 中,NX/XX 选项是与过期时间设置在同一个重载函数中的// 如果不需要过期,可以将过期时间设为 0// 1. 使用 NX: 当 key 不存在时设置 std::cout <<"尝试使用 NX (NOT_EXIST) 设置 'key'"<< std::endl;// 参数:键, 值, 过期时间(0s表示不过期), 更新类型bool success = redis.set("key","val_nx",0s, sw::redis::UpdateType::NOT_EXIST);if(success){ std::cout <<"成功: 'key' 原本不存在,已设置为 'val_nx'"<< std::endl;}else{ std::cout <<"失败: 'key' 已存在,设置操作被忽略"<< std::endl;} std::cout <<"当前 'key' 的值: "<< redis.get("key").value_or("N/A")<< std::endl;// 2. 再次使用 NX: 此时 key 已存在,操作将失败 std::cout <<"\n再次尝试使用 NX 设置 'key'"<< std::endl; success = redis.set("key","another_val",0s, sw::redis::UpdateType::NOT_EXIST);if(success){ std::cout <<"成功: 'key' 原本不存在,已设置为 'another_val'"<< std::endl;}else{ std::cout <<"失败: 'key' 已存在,设置操作被忽略"<< std::endl;} std::cout <<"当前 'key' 的值: "<< redis.get("key").value_or("N/A")<< std::endl;// 3. 使用 XX: 当 key 存在时更新 std::cout <<"\n尝试使用 XX (EXIST) 更新 'key'"<< std::endl; success = redis.set("key","val_xx",0s, sw::redis::UpdateType::EXIST);if(success){ std::cout <<"成功: 'key' 存在,已更新为 'val_xx'"<< std::endl;}else{ std::cout <<"失败: 'key' 不存在,更新操作被忽略"<< std::endl;} std::cout <<"当前 'key' 的值: "<< redis.get("key").value_or("N/A")<< std::endl;// 4. 删除 key 后再使用 XX redis.del("key"); std::cout <<"\n删除 'key' 后,尝试使用 XX 更新"<< std::endl; success = redis.set("key","another_val_xx",0s, sw::redis::UpdateType::EXIST);if(success){ std::cout <<"成功: 'key' 存在,已更新为 'another_val_xx'"<< std::endl;}else{ std::cout <<"失败: 'key' 不存在,更新操作被忽略"<< std::endl;} std::cout <<"当前 'key' 的值: "<< redis.get("key").value_or("N/A")<< std::endl;}
代码深度解析
  • redis.set(..., sw::redis::UpdateType::NOT_EXIST): 这行代码的意图非常明确——仅当 key 不存在时,才将其值设为 val_nxset 命令的这个重载版本会返回一个 bool 值,true 表示设置成功,false 表示因不满足条件而未执行。
  • sw::redis::UpdateType::EXIST: 逻辑与 NOT_EXIST 相反,要求 key 必须已存在,set 操作才会执行。
核心应用:实现分布式锁

SET key value NX EX seconds 是 Redis 实现分布式锁的经典模式。

  1. 获取锁: 一个客户端尝试执行 redis.set("my_lock", "unique_id", 30s, sw::redis::UpdateType::NOT_EXIST)
    • 如果返回 true,代表该客户端成功获取了锁。unique_id 是一个随机字符串,用于标识锁的持有者。30s 的过期时间是为了防止客户端崩溃导致锁无法释放。
    • 如果返回 false,代表锁已被其他客户端持有,获取失败。
  2. 释放锁: 锁的持有者在完成任务后,需要通过一个脚本(确保原子性)来判断锁的 unique_id 是否与自己持有的一致,如果一致才执行 DEL 命令,防止误删他人的锁。

第四章:效率为王 —— MSETMGET 批量操作

假设你需要一次性设置或获取 100 个键值对。如果使用循环调用 100 次 SETGET,将会产生 100 次独立的网络往返(Round-Trip Time, RTT)。在网络延迟较高的环境中,这会极大地影响程序性能。

Redis 提供了 MSETMGET 命令,允许你在一次请求中处理多个键,将网络开销从 O(N) 降为 O(1)。

4.1 MSET:一次设置多个键值对

MSET 是一个原子性操作,它会一次性设置所有提供的键值对,要么全部成功,要么全部失败(例如在事务中)。

redis-plus-plus 提供了多种便捷的方式来调用 MSET

方法一:使用初始化列表

对于固定的、少量的键值对,使用 C++11 的初始化列表(initializer list)最为直观。

image.png
voidtest4_mset_initializer_list(sw::redis::Redis& redis){ std::cout <<"\n>> 4.1 演示 MSET (使用初始化列表)"<< std::endl; redis.flushall();// MSET 可以在一个原子操作中设置多个键值对// 使用 std::make_pair 或 C++17 的类模板参数推导 {"key", "value"} 均可 std::cout <<"一次性设置 key1, key2, key3"<< std::endl; redis.mset({ std::make_pair("key1","111"), std::make_pair("key2","222"),{"key3","333"}// C++17 更简洁的写法});// 逐一验证auto value1 = redis.get("key1");auto value2 = redis.get("key2");auto value3 = redis.get("key3"); std::cout <<"获取 'key1': "<< value1.value_or("N/A")<< std::endl; std::cout <<"获取 'key2': "<< value2.value_or("N/A")<< std::endl; std::cout <<"获取 'key3': "<< value3.value_or("N/A")<< std::endl;}
方法二:使用迭代器

当键值对是动态生成并存储在容器(如 std::vector)中时,使用迭代器的方式则更加灵活和强大。

image.png
#include<vector>#include<string>voidtest5_mset_mget_iterators(sw::redis::Redis& redis){ std::cout <<"\n>> 4.2 演示 MSET 和 MGET (使用迭代器)"<< std::endl; redis.flushall();// 1. 将多个键值对预先组织到容器中 std::vector<std::pair<std::string, std::string>> kvs ={{"key1","111"},{"key2","222"},{"key3","333"}}; std::cout <<"使用 vector 和迭代器进行 MSET"<< std::endl; redis.mset(kvs.begin(), kvs.end());// 2. 使用 MGET 一次性获取多个键的值 std::vector<std::string> keys_to_get ={"key1","key_nonexistent","key3"};// 准备一个容器来接收结果 std::vector<sw::redis::OptionalString> result_values;// 创建一个后插迭代器 (back-insert iterator)// 它会将 MGET 的输出逐个 push_back 到 result_values 中auto inserter = std::back_inserter(result_values); std::cout <<"\n使用 MGET 和输出迭代器获取 'key1', 'key_nonexistent', 'key3'"<< std::endl; redis.mget(keys_to_get.begin(), keys_to_get.end(), inserter);// 3. 遍历并打印结果 std::cout <<"MGET 返回结果:"<< std::endl;for(size_t i =0; i < keys_to_get.size();++i){constauto& key = keys_to_get[i];constauto& val = result_values[i];if(val){ std::cout <<" - "<< key <<": "<< val.value()<< std::endl;}else{ std::cout <<" - "<< key <<": (不存在)"<< std::endl;}}}
代码深度解析
  1. redis.mset(kvs.begin(), kvs.end()): mset 的这个重载版本接受一对迭代器,代表一个包含键值对的区间。这体现了 redis-plus-plus 与 C++ 标准模板库(STL)的无缝集成。
  2. MGET 与输出迭代器: MGET 的设计尤为出色。MGET 命令返回一个值的列表,其顺序与请求的键的顺序完全对应。如果某个键不存在,其对应位置返回的是 nil
    • std::vector<sw::redis::OptionalString> result_values: 接收结果的容器,元素类型必须是 OptionalString,以正确处理可能不存在的键。
    • auto inserter = std::back_inserter(result_values): 这是理解此模式的关键。std::back_inserter 是一个由标准库提供的“输出迭代器适配器”。它会创建一个特殊的迭代器,对这个迭代器进行赋值操作(如 *inserter = value),会被神奇地转换为对其关联容器的 push_back(value) 调用。
    • redis.mget(..., inserter): mget 方法在内部获取到 Redis 返回的每一个值后,就通过这个 inserter 迭代器将其“写入”。这避免了在 mget 函数内部创建并返回一个临时 vector(可能涉及额外的内存分配和拷贝),而是直接将结果填充到调用者提供的容器中,既高效又灵活。

第五章:精雕细琢 —— GETRANGESETRANGE 子串操作

有时候我们不需要获取或修改整个字符串,而只关心其中的一部分。GETRANGESETRANGE 就像是为 Redis 字符串提供了数组切片和修改的能力。

image.png
voidtest6_range_operations(sw::redis::Redis& redis){ std::cout <<"\n>> 5. 演示 GETRANGE 和 SETRANGE"<< std::endl; redis.flushall(); redis.set("key","Hello, Redis World!");// 1. GETRANGE: 获取子字符串// 索引从 0 开始,范围是 [start, end] (闭区间)// 获取 "Redis" std::cout <<"原始字符串: 'Hello, Redis World!'"<< std::endl; std::string substring = redis.getrange("key",7,11); std::cout <<"getrange('key', 7, 11) -> '"<< substring <<"'"<< std::endl;// 也支持负数索引,-1 表示最后一个字符// 获取 "World!" substring = redis.getrange("key",-6,-1); std::cout <<"getrange('key', -6, -1) -> '"<< substring <<"'"<< std::endl;// 2. SETRANGE: 覆写子字符串// 从指定偏移量开始,用新字符串覆盖旧内容// 将 "Redis" 替换为 "C++" std::cout <<"\n执行 setrange('key', 7, 'C++')"<< std::endl;// SETRANGE 返回被修改后字符串的总长度longlong new_len = redis.setrange("key",7,"C++");auto final_value = redis.get("key"); std::cout <<"修改后字符串: '"<< final_value.value_or("N/A")<<"'"<< std::endl; std::cout <<"SETRANGE 返回的新长度: "<< new_len << std::endl;// 如果偏移量超出当前字符串长度,中间会用空字节 (\x00) 填充 redis.setrange("key",25,"End"); std::cout <<"\n在远超末尾的位置执行 setrange('key', 25, 'End')"<< std::endl; std::cout <<"最终字符串: '"<< redis.get("key").value_or("N/A")<<"'"<< std::endl;}
代码深度解析
  • redis.getrange("key", 2, 5): 对应 GETRANGE key 2 5,它提取从索引 2 到索引 5(包括 2 和 5)的子串。Redis 的字符串是二进制安全的,这意味着它可以包含任何字节,包括 \0
  • redis.setrange("key", 2, "abc"): 对应 SETRANGE key 2 abc,从索引 2 开始,用 “abc” 覆盖原有的字符。如果原字符串长度不足,SETRANGE 会自动扩展字符串,并在必要时用空字节填充。
应用场景
  • 位图(Bitmap): 这是 GETRANGESETRANGE 的一个高级应用。通过将字符串视为一个位的序列,并使用 GETBITSETBITredis-plus-plus 也支持)命令,可以实现高效的位图数据结构,用于用户签到、在线状态统计等场景。
  • 固定大小的二进制数据块操作: 当你将一个结构体或复杂二进制数据序列化后存入 Redis 字符串时,SETRANGE 可以让你在不读取整个数据的情况下,精确地修改其中某一部分字段。

第六章:原子之力 —— INCRDECR 计数器

在Web应用中,计数器是一个极其常见的需求,如文章阅读量、用户点赞数、API 调用频率限制等。如果采用传统的“读取-修改-写回”模式,在高并发下会立刻遇到**竞争条件(Race Condition)**问题,导致计数不准。

Redis 的 INCRDECR 命令提供了一种原子性的解决方案。由于 Redis 的命令执行是单线程的,所以 INCR 操作从读取到写回的整个过程不会被任何其他命令打断,从而保证了计数的绝对准确性。

image.png
voidtest7_atomic_counters(sw::redis::Redis& redis){ std::cout <<"\n>> 6. 演示 INCR 和 DECR 原子计数器"<< std::endl; redis.flushall();// 1. 设置一个可以被解释为整数的字符串值 redis.set("counter","10"); std::cout <<"初始值 counter: "<< redis.get("counter").value_or("N/A")<< std::endl;// 2. INCR: 将 key 的值原子性地加 1// 命令的返回值是增加之后的新值longlong result = redis.incr("counter"); std::cout <<"\n执行 incr('counter') 后..."<< std::endl; std::cout <<" - INCR 命令返回值: "<< result << std::endl; std::cout <<" - Redis 中存储的值: "<< redis.get("counter").value_or("N/A")<< std::endl;// 3. DECR: 将 key 的值原子性地减 1 result = redis.decr("counter"); std::cout <<"\n执行 decr('counter') 后..."<< std::endl; std::cout <<" - DECR 命令返回值: "<< result << std::endl; std::cout <<" - Redis 中存储的值: "<< redis.get("counter").value_or("N/A")<< std::endl;// 4. INCRBY/DECRBY: 按指定步长增减 result = redis.incrby("counter",5); std::cout <<"\n执行 incrby('counter', 5) 后..."<< std::endl; std::cout <<" - INCRBY 命令返回值: "<< result << std::endl;// 5. 对不存在的 key 执行 INCR// Redis 会将其视为 0,然后执行操作 result = redis.incr("new_counter"); std::cout <<"\n对不存在的 'new_counter' 执行 incr 后..."<< std::endl; std::cout <<" - INCR 命令返回值: "<< result << std::endl; std::cout <<" - Redis 中存储的值: "<< redis.get("new_counter").value_or("N/A")<< std::endl;// 6. 浮点数增减 redis.set("float_counter","10.5");double float_result = redis.incrbyfloat("float_counter",2.1); std::cout <<"\n执行 incrbyfloat('float_counter', 2.1) 后..."<< std::endl; std::cout <<" - INCRBYFLOAT 命令返回值: "<< float_result << std::endl;}
代码深度解析
  • long long result = redis.incr("key"): incr 方法对应 Redis 的 INCR 命令。它会尝试将 key 对应的字符串值解析为 64 位有符号整数,对其加 1,然后将新值存回。最关键的是,该方法返回的是执行加法之后的新值。这一点非常有用。
  • 数据类型: INCRDECR 只能对可以被解释为整数的字符串操作,否则 Redis 会报错。
  • 原子性保证: INCR 的原子性是 Redis 最强大的特性之一。它简化了并发编程,让你无需关心复杂的加锁机制就能实现可靠的计数器。
  • 扩展命令:
    • INCRBY/DECRBY: 按指定的整数步长进行增减。
    • INCRBYFLOAT: 按指定的浮点数步长进行增减,这使得 Redis 也可以用作浮点数计数器。
应用场景
  • 网站计数器: 统计页面浏览量(PV)、用户独立访客数(UV)。
  • 限流器(Rate Limiting): 可以为每个用户或 IP 创建一个计数器,例如 rate:limit:user_id:api_endpoint,并为其设置 1 分钟的过期时间。每次请求时执行 INCR,如果返回值超过了预设的阈值(如 100),则拒绝该请求。

总结

通过本文的深入探讨,我们全面地学习了 Redis String 类型的核心命令,并掌握了如何使用 redis-plus-plus 这一优秀的 C++ 库在实际项目中应用它们。

我们从最基础的 SETGET 出发,理解了 redis-plus-plus 借助 std::optional 带来的类型安全;我们学会了使用 std::chrono 为键赋予生命周期,使其能够自动过期;我们探索了 NX/XX 选项在实现分布式锁等场景下的精妙用途;我们领略了 MSET/MGET 结合迭代器模式在批量操作中带来的巨大性能提升;我们还实践了 GETRANGE/SETRANGE 对字符串的局部精细操作,以及 INCR/DECR 家族在构建高并发原子计数器时的核心价值。

Redis String 远不止是一个简单的键值存储。它是一个功能强大、灵活多变的多面手。熟练掌握其各种命令,并结合像 redis-plus-plus 这样设计精良的客户端库,将使您在构建高性能、高并发的 C++ 应用时如虎添翼。希望本文能成为您 Redis C++ 开发道路上一份有价值的参考。

Read more

鸿蒙 App 如何设计 AI 原生的信息架构

鸿蒙 App 如何设计 AI 原生的信息架构

子玥酱(掘金 / 知乎 / ZEEKLOG / 简书 同名) 大家好,我是子玥酱,一名长期深耕在一线的前端程序媛 👩‍💻。曾就职于多家知名互联网大厂,目前在某国企负责前端软件研发相关工作,主要聚焦于业务型系统的工程化建设与长期维护。 我持续输出和沉淀前端领域的实战经验,日常关注并分享的技术方向包括前端工程化、小程序、React / RN、Flutter、跨端方案, 在复杂业务落地、组件抽象、性能优化以及多端协作方面积累了大量真实项目经验。 技术方向:前端 / 跨端 / 小程序 / 移动端工程化 内容平台:掘金、知乎、ZEEKLOG、简书 创作特点:实战导向、源码拆解、少空谈多落地 文章状态:长期稳定更新,大量原创输出 我的内容主要围绕 前端技术实战、真实业务踩坑总结、框架与方案选型思考、行业趋势解读 展开。文章不会停留在“API 怎么用”,而是更关注为什么这么设计、在什么场景下容易踩坑、

By Ne0inhk

Node.js 安装指南(Mac 版本)

第一章:准备工作与环境检查 1.1 确认系统要求 在开始安装 Node.js 之前,首先需要确认您的 Mac 系统是否符合要求: 系统版本要求: * macOS 10.10 (Yosemite) 或更高版本 * 推荐使用 macOS 10.15 (Catalina) 或更新版本 * 同时支持 Intel 和 Apple Silicon (M1/M2) 芯片 检查您的 macOS 版本: 1. 点击屏幕左上角的 Apple 菜单 2. 选择"关于本机" 3. 查看显示的版本信息 通过终端检查: bash sw_vers

By Ne0inhk
Flutter for OpenHarmony:phone_numbers_parser 国际电话号码的解析、验证与格式化(全球化号码处理) 深度解析与鸿蒙适配指南

Flutter for OpenHarmony:phone_numbers_parser 国际电话号码的解析、验证与格式化(全球化号码处理) 深度解析与鸿蒙适配指南

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 随着应用出海,处理全球各地的手机号码成为刚需。不同国家的号码格式千奇百怪:有的带国家码(+86),有的带括号,有的带空格。如何验证用户输入的号码是否合法?如何将其格式化为标准的 E.164 用于后端存储? phone_numbers_parser 是 Dart 生态中优秀的电话号码处理库,它是 Google libphonenumber 的轻量级 Dart 移植版,不依赖任何原生代码,因此在 OpenHarmony 上运行时无需任何额外配置,且包体积极小。 一、概念介绍/原理解析 1.1 基础概念 * E.164: 国际电信联盟定义的标准号码格式(如 +8613800138000),后端存储通常使用此格式。 * National: 本地显示格式(如

By Ne0inhk

Flutter for OpenHarmony: Flutter 三方库 plugin_platform_interface 规范鸿蒙插件跨端接口契约(插件开发标准指南)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在进行 OpenHarmony 插件开发时,一个核心挑战是如何确保你的插件在 Android、iOS 和鸿蒙等多端表现一致。为了保证扩展的可测试性和规范性,Flutter 团队提出了一套“基于接口”的插件架构规范。 plugin_platform_interface 正是实现这一架构的官方基石。它通过强行校验开发者是否继承了特定的基类,确保任何三方开发者(或你自己在进行鸿蒙适配时)在模拟或重写平台库时,都能遵循严格的协议契约,防止因漏写方法而导致的运行时崩溃。 一、标准分层插件架构 该库致力于定义中间的“平台接口层(Platform Interface)”。 注册实现 注册实现 通过校验器 Flutter App 插件 API (面向用户) Platform Interface (定义契约) 鸿蒙特定实现 (ArkTS 交互) Android 特定实现

By Ne0inhk