当前位置: 首页 > article >正文

Redis从简单使用到底层原理与分布式缓存

文章目录

  • ==[Redis参考手册](https://redis.io/docs/latest/commands/)==
  • 1 基础认识
    • 1.1 安装配置
    • 1.2 通用命令
    • 1.3 数据类型
      • 1.3.1 数据结构与内部编码
      • string
      • key的结构
      • hash
      • list
      • set
      • sorted_set
    • 1.4 单线程模型
  • 2 redis客户端
    • 2.1 RESP协议(Redis serialization protocol)
    • 2.2 基础案例
  • 3 分布式缓存 —— redis集群
    • 3.1 持久化
      • 3.1.1 RDB
      • 3.1.2 RDB底层原理
      • 3.1.3 AOF持久化
      • 3.1.4 AOF与RDB的对比
    • 3.2 Redis主从
      • 3.2.1 搭建主从集群
      • 3.2.2 数据同步原理
    • 3.3 Redis哨兵
        • Sentinel监控原理
        • 选择新的master
        • 如何实现故障转移
      • 3.3.1 搭建哨兵集群
      • 3.3.2 Redis客户端状态感知
    • 3.4 Redis分片集群
      • 3.4.1 搭建分片集群
      • 3.4.2 散列插槽
      • 3.4.3 集群伸缩
      • 3.4.4 故障转移
  • 4 多级缓存
    • 4.1 JVM进程缓存
    • 4.2 OpenResty
    • 4.3 缓存同步
      • 4.3.1 Canel
  • 5 Redis实践优化
    • 5.1 key的设计
    • 5.2 批处理优化
      • 5.2.1 集群模式下的批处理
    • 5.3 服务端优化
      • 5.3.1 持久化配置
      • 5.3.2 慢查询
      • 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
      • 5.3.3 服务器安全配置
      • 5.3.4 内存配置
      • 5.3.5 集群相关问题
  • 6 Redis底层原理
    • 6.1 底层数据结构
      • 6.1.1 SDS动态数组
      • 6.1.2 Intset
      • 6.1.3 Dict
      • 6.1.4 ZipList
      • 6.1.5 QuickList
      • 6.1.5 SkipList
      • 6.1.6 RedisObject
    • 6.2 五种数据结构
      • 6.2.1 String
      • 6.2.2 List
      • 6.2.3 Set
      • 6.2.4 ZSet
      • 6.2.5 Hash
    • 6.3 Redis网络模型
      • 6.3.1 selecct
      • 6.3.2 poll
      • 6.3.3 epoll
      • 6.3.4 Redis网络模型
    • 6.4 Redis通信协议
  • 7 Redis实战
    • 7.1 短信登录
    • 7.2 商户查询缓存
      • 6.2.4 ZSet
      • 6.2.5 Hash
    • 6.3 Redis网络模型
      • 6.3.1 selecct
      • 6.3.2 poll
      • 6.3.3 epoll
      • 6.3.4 Redis网络模型
    • 6.4 Redis通信协议
  • 7 Redis实战
    • 7.1 短信登录
    • 7.2 商户查询缓存

Redis参考手册

1 基础认识

  • NoSQL与SQL

    image-20240425234137088

  • 特征

image-20240425234614363

1.1 安装配置

  • 安装:sudo apt install redis

  • 配置文件etc/redis/redis.conf

    bind 0.0.0.0 ::1    #将本地环回改为任意ip
    
    daemonize yes #守护进程启动
    
    requirepass 123123 #设置密码为123123
    
    protected-mode no   #开启后其他主机才能访问
    
    logfile "xxx.log"  #开启日志,并记录到xxx.log中
    

    配置完后重启服务器:service redis-sever restart

    查看运行状态:service redis-server status

  • 链接

    redis-cli
    
    #--raw 将二进制编码
    
  • 退出:quitctrl + d

1.2 通用命令

help @generic #查看通用命令文档 
  • 类型
    • key:字符串
    • value:字符串、哈希表、列表、集合…
  1. get
GET key
  1. set
SET key value
  1. exists
EXISTS key [key ...]	#判断多个key,返回key存在  的个数
  1. del
DEL key [key ...] #返回删除key的个数
  1. expire:设置过期时间
EXPIRE key [seconds]
PEXPIRE key [millisecond]
  1. ttl:(time to live)获取key的过期时间
TTL key   #返回-2表示key已经删除了
  • 过期策略

定期删除 与 惰性删除 相结合

  1. type : 查看key对应的value的值
redis> SET key1 "value"
"OK"
redis> LPUSH key2 "value"
(integer) 1
redis> SADD key3 "value"
(integer) 1
redis> TYPE key1
"string"
redis> TYPE key2
"list"
redis> TYPE key3
"set"
redis> 
  1. keys
KEYS pattern
  • pattern

    image-20240421224000208

  1. flushall:清除所有数据库中的key

  2. scan ——渐进式遍历

  3. select dbIndex —— 切换数据库

dbIndex范围为0-15

1.3 数据类型

1.3.1 数据结构与内部编码

  • raw:原始字符串(C语言中的字符数组)

  • embstr:对于短字符串的优化

  • ziplist:对于键值对少的时候,通过压缩列表进行优化

  • quicklist:从redis3.2开始用quicklist实现list

  • skiplist:跳表

image-20240422011248940

  • 查看实际编码方式

    OBJECT encoding key
    

string

  1. set
SET key value [NX | XX] [GET] [EX seconds | PX milliseconds | EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL]

image-20240422020234175

  1. get

get只能查询字符串类型的value

  1. mget / mset

设置/获取多组键值对

  1. setnx / setex / psetex

不存在则设置/另外设置过期时间(单位s)/(单位ms)

  1. #将value视为整数,对其+-
    incr		
    incrby
    decr
    decrby
    incrbyfloat		#前面四个只能算整数,incrbyfloat可以算浮点数
    
  2. 函数例子效果
    appendappend key value追加fvalue
    getrangegetrange key start end截取value的[start, end]区间部分,负数下标表示倒数
    setrangesetrange key offset valueoffset表示从第几个开始
    strlenstrlen key获取value的长度(字符为单位),若不是string类型则报错

key的结构

  • 让redis像mysql一样存储
  • key:数据库:表
  • value:{字段:值, 字段:值...}

image-20240426144736318

hash

image-20240426145202610

list

image-20240426150022647

set

image-20240426151000576

sorted_set

  • 默认按score升序排列,在Z后面加上REV倒序排列

image-20240426151322606

1.4 单线程模型

redis虽然是单线程模型,为什么效率却这么高

  1. 访问内存,MySQL则多是访问硬盘
  2. 核心功能简单
  3. 单线程模型,避免了多线程带来的开销
  4. 底层使用epoll

2 redis客户端

  • redis自定义应用层协议

2.1 RESP协议(Redis serialization protocol)

2.2 基础案例

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

using namespace sw::redis;

try {
    // Create an Redis object, which is movable but NOT copyable.
    auto redis = Redis("tcp://127.0.0.1:6379");

    // ***** STRING commands *****

    redis.set("key", "val");
    auto val = redis.get("key");    // val is of type OptionalString. See 'API Reference' section for details.
    if (val) {
        // Dereference val to get the returned value of std::string type.
        std::cout << *val << std::endl;
    }   // else key doesn't exist.

    // ***** LIST commands *****

    // std::vector<std::string> to Redis LIST.
    std::vector<std::string> vec = {"a", "b", "c"};
    redis.rpush("list", vec.begin(), vec.end());

    // std::initializer_list to Redis LIST.
    redis.rpush("list", {"a", "b", "c"});

    // Redis LIST to std::vector<std::string>.
    vec.clear();
    redis.lrange("list", 0, -1, std::back_inserter(vec));

    // ***** HASH commands *****

    redis.hset("hash", "field", "val");

    // Another way to do the same job.
    redis.hset("hash", std::make_pair("field", "val"));

    // std::unordered_map<std::string, std::string> to Redis HASH.
    std::unordered_map<std::string, std::string> m = {
        {"field1", "val1"},
        {"field2", "val2"}
    };
    redis.hmset("hash", m.begin(), m.end());

    // Redis HASH to std::unordered_map<std::string, std::string>.
    m.clear();
    redis.hgetall("hash", std::inserter(m, m.begin()));

    // Get value only.
    // NOTE: since field might NOT exist, so we need to parse it to OptionalString.
    std::vector<OptionalString> vals;
    redis.hmget("hash", {"field1", "field2"}, std::back_inserter(vals));

    // ***** SET commands *****

    redis.sadd("set", "m1");

    // std::unordered_set<std::string> to Redis SET.
    std::unordered_set<std::string> set = {"m2", "m3"};
    redis.sadd("set", set.begin(), set.end());

    // std::initializer_list to Redis SET.
    redis.sadd("set", {"m2", "m3"});

    // Redis SET to std::unordered_set<std::string>.
    set.clear();
    redis.smembers("set", std::inserter(set, set.begin()));

    if (redis.sismember("set", "m1")) {
        std::cout << "m1 exists" << std::endl;
    }   // else NOT exist.

    // ***** SORTED SET commands *****

    redis.zadd("sorted_set", "m1", 1.3);

    // std::unordered_map<std::string, double> to Redis SORTED SET.
    std::unordered_map<std::string, double> scores = {
        {"m2", 2.3},
        {"m3", 4.5}
    };
    redis.zadd("sorted_set", scores.begin(), scores.end());

    // Redis SORTED SET to std::vector<std::pair<std::string, double>>.
    // NOTE: The return results of zrangebyscore are ordered, if you save the results
    // in to `std::unordered_map<std::string, double>`, you'll lose the order.
    std::vector<std::pair<std::string, double>> zset_result;
    redis.zrangebyscore("sorted_set",
            UnboundedInterval<double>{},            // (-inf, +inf)
            std::back_inserter(zset_result));

    // Only get member names:
    // pass an inserter of std::vector<std::string> type as output parameter.
    std::vector<std::string> without_score;
    redis.zrangebyscore("sorted_set",
            BoundedInterval<double>(1.5, 3.4, BoundType::CLOSED),   // [1.5, 3.4]
            std::back_inserter(without_score));

    // Get both member names and scores:
    // pass an back_inserter of std::vector<std::pair<std::string, double>> as output parameter.
    std::vector<std::pair<std::string, double>> with_score;
    redis.zrangebyscore("sorted_set",
            BoundedInterval<double>(1.5, 3.4, BoundType::LEFT_OPEN),    // (1.5, 3.4]
            std::back_inserter(with_score));

    // ***** SCRIPTING commands *****

    // Script returns a single element.
    auto num = redis.eval<long long>("return 1", {}, {});

    // Script returns an array of elements.
    std::vector<std::string> nums;
    redis.eval("return {ARGV[1], ARGV[2]}", {}, {"1", "2"}, std::back_inserter(nums));

    // mset with TTL
    auto mset_with_ttl_script = R"(
        local len = #KEYS
        if (len == 0 or len + 1 ~= #ARGV) then return 0 end
        local ttl = tonumber(ARGV[len + 1])
        if (not ttl or ttl <= 0) then return 0 end
        for i = 1, len do redis.call("SET", KEYS[i], ARGV[i], "EX", ttl) end
        return 1
    )";

    // Set multiple key-value pairs with TTL of 60 seconds.
    auto keys = {"key1", "key2", "key3"};
    std::vector<std::string> args = {"val1", "val2", "val3", "60"};
    redis.eval<long long>(mset_with_ttl_script, keys.begin(), keys.end(), args.begin(), args.end());

    // ***** Pipeline *****

    // Create a pipeline.
    auto pipe = redis.pipeline();

    // Send mulitple commands and get all replies.
    auto pipe_replies = pipe.set("key", "value")
                            .get("key")
                            .rename("key", "new-key")
                            .rpush("list", {"a", "b", "c"})
                            .lrange("list", 0, -1)
                            .exec();

    // Parse reply with reply type and index.
    auto set_cmd_result = pipe_replies.get<bool>(0);

    auto get_cmd_result = pipe_replies.get<OptionalString>(1);

    // rename command result
    pipe_replies.get<void>(2);

    auto rpush_cmd_result = pipe_replies.get<long long>(3);

    std::vector<std::string> lrange_cmd_result;
    pipe_replies.get(4, back_inserter(lrange_cmd_result));

    // ***** Transaction *****

    // Create a transaction.
    auto tx = redis.transaction();

    // Run multiple commands in a transaction, and get all replies.
    auto tx_replies = tx.incr("num0")
                        .incr("num1")
                        .mget({"num0", "num1"})
                        .exec();

    // Parse reply with reply type and index.
    auto incr_result0 = tx_replies.get<long long>(0);

    auto incr_result1 = tx_replies.get<long long>(1);

    std::vector<OptionalString> mget_cmd_result;
    tx_replies.get(2, back_inserter(mget_cmd_result));

    // ***** Generic Command Interface *****

    // There's no *Redis::client_getname* interface.
    // But you can use *Redis::command* to get the client name.
    val = redis.command<OptionalString>("client", "getname");
    if (val) {
        std::cout << *val << std::endl;
    }

    // Same as above.
    auto getname_cmd_str = {"client", "getname"};
    val = redis.command<OptionalString>(getname_cmd_str.begin(), getname_cmd_str.end());

    // There's no *Redis::sort* interface.
    // But you can use *Redis::command* to send sort the list.
    std::vector<std::string> sorted_list;
    redis.command("sort", "list", "ALPHA", std::back_inserter(sorted_list));

    // Another *Redis::command* to do the same work.
    auto sort_cmd_str = {"sort", "list", "ALPHA"};
    redis.command(sort_cmd_str.begin(), sort_cmd_str.end(), std::back_inserter(sorted_list));

    // ***** Redis Cluster *****

    // Create a RedisCluster object, which is movable but NOT copyable.
    auto redis_cluster = RedisCluster("tcp://127.0.0.1:7000");

    // RedisCluster has similar interfaces as Redis.
    redis_cluster.set("key", "value");
    val = redis_cluster.get("key");
    if (val) {
        std::cout << *val << std::endl;
    }   // else key doesn't exist.

    // Keys with hash-tag.
    redis_cluster.set("key{tag}1", "val1");
    redis_cluster.set("key{tag}2", "val2");
    redis_cluster.set("key{tag}3", "val3");

    std::vector<OptionalString> hash_tag_res;
    redis_cluster.mget({"key{tag}1", "key{tag}2", "key{tag}3"},
            std::back_inserter(hash_tag_res));

} catch (const Error &e) {
    // Error handling.
}

3 分布式缓存 —— redis集群

3.1 持久化

  • 单节点Redis的问题:1. 数据丢失 2. 并发能力 3. 存储能力 4. 故障恢复

3.1.1 RDB

image-20240427230804132

image-20240427233636584

3.1.2 RDB底层原理

image-20240427234336347

3.1.3 AOF持久化

image-20240427235256612

  • 默认选择appendfsync everysec

image-20240427235540933

  • 解决AOF文件过大的问题(清除无效数据) —— 执行bgrewriteaof命令

image-20240428000342657

3.1.4 AOF与RDB的对比

image-20240428200406918

3.2 Redis主从

3.2.1 搭建主从集群

(转Redis集群.md)

3.2.2 数据同步原理

  1. 全量同步
  • id不同- > 生成RDB做全量同步
  • id相同 -> 通过offset做增量同步

image-20240428202606843

image-20240428202728682

  1. 增量同步

11111

  • 如何优化主从集群同步

image-20240428203425431

3.3 Redis哨兵

image-20240428205054062

Sentinel监控原理

Sentinel基于心跳机制监测服务状态,每隔1秒向集群的每个实例发送ping命令

主观下线:如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线。

客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半。

选择新的master

image-20240428205910199

如何实现故障转移

image-20240428210025947

3.3.1 搭建哨兵集群

(转Redis集群.md)

3.3.2 Redis客户端状态感知

3.4 Redis分片集群

3.4.1 搭建分片集群

(转Redis集群.md)

image-20240428223237874

3.4.2 散列插槽

image-20240428225102178

  • 如何将同一类数据固定的保存在同一个Redis实例中

image-20240428225417930

3.4.3 集群伸缩

  • 创建新Redis,加入到集群中

image-20240428225728626

  • 分配插槽
redis-cli --cluster reshard ip:port

3.4.4 故障转移

  1. 自动转移
  • 一个服务突然宕机,slave节点自动转移成master
  1. 手动转移

image-20240428231148079

4 多级缓存

image-20240428232744878

4.1 JVM进程缓存

4.2 OpenResty

image-20240429231721675

image-20240429234315794

  1. 修改nginx.conf文件

image-20240429235323238

  1. 编写item.lua文件

image-20240429235435025

  • 获取请求参数

image-20240429235839151

  • 封装http请求,由OpenResty发送给后端

image-20240430001548649

image-20240430001527599

  • 使用common.lua,通过item.lua发送请求到后端

image-20240430003124528

  • Redis预热

  • OpenResty访问Redis缓存

image-20240501150214696

  • nginx本地缓存

4.3 缓存同步

image-20240502121041244

4.3.1 Canel

image-20240502121331454

image-20240502220556739

5 Redis实践优化

5.1 key的设计

image-20240502221558171

  • 什么是Big Key

单个key的value小于10KB

对于集合类型的key,建议元素数量小于1000

  • Big Key的危害

image-20240502222457055

  • 删除Big Key

image-20240502223603980

5.2 批处理优化

  • 批处理一次性执行太多任务会阻塞网络
  1. 原生的M操作
  2. Pipeline
  • pipeline的多个命令不具有原子性

image-20240503115012875

5.2.1 集群模式下的批处理

image-20240503120412597

5.3 服务端优化

5.3.1 持久化配置

image-20240503223951603

5.3.2 慢查询

  • 该配置重启会恢复,要用就配置可以修改配置文件

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 其他命令

image-20240504000236684

5.3.3 服务器安全配置

image-20240504135349697

  • 安全配置
  1. Redis一定要设置密码
  2. 禁止线上使用下面命令: keys、flushall、flushdb、config set等命令。可以利用rename-command禁用。(在redis.conf文件下设置 rename-command keys xxx123xxxdfsda
  3. bind:限制网卡,禁止外网网卡访问禁止线上使用下面命令(在redis.conf文件下设置)
  4. 开启防火墙开启防火墙
  5. 不要使用Root账户启动Redis
  6. 尽量不要使用默认端口

5.3.4 内存配置

image-20240504140451848

  • 查看内存使用情况的命令:
    • memory xxx
    • info memory

image-20240504142118346

  • 两个查看客户端链接信息的命令
    • info client
    • client list

5.3.5 集群相关问题

  • 集群的完整性问题

image-20240504143153125

  • 集群的带宽问题

image-20240504143434456

  • 集群带来的其他问题

image-20240504143734732

6 Redis底层原理

6.1 底层数据结构

6.1.1 SDS动态数组

image-20240504150555378

image-20240504150612583

6.1.2 Intset

image-20240504150909130

  • intset升级

image-20240504152243964

  • 优点
  1. Redis会确保Intset中的元素唯一、有序
  2. 具备类型升级机制,可以节省内存空间
  3. 底层采用二分查找方式来查询

6.1.3 Dict

  • Dict由三部分组成:

image-20240504153208343

image-20240504153407129

  • Dict的扩容机制

image-20240504163735702

  • 渐进式rehash

ss

6.1.4 ZipList

image-20240504165810053

image-20240504170359861

  • encoding编码(小端)

    • 字符串类型编码(00、01、10开头)

      image-20240504171221619

    • 整数类型编码

  • 连锁更新问题

image-20240504230957638

6.1.5 QuickList

  • QuickList = LinkList + ZipList

  • QuickList:限制ZipList的大小,解决内存分配的效率问题

image-20240504233808521

  • 如何限制ZipList大小

image-20240504233856667

  • QuickList节点压缩image-20240504234159575

image-20240504235503333

image-20240504235643390

6.1.5 SkipList

image-20240505000844632

  • 底层结构

image-20240505001646311

image-20240505001031349

6.1.6 RedisObject

image-20240507103139134

  • 11种编码格式

image-20240507103439069

  • 数据类型对应的编码格式

image-20240507103733752

6.2 五种数据结构

6.2.1 String

  • raw:其基本编码方式是RAW,基于简单动态字符串(SDS)实现,存储上限为512mb。
  • embstr:如果存储的SDS长度小于44字节,则会采用EMBSTR编码,此时object head与SDS是一段连续空间。申请内存时只需要调用一次内存分配函数,效率更高。
  • int:如果存储的字符串是整数值,并且大小在LONG_MAX范围内,则会采用INT编码:直接将数据保存在RedisObject的ptr指针位置(刚好8字节),不再需要SDS了。

image-20240507105438669

6.2.2 List

image-20240507110929648

6.2.3 Set

Set是Redis中的集合,不一定确保元素有序,可以满足元素唯一、查询效率要求极高。

  • Dict:为了查询效率和唯一性,set采用HT编码(Dict)。Dict中的key用来存储元素, value统一为null。

  • IIntSet:当存储的所有数据都是整数,并且元素数量不超过set-max-intset-entries时,Set会采用IntSet编码,以节省内存。

image-20240507112417630

  • 插入时类型转换

image-20240507112431813

6.2.4 ZSet

zset底层数据结构必须满足键值存储、键必须唯一、可排序这几个需求

  • SkipList:可以排序,并且可以同时存储score和ele值( member)
  • HT ( Dict):可以键值存储,并且可以根据key找value

image-20240507153935285

  • 当元素不多时,使用ZipList编码

image-20240507154502329

image-20240507154916702

6.2.5 Hash

image-20240507155424965

6.3 Redis网络模型

6.3.1 selecct

image-20240507203420468

6.3.2 poll

image-20240507203831453

6.3.3 epoll

image-20240507205127392

6.3.4 Redis网络模型

单线程?多线程?

image-20240507212223799

image-20240507222739878

6.4 Redis通信协议

image-20240510101118195

7 Redis实战

7.1 短信登录

image-20240905170425429

image-20240905170443544

7.2 商户查询缓存

tXZN6w-1725528160173)]

