百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

Redis C++使用(redis -c)

mhr18 2025-04-30 18:15 31 浏览 0 评论

一、Redis的自定义网络协议

1.1 为什么可以编写出一个自定义的Redis客户端

为什么我们可以编写出一个自定义的Redis客户端?因为Redis公开了自己的自定义协议。而对于一些其他软件的客户端,我们无法编写出一个自定义的Redis客户端,因为他们没有公开自己的自定义协议,但是我们可以通过一些抓包/逆向的手段猜测其应用层协议是什么样子的!!

在网络通信过程中,会用到很多的“协议”,比如数据链路层的以太网协议,网络层的IP协议,传输层的TCP/UDP协议,应用层的协议更多!!虽然业界中有很多成熟的应用层协议:HTTP等~~但是此处更多的时候,都会“自定义”应用层协议,Redis此处的应用层协议就是自定义的协议(传输层还是基于TCP)~~

1.2 RESP protocol spec

RESP 协议的优点:

简单好实现

快速进行解析

肉眼可读

传输层这里基于TCP,但是又和TCP没有强耦合。

请求和响应之间的通信模型是一问一答的形式~~(客户端给服务器发送一个请求,服务器返回一个响应)

我们需要根据上述规则进行字符串的编写,然后将这个字符串写入到 tcp socket 中。因此,redis客户端服务器要做的工作是:

按照上述格式,构造出字符串,往 socket 中写入

从 socket 中读取字符串,按照上述格式解析

二、安装 redis-plus-plus

由于 redis-plus-plus 依赖了 hiredis(C语言版本的redis客户端库),所以我们要先下载 hiredis 库(可以直接使用包管理器来安装)。

但是 redis-plus-plus 本体,只能编译安装了。如果是编译安装,使用 Ubuntu 比 使用 centos 简单很多。redis-plus-plus 本身功能比较简陋,比较原始,写起来也比较麻烦,实际开发中很少会手写 makefile。通过程序来生成 makefile,cmake就是一个生成 makefile 的工具。

先来看一下如何使用 CMake 编译程序:

创建一个 build 目录是习惯做法,并非是必须,目的是为了让编译生成的临时文件都放到build下面,避免污染源代码目录~

cmake .. 这个操作是生成 makefile,此处的 .. 指向的是刚才 CMakeLists.txt 文件所在的目录~~

make 进行编译

make install 把刚才的库拷贝到系统目录

三、进行 ping 命令验证

#include <sw/redis++/redis++.h>

1.包含 redis-plus-plus 的头文件,如果我们不知道将这个库下载到哪里,我们可以使用 find 命令进行查找,命令如下所示:

find /XXX(路径) -name XXX 

2.创建了一个 Redis 对象