6.2.4 ZSet

zset底层数据结构必须满足键值存储、键必须唯一、可排序这几个需求

  • SkipList:可以排序,并且可以同时存储score和ele值( member)
  • HT ( Dict):可以键值存储,并且可以根据key找value

[外链图片转存中…(img-zQwHt0dx-1725528160173)]

  • 当元素不多时,使用ZipList编码

[外链图片转存中…(img-SCoUVdDn-1725528160173)]

[外链图片转存中…(img-KIqmlnJz-1725528160173)]

6.2.5 Hash

[外链图片转存中…(img-YExQQbXg-1725528160174)]

6.3 Redis网络模型

6.3.1 selecct

[外链图片转存中…(img-fFzuys3R-1725528160174)]

6.3.2 poll

[外链图片转存中…(img-OxEyBeGi-1725528160174)]

6.3.3 epoll

[外链图片转存中…(img-0SEZ3rgN-1725528160174)]

6.3.4 Redis网络模型

单线程?多线程?

[外链图片转存中…(img-fRIrVeDR-1725528160175)]

[外链图片转存中…(img-YhYlOECO-1725528160175)]

6.4 Redis通信协议

[外链图片转存中…(img-nUoZADFd-1725528160175)]

7 Redis实战

7.1 短信登录

[外链图片转存中…(img-auMEyc95-1725528160175)]

[外链图片转存中…(img-cedX1Yyj-1725528160176)]

7.2 商户查询缓存


http://www.kler.cn/news/292699.html

相关文章:

  • STM32外部中断(总结了易出现的BUG)
  • 基于springboot的二手车交易系统的设计与实现
  • 在 Cloud TPU Pod 上训练 PyTorch 模型
  • Java重修笔记 第四十八天 TreeSet 类、TreeMap 类
  • 计算机毕设选题推荐-基于python的剧本杀预约服务平台【python-爬虫-大数据定制】
  • 人工智能在网络安全中的重要性
  • 一文讲懂扩散模型
  • 安装opengauss企业版单机流程
  • 【GD32】---- 使用GD32调试串口并实现printf打印输出
  • 修改服务器DNS解析及修改自动对时时区
  • 【Motion Forecasting】SIMPL:简单且高效的自动驾驶运动预测Baseline
  • AI时代来临,AI基础数据服务行业未来发展有哪些变化
  • 产品经理的学习笔记(全集)-持续更新
  • 基础算法题————散列/哈希/Hash
  • ElasticSearch-倒排索引 文档映射
  • 深入理解JavaScript闭包:避免常见的内存泄漏问题
  • 深度学习|模型推理:端到端任务处理
  • 【Netty】自定义网络通信协议
  • FFmpeg源码:avpriv_set_pts_info函数分析
  • SpringBoot 实战:SpringBoot整合Flink CDC,实时追踪mysql数据变动
  • Java简单实现服务器客户端通信
  • 0to1使用JWT实现登录认证
  • ubuntu24下安装pytorch3d
  • ARM 伪指令 (26)
  • 2024年高教社杯数学建模国赛赛题浅析——助攻快速选题
  • 【系统架构设计师】论文:论SOA在企业集成架构设计中的应用
  • 【Rust光年纪】探索Rust嵌入式开发利器:从硬件访问到USB绑定
  • Ubuntu设置
  • Spring Boot实现License生成和校验
  • Java中的Stream流