sw::redis::Redis redis("tcp://127.0.0.1:6379);

3.进行 ping 命令

4.使用 Makefile 编译程序

编译程序的时候,需要引入一些库文件(需要知道这些库文件的目录):

1.redis++ 自己的静态库

2.hiredis 的静态库

3.线程库

四、Redis 的通用命令的使用

一览整个Redis通用命令的使用:

get/set
exists
del
keys
expire/ttl
type

4.1 get/set

在 C++ 中,std::string 是可以修改,既能读,也能写。但是 StringView 是只读的(不能修改),针对只读操作,做很多的优化工作,效率比 std::string 更高。在 C++17 标准库中,也提供了一个 std::string_view。这里是为了兼容 C++11,14,17,所以自己封装了一个类型。StringView 中的各个操作和string类似,只不过只是包含了一些只读方法。

在 Java 中的 String 就是类似于 StringView 只读的,Java 中要想使用可修改的字符串,要使用 StringBuilder 或者 StringBuffer。

对于 get 来说,有可能获取不到元素,这时应该返回什么类型呢??

如果直接使用 std::string 来表示,不方便来表现这个 nil(无效值),如果使用 std::string* 来表示,是可以使用 nullptr 表示无效的,但是返回指针又涉及到内存归谁管~~

因此,作者就自己封装了一个类型,此处的 Optional 可以表示 “非法值” 或者 “无效值”。在 Boost 中,很早就引入了 optional 类型,C++14版本中,就正式归纳标准库了。

在使用 Optional 类型的时候,有可能出现以下这个错误:

此处不需要给这个 Optional 类型搞一个 << 重载,只需要把 Optional 里面包含的元素取出来即可~~

void test1(sw::redis::Redis& redis) {
    std::cout << "get 和 set 的使用" << std::endl;
 
    // 清空一下数据库, 避免之前残留的数据有干扰. 
    redis.flushall();
 
    // 使用 set 设置 key
    redis.set("key1", "111");
    redis.set("key2", "222");
    redis.set("key3", "333");
 
    // 使用 get 获取到 key 对应的 value
    auto value1 = redis.get("key1");
    // optional 可以隐式转成 bool 类型, 可以直接在 if 中判定. 如果是无效元素, 就是返回 false
    if (value1) {
        std::cout << "value1=" << value1.value() << std::endl;
    }
 
    auto value2 = redis.get("key2");
    if (value2) {
        std::cout << "value2=" << value2.value() << std::endl;
    }
 
    auto value3 = redis.get("key3");
    if (value3) {
        std::cout << "value3=" << value3.value() << std::endl;
    }
 
    auto value4 = redis.get("key4");
    if (value4) {
        std::cout << "value4=" << value4.value() << std::endl;
    }
}

4.2 exists

void test2(sw::redis::Redis& redis) {
    std::cout << "exists" << std::endl;
 
    redis.flushall();
 
    redis.set("key", "111");
    redis.set("key3", "111");
 
    auto ret = redis.exists("key");
    std::cout << ret << std::endl;
 
    ret = redis.exists("key2");
    std::cout << ret << std::endl;
 
    ret = redis.exists({"key", "key2", "key3"});
    std::cout << ret << std::endl;
}

对于 exists 命令来说,我们可以一次性查看多个键值,我们可以使用初始化列表传参,代码如下:

redis.exists({"key", "key2", "key3"});

4.3 del

void test3(sw::redis::Redis& redis) {
std::cout << "del" << std::endl;
// 清除库非常必要的!
redis.flushall();

redis.set("key", "111");
redis.set("key2", "111");

// redis.del("key");

auto ret = redis.del({"key", "key2", "key3"});
std::cout << ret << std::endl;

ret = redis.exists({"key", "key2"});
std::cout << ret << std::endl;
}

4.4 keys

keys 命令不可以随便使用,否则会影响其他命令的执行,因为 Redis 是单线程。keys 的返回值有多个。其返回值类型为:

这是插入迭代器,插入迭代器的本质是一种“输出迭代器”,通常,一个输出迭代器主要表示一个位置。插入迭代器,则是“位置” + “动作”。 插入迭代器总共有三种类型:

这里直接使用容器作为参数,keys内部直接操作容器,进行插入不是更好吗,为什么要通过迭代器呢??

因为可以解耦合。

void test4(sw::redis::Redis& redis) {
    std::cout << "keys" << std::endl;
    redis.flushall();
 
    redis.set("key", "111");
    redis.set("key2", "222");
    redis.set("key3", "333");
    redis.set("key4", "444");
    redis.set("key5", "555");
    redis.set("key6", "666");
 
    // keys 的第二个参数, 是一个 "插入迭代器". 咱们需要先准备好一个保存结果的容器. 
    // 接下来再创建一个插入迭代器指向容器的位置. 就可以把 keys 获取到的结果依次通过刚才的插入迭代器插入到容器的指定位置中了. 
    vector<string> result;
    auto it = std::back_inserter(result);
    redis.keys("*", it);
    printContainer(result);
}

4.5 expire/ttl

void test5(sw::redis::Redis& redis) {
    using namespace std::chrono_literals;
 
    std::cout << "expire and ttl" << std::endl;
    redis.flushall();
 
    redis.set("key", "111");
    // 10s => std::chrono::seconds(10)
    redis.expire("key", 10s);
 
    std::this_thread::sleep_for(3s);
 
    auto time = redis.ttl("key");
    std::cout << time << std::endl;
}

在使用睡眠函数的时候,由于不同系统之间的单位不同,我们更好的选择是使用线程库中的睡眠函数:sleep_for。

Linux的sleep和Windows的Sleep,都属于系统函数,是和系统相关的,同样的功能,在不同系统中可能是完全不同的函数~

4.6 type

void test6(sw::redis::Redis& redis) {
    std::cout << "type" << std::endl;
    redis.flushall();
 
    redis.set("key", "111");
    string result = redis.type("key");
    std::cout << "key: " << result << std::endl;
 
    redis.lpush("key2", "111");
    result = redis.type("key2");
    std::cout << "key2: " << result << std::endl;
 
    redis.hset("key3", "aaa", "111");
    result = redis.type("key3");
    std::cout << "key3: " << result << std::endl;
 
    redis.sadd("key4", "aaa");
    result = redis.type("key4");
    std::cout << "key4: " << result << std::endl;
 
    redis.zadd("key5", "吕布", 99);
    result = redis.type("key5");
    std::cout << "key5: " << result << std::endl;
}

五、string类型的操作

5.1 get/set

void test1(Redis& redis) {
    std::cout << "get 和 set" << std::endl;
    redis.flushall();
 
    redis.set("key", "111");
    auto value = redis.get("key");
    if (value) {
        std::cout << "value: " << value.value() << std::endl;
    }
 
    redis.set("key", "222");
    value = redis.get("key");
    if (value) {
        std::cout << "value: " << value.value() << std::endl;
    }
}

5.2 set带有超时时间

void test2(Redis& redis) {
    std::cout << "set 带有超时时间" << std::endl;
    redis.flushall();
 
    redis.set("key", "111", 10s);
 
    std::this_thread::sleep_for(3s);
 
    long long time = redis.ttl("key");
    std::cout << "time: " << time << std::endl;
}

5.3 set NX/XX

void test3(Redis& redis) {
    std::cout << "set NX 和 XX" << std::endl;
    redis.flushall();
 
    redis.set("key", "111");
 
    // set 的重载版本中, 没有单独提供 NX 和 XX 的版本, 必须搭配过期时间的版本来使用. 
    redis.set("key", "222", 0s, sw::redis::UpdateType::EXIST);
 
    auto value = redis.get("key");
    if (value) {
        std::cout << "value: " << value.value() << std::endl;
    } else {
        std::cout << "key 不存在!" << std::endl;
    }
}

5.4 mset

void test4(Redis& redis) {
    std::cout << "mset" << std::endl;
 
    redis.flushall();
 
    // 第一种写法, 使用初始化列表描述多个键值对
    // redis.mset({ std::make_pair("key1", "111"), std::make_pair("key2", "222"), std::make_pair("key3", "333") });
 
    // 第二种写法, 可以把多个键值对提前组织到容器中. 以迭代器的形式告诉 mset
    vector<std::pair<string, string>> keys = {
        {"key1", "111"},
        {"key2", "222"},
        {"key3", "333"}
    };
    redis.mset(keys.begin(), keys.end());
 
    auto value = redis.get("key1");
    if (value) {
        std::cout << "value: " << value.value() << std::endl;
    }
 
    value = redis.get("key2");
    if (value) {
        std::cout << "value: " << value.value() << std::endl;
    }
 
    value = redis.get("key3");
    if (value) {
        std::cout << "value: " << value.value() << std::endl;
    }
}

5.5 mget

void test5(Redis& redis) {
    std::cout << "mget" << std::endl;
    redis.flushall();
 
    vector<std::pair<string, string>> keys = {
        {"key1", "111"},
        {"key2", "222"},
        {"key3", "333"}
    };
    redis.mset(keys.begin(), keys.end());
 
    vector<sw::redis::OptionalString> result;
    auto it = std::back_inserter(result);
    redis.mget({"key1", "key2", "key3", "key4"}, it);
 
    printContainerOptional(result);
}

5.6 getrange/setrange

void test6(Redis& redis) {
    std::cout << "getrange 和 setrange" << std::endl;
    redis.flushall();
 
    redis.set("key", "abcdefghijk");
 
    string result = redis.getrange("key", 2, 5);
    std::cout << "result: " << result << std::endl;
 
    redis.setrange("key", 2, "xyz");
 
    auto value = redis.get("key");
    std::cout << "value: " << value.value() << std::endl;
}

5.7 incr/decr

incr 和 decr 得到的是 long long 类型(使用这个更多一些),get 得到的是 OptionalString 类型,需要手动转成数字~C++中把字符串转成数字,也有很多种方法。

void test7(Redis& redis) {
    std::cout << "incr 和 decr" << std::endl;
    redis.flushall();
 
    redis.set("key", "100");
 
    long long result = redis.incr("key");
    std::cout << "result: " << result << std::endl;
 
    auto value = redis.get("key");
    std::cout << "value: " << value.value() << std::endl;
 
    result = redis.decr("key");
    std::cout << "result: " << result << std::endl;
 
    value = redis.get("key");
    std::cout << "value: " << value.value() << std::endl;
}

六、list类型的操作

6.1 lpush/prange

void test1(Redis& redis) {
    std::cout << "lpush 和 lrange" << std::endl;
    redis.flushall();
 
    // 插入单个元素
    redis.lpush("key", "111");
 
    // 插入一组元素, 基于初始化列表
    redis.lpush("key", {"222", "333", "444"});
 
    // 插入一组元素, 基于迭代器
    vector<string> values = {"555", "666", "777"};
    redis.lpush("key", values.begin(), values.end());
 
    // lrange 获取到列表中的元素
    vector<string> results;
    auto it = std::back_inserter(results);
    redis.lrange("key", 0, -1, it);
 
    printContainer(results);
}

6.2 rpush

void test2(Redis& redis) {
    std::cout << "rpush" << std::endl;
    redis.flushall();
 
    // 插入单个元素
    redis.rpush("key", "111");
 
    // 插入多个元素, 基于初始化列表
    redis.rpush("key", {"222", "333", "444"});
 
    // 插入多个元素, 基于容器
    vector<string> values = {"555", "666", "777"};
    redis.rpush("key", values.begin(), values.end());
 
    // 使用 lrange 获取元素
    vector<string> results;
    auto it = std::back_inserter(results);
    redis.lrange("key", 0, -1, it);
 
    printContainer(results);
}

6.3 lpop/rpop

void test3(Redis& redis) {
    std::cout << "lpop 和 rpop" << std::endl;
    redis.flushall();
 
    // 构造一个 list
    redis.rpush("key", {"1", "2", "3", "4"});
 
    auto result = redis.lpop("key");
    if (result) {
        std::cout << "lpop: " << result.value() << std::endl;
    }
 
    result = redis.rpop("key");
    if (result) {
        std::cout << "rpop: " << result.value() << std::endl;
    }
}

6.4 blpop

void test4(Redis& redis) {
    using namespace std::chrono_literals;
    std::cout << "blpop" << std::endl;
    redis.flushall();
 
    auto result = redis.blpop({"key", "key2", "key3"}, 10s);
    if (result) {
        std::cout << "key:" << result->first << std::endl;
        std::cout << "elem:" << result->second << std::endl;
    } else {
        std::cout << "result 无效!" << std::endl;
    }
}

6.5 llen

void test5(Redis& redis) {
    std::cout << "llen" << std::endl;
    redis.flushall();
 
    redis.lpush("key", {"111", "222", "333", "444"});
    long long len = redis.llen("key");
    std::cout << "len: " << len << std::endl;
}

对于 redis-plus-plus 这个库来说,接口风格的设计是非常统一的。

当一个函数参数需要传递多个值的时候,往往都是支持初始化列表或者一对迭代器的方式来进行实现的

当一个函数的返回值需要表示多个数据的时候,也往往会借助插入迭代器来实现往一个容器中添加元素的效果

当某些场景涉及到无效值的时候,往往会搭配 std::optional 来使用

七、set类型的操作

7.1 sadd/smembers

void test1(Redis& redis) {
    std::cout << "sadd 和 smembers" << std::endl;
    redis.flushall();
 
    // 一次添加一个元素
    redis.sadd("key", "111");
 
    // 一次添加多个元素(使用初始化列表)
    redis.sadd("key", {"222", "333", "444"});
 
    // 一次添加多个元素(使用迭代器)
    set<string> elems = {"555", "666", "777"};
    redis.sadd("key", elems.begin(), elems.end());
 
    // 获取到上述元素
    // 此处用来保存 smembers 的结果, 使用 set 可能更合适. 
    vector<string> result;
    // auto it = std::back_inserter(result);
    // 由于此处 set 里的元素顺序是固定的. 指定一个 result.end() 或者 result.begin() 或者其他位置的迭代器, 都无所谓~~
    auto it = std::inserter(result, result.end());
    redis.smembers("key", it);
 
    printContainer(result);
}

7.2 sismember

void test2(Redis& redis) {
    std::cout << "sismember" << std::endl;
    redis.flushall();
 
    redis.sadd("key", {"111", "222", "333", "444"});
 
    bool result = redis.sismember("key", "555");
    std::cout << "result: " << result << std::endl;
}

7.3 scrad

void test3(Redis& redis) {
    std::cout << "scard" << std::endl;
    redis.flushall();
 
    redis.sadd("key", {"111", "222", "333"});
    long long result = redis.scard("key");
    std::cout << "result: " << result << std::endl;
}

7.4 spop

void test4(Redis& redis) {
    std::cout << "spop" << std::endl;
    redis.flushall();
 
    redis.sadd("key", {"111", "222", "333", "444"});
    auto result = redis.spop("key");
    if (result) {
        std::cout << "result: " << result.value() << std::endl;
    } else {
        std::cout << "result 无效!" << std::endl;
    }
}

7.5 sinter

void test5(Redis& redis) {
    std::cout << "sinter" << std::endl;
    redis.flushall();
 
    redis.sadd("key1", {"111", "222", "333"});
    redis.sadd("key2", {"111", "222", "444"});
 
    set<string> result;
    auto it = std::inserter(result, result.end());
    redis.sinter({"key1", "key2"}, it);
 
    printContainer(result);
}

7.6 sinterstore

void test6(Redis& redis) {
    std::cout << "sinterstore" << std::endl;
    redis.flushall();
 
    redis.sadd("key1", {"111", "222", "333"});
    redis.sadd("key2", {"111", "222", "444"});
 
    long long len = redis.sinterstore("key3", {"key1", "key2"});
    std::cout << "len: " << len << std::endl;
 
    set<string> result;
    auto it = std::inserter(result, result.end());
    redis.smembers("key3", it);
 
    printContainer(result);
}

八、hash类型的操作

8.1 hset/hget

void test2(Redis& redis) {
    std::cout << "hexits" << std::endl;
    redis.flushall();
 
    redis.hset("key", "f1", "111");
    redis.hset("key", "f2", "222");
    redis.hset("key", "f3", "333");
 
    bool result = redis.hexists("key", "f4");
    std::cout << "result: " << result << std::endl;
}

8.2 hexists

void test2(Redis& redis) {

std::cout << "hexits" << std::endl;

redis.flushall();

redis.hset("key", "f1", "111");

redis.hset("key", "f2", "222");

redis.hset("key", "f3", "333");

bool result = redis.hexists("key", "f4");

std::cout << "result: " << result << std::endl;

}

8.3 hdel

void test3(Redis& redis) {
    std::cout << "hdel" << std::endl;
    redis.flushall();
 
    redis.hset("key", "f1", "111");
    redis.hset("key", "f2", "222");
    redis.hset("key", "f3", "333");
 
    long long result = redis.hdel("key", "f1");
    std::cout << "result: " << result << std::endl;
 
    result = redis.hdel("key", {"f2", "f3"});
    std::cout << "result: " << result << std::endl;
 
    long long len = redis.hlen("key");
    std::cout << "len: " << len << std::endl;
}

8.4 hkeys/hvals

void test4(Redis& redis) {
    std::cout << "hkeys 和 hvals" << std::endl;
    redis.flushall();
 
    redis.hset("key", "f1", "111");
    redis.hset("key", "f2", "222");
    redis.hset("key", "f3", "333");
 
    vector<string> fields;
    auto itFields = std::back_inserter(fields);
    redis.hkeys("key", itFields);
    printContainer(fields);
 
    vector<string> values;
    auto itValues = std::back_inserter(values);
    redis.hvals("key", itValues);
    printContainer(values);
}

8.5 hmget/hmset

void test5(Redis& redis) {

std::cout << "hmget 和 hmset" << std::endl;

redis.flushall();

redis.hmset("key", {

std::make_pair("f1", "111"),

std::make_pair("f2", "222"),

std::make_pair("f3", "333")

});

vector<std::pair<string, string>> pairs = {

std::make_pair("f4", "444"),

std::make_pair("f5", "555"),

std::make_pair("f6", "666")

};

redis.hmset("key", pairs.begin(), pairs.end());

vector<string> values;

auto it = std::back_inserter(values);

redis.hmget("key", {"f1", "f2", "f3"}, it);

printContainer(values);

}

九、zset类型的操作

void test1(Redis& redis) {
    std::cout << "zadd 和 zrange" << std::endl;
    redis.flushall();
 
    redis.zadd("key", "吕布", 99);
    redis.zadd("key", {
        std::make_pair("赵云", 98),
        std::make_pair("典韦", 97)
    });
    vector<std::pair<string, double>> members = {
        std::make_pair("关羽", 95),
        std::make_pair("张飞", 93)
    };
    redis.zadd("key", members.begin(), members.end());
 
    // zrange 支持两种主要的风格:
    // 1. 只查询 member, 不带 score
    // 2. 查询 member 同时带 score
    // 关键就是看插入迭代器指向的容器的类型. 
    // 指向的容器只是包含一个 string, 就是只查询 member
    // 指向的容器包含的是一个 pair, 里面有 string 和 double, 就是查询 member 同时带有 score
    vector<string> memberResults;
    auto it = std::back_inserter(memberResults);
    redis.zrange("key", 0, -1, it);
    printContainer(memberResults);
 
    vector<std::pair<string, double>> membersWithScore;
    auto it2 = std::back_inserter(membersWithScore);
    redis.zrange("key", 0, -1, it2);
    printContainerPair(membersWithScore);
}
 
void test2(Redis& redis) {
    std::cout << "zcard" << std::endl;
    redis.flushall();
 
    redis.zadd("key", "zhangsan", 90);
    redis.zadd("key", "lisi", 91);
    redis.zadd("key", "wangwu", 92);
    redis.zadd("key", "zhaoliu", 93);
 
    long long result = redis.zcard("key");
    std::cout << "result: " << result << std::endl;
}
 
void test3(Redis& redis) {
    std::cout << "zrem" << std::endl;
    redis.flushall();
 
    redis.zadd("key", "zhangsan", 90);
    redis.zadd("key", "lisi", 91);
    redis.zadd("key", "wangwu", 92);
    redis.zadd("key", "zhaoliu", 93);
 
    redis.zrem("key", "zhangsan");
 
    long long result = redis.zcard("key");
    std::cout << "result: " << result << std::endl;
}
 
void test4(Redis& redis) {
    std::cout << "zscore" << std::endl;
    redis.flushall();
 
    redis.zadd("key", "zhangsan", 90);
    redis.zadd("key", "lisi", 91);
    redis.zadd("key", "wangwu", 92);
    redis.zadd("key", "zhaoliu", 93);
 
    auto score = redis.zscore("key", "zhangsan");
    if (score) {
        std::cout << "score: " << score.value() << std::endl;
    } else {
        std::cout << "score 无效" << std::endl;
    }
}
 
void test5(Redis& redis) {
    std::cout << "zrank" << std::endl;
    redis.flushall();
 
    redis.zadd("key", "zhangsan", 90);
    redis.zadd("key", "lisi", 91);
    redis.zadd("key", "wangwu", 92);
    redis.zadd("key", "zhaoliu", 93);
 
    auto rank = redis.zrank("key", "zhaoliu");
    if (rank) {
        std::cout << "rank: " << rank.value() << std::endl;
    } else {
        std::cout << "rank 无效" << std::endl;
    }
}

相关推荐

【推荐】一个开源免费、AI 驱动的智能数据管理系统,支持多数据库

如果您对源码&技术感兴趣,请点赞+收藏+转发+关注,大家的支持是我分享最大的动力!!!.前言在当今数据驱动的时代,高效、智能地管理数据已成为企业和个人不可或缺的能力。为了满足这一需求,我们推出了这款开...

Pure Storage推出统一数据管理云平台及新闪存阵列

PureStorage公司今日推出企业数据云(EnterpriseDataCloud),称其为组织在混合环境中存储、管理和使用数据方式的全面架构升级。该公司表示,EDC使组织能够在本地、云端和混...

对Java学习的10条建议(对java课程的建议)

不少Java的初学者一开始都是信心满满准备迎接挑战,但是经过一段时间的学习之后,多少都会碰到各种挫败,以下北风网就总结一些对于初学者非常有用的建议,希望能够给他们解决现实中的问题。Java编程的准备:...

SQLShift 重大更新:Oracle→PostgreSQL 存储过程转换功能上线!

官网:https://sqlshift.cn/6月,SQLShift迎来重大版本更新!作为国内首个支持Oracle->OceanBase存储过程智能转换的工具,SQLShift在过去一...

JDK21有没有什么稳定、简单又强势的特性?

佳未阿里云开发者2025年03月05日08:30浙江阿里妹导读这篇文章主要介绍了Java虚拟线程的发展及其在AJDK中的实现和优化。阅前声明:本文介绍的内容基于AJDK21.0.5[1]以及以上...

「松勤软件测试」网站总出现404 bug?总结8个原因,不信解决不了

在进行网站测试的时候,有没有碰到过网站崩溃,打不开,出现404错误等各种现象,如果你碰到了,那么恭喜你,你的网站出问题了,是什么原因导致网站出问题呢,根据松勤软件测试的总结如下:01数据库中的表空间不...

Java面试题及答案最全总结(2025版)

大家好,我是Java面试陪考员最近很多小伙伴在忙着找工作,给大家整理了一份非常全面的Java面试题及答案。涉及的内容非常全面,包含:Spring、MySQL、JVM、Redis、Linux、Sprin...

数据库日常运维工作内容(数据库日常运维 工作内容)

#数据库日常运维工作包括哪些内容?#数据库日常运维工作是一个涵盖多个层面的综合性任务,以下是详细的分类和内容说明:一、数据库运维核心工作监控与告警性能监控:实时监控CPU、内存、I/O、连接数、锁等待...

分布式之系统底层原理(上)(底层分布式技术)

作者:allanpan,腾讯IEG高级后台工程师导言分布式事务是分布式系统必不可少的组成部分,基本上只要实现一个分布式系统就逃不开对分布式事务的支持。本文从分布式事务这个概念切入,尝试对分布式事务...

oracle 死锁了怎么办?kill 进程 直接上干货

1、查看死锁是否存在selectusername,lockwait,status,machine,programfromv$sessionwheresidin(selectsession...

SpringBoot 各种分页查询方式详解(全网最全)

一、分页查询基础概念与原理1.1什么是分页查询分页查询是指将大量数据分割成多个小块(页)进行展示的技术,它是现代Web应用中必不可少的功能。想象一下你去图书馆找书,如果所有书都堆在一张桌子上,你很难...

《战场兄弟》全事件攻略 一般事件合同事件红装及隐藏职业攻略

《战场兄弟》全事件攻略,一般事件合同事件红装及隐藏职业攻略。《战场兄弟》事件奖励,事件条件。《战场兄弟》是OverhypeStudios制作发行的一款由xcom和桌游为灵感来源,以中世纪、低魔奇幻为...

LoadRunner(loadrunner录制不到脚本)

一、核心组件与工作流程LoadRunner性能测试工具-并发测试-正版软件下载-使用教程-价格-官方代理商的架构围绕三大核心组件构建,形成完整测试闭环:VirtualUserGenerator(...

Redis数据类型介绍(redis 数据类型)

介绍Redis支持五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)及Zset(sortedset:有序集合)。1、字符串类型概述1.1、数据类型Redis支持...

RMAN备份监控及优化总结(rman备份原理)

今天主要介绍一下如何对RMAN备份监控及优化,这里就不讲rman备份的一些原理了,仅供参考。一、监控RMAN备份1、确定备份源与备份设备的最大速度从磁盘读的速度和磁带写的带度、备份的速度不可能超出这两...

取消回复欢迎 发表评论: