第六:go 操作 redis-go
Redis
在项目开发中redis的使用也比较频繁,本文介绍了Go语言中go-redis库的基本使用。
Redis介绍
Redis是一个开源的内存数据库,Redis提供了多种不同类型的数据结构,很多业务场景下的问题都可以很自然地映射到这些数据结构上。除此之外,通过复制、持久化和客户端分片等特性,我们可以很方便地将Redis扩展成一个能够包含数百GB数据、每秒处理上百万次请求的系统。
Redis支持的数据结构
Redis支持诸如字符串(string)、哈希(hashe)、列表(list)、集合(set)、带范围查询的排序集合(sorted set)、bitmap、hyperloglog、带半径查询的地理空间索引(geospatial index)和流(stream)等数据结构。
Redis应用场景
- 缓存系统,减轻主数据库(MySQL)的压力。
- 计数场景,比如微博、抖音中的关注数和粉丝数。
- 热门排行榜,需要排序的场景特别适合使用ZSET。
- 利用 LIST 可以实现队列的功能。
- 利用 HyperLogLog 统计UV、PV等数据。
- 使用 geospatial index 进行地理位置相关查询。
准备Redis环境
读者可以选择在本机安装 redis 或使用云数据库,这里直接使用Docker启动一个 redis 环境,方便学习使用。
使用下面的命令启动一个名为 redis507 的 5.0.7 版本的 redis server环境。
docker run --name redis507 -p 6379:6379 -d redis:5.0.7
注意: 此处的版本、容器名和端口号可以根据自己需要设置。
启动一个 redis-cli 连接上面的 redis server。
docker run -it --network host --rm redis:5.0.7 redis-cli
go-redis库
安装
Go 社区中目前有很多成熟的 redis client 库,比如GitHub - gomodule/redigo: Go client for Redis 和GitHub - redis/go-redis: Redis Go client,读者可以自行选择适合自己的库。本文使用 go-redis 这个库来操作 Redis 数据库。
使用以下命令下安装 go-redis 库。
安装v8版本:
go get github.com/redis/go-redis/v8
安装v9版本:
go get github.com/redis/go-redis/v9
连接
在项目中导入 go-redis库(请根据实际情况导入自己需要的版本)。
import "github.com/redis/go-redis/v9"
SetNX方法仅在键不存在时设置值:
err := rdb.SetNX(ctx, "key1", "value", 0).Err()
if err != nil {panic(err)
}
2.5.1 添加和删除
ZAdd用于添加或更新元素,ZRem用于删除元素:
err := rdb.ZAdd(ctx, "key", &redis.Z{Score: 2.5, Member: "zhangsan"}).Err()
if err != nil {
panic(err)
}
rdb.ZRem(ctx, "key", "zhangsan")
2.5.2 查询操作
ZRange和ZRevRange用于按分数排序返回元素,ZScore用于查询元素的分数:
vals, err := rdb.ZRange(ctx, "key", 0, -1).Result()
if err != nil {
panic(err)
}
fmt.Println(vals)
score, err := rdb.ZScore(ctx, "key", "zhangsan").Result()
if err != nil {
panic(err)
}
fmt.Println(score)
原文链接:https://blog.csdn.net/weixin_73833086/article/details/146127620
普通连接模式
注意 如果 redis 的值不存的情况下:
if err == redis.Nil 的情况判断



go-redis 库中使用 redis.NewClient 函数连接 Redis 服务器。
rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379",Password: "", // 密码DB: 0, // 数据库PoolSize: 20, // 连接池大小
})
除此之外,还可以使用 redis.ParseURL 函数从表示数据源的字符串中解析得到 Redis 服务器的配置信息。
opt, err := redis.ParseURL("redis://<user>:<pass>@localhost:6379/<db>")
if err != nil {panic(err)
}rdb := redis.NewClient(opt)
TLS连接模式
如果使用的是 TLS 连接方式,则需要使用 tls.Config 配置。
rdb := redis.NewClient(&redis.Options{TLSConfig: &tls.Config{MinVersion: tls.VersionTLS12,// Certificates: []tls.Certificate{cert},// ServerName: "your.domain.com",},
})
Redis Sentinel模式 哨兵模式
使用下面的命令连接到由 Redis Sentinel 管理的 Redis 服务器。

rdb := redis.NewFailoverClient(&redis.FailoverOptions{MasterName: "master-name",SentinelAddrs: []string{":9126", ":9127", ":9128"},
})
Redis Cluster模式 集群模式
使用下面的命令连接到 Redis Cluster,go-redis 支持按延迟或随机路由命令。

rdb := redis.NewClusterClient(&redis.ClusterOptions{Addrs: []string{":7000", ":7001", ":7002", ":7003", ":7004", ":7005"},// 若要根据延迟或随机路由命令,请启用以下命令之一// RouteByLatency: true,// RouteRandomly: true,
})
基本使用
执行命令
下面的示例代码演示了 go-redis 库的基本使用。
// doCommand go-redis基本使用示例
func doCommand() {ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)defer cancel()// 执行命令获取结果val, err := rdb.Get(ctx, "key").Result()fmt.Println(val, err)// 先获取到命令对象cmder := rdb.Get(ctx, "key")fmt.Println(cmder.Val()) // 获取值fmt.Println(cmder.Err()) // 获取错误// 直接执行命令获取错误err = rdb.Set(ctx, "key", 10, time.Hour).Err()// 直接执行命令获取值value := rdb.Get(ctx, "key").Val()fmt.Println(value)
}
执行任意命令
go-redis 还提供了一个执行任意命令或自定义命令的 Do 方法,特别是一些 go-redis 库暂时不支持的命令都可以使用该方法执行。具体使用方法如下。
// doDemo rdb.Do 方法使用示例
func doDemo() {ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)defer cancel()// 直接执行命令获取错误err := rdb.Do(ctx, "set", "key", 10, "EX", 3600).Err()fmt.Println(err)// 执行命令获取结果val, err := rdb.Do(ctx, "get", "key").Result()fmt.Println(val, err)
}
redis.Nil
go-redis 库提供了一个 redis.Nil 错误来表示 Key 不存在的错误。因此在使用 go-redis 时需要注意对返回错误的判断。在某些场景下我们应该区别处理 redis.Nil 和其他不为 nil 的错误。
// getValueFromRedis redis.Nil判断
func getValueFromRedis(key, defaultValue string) (string, error) {ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)defer cancel()val, err := rdb.Get(ctx, key).Result()if err != nil {// 如果返回的错误是key不存在if errors.Is(err, redis.Nil) {return defaultValue, nil}// 出其他错了return "", err}return val, nil
}
其他示例
zset示例
下面的示例代码演示了如何使用 go-redis 库操作 zset。
// zsetDemo 操作zset示例
func zsetDemo() {// keyzsetKey := "language_rank"// value// 注意:v8版本使用[]*redis.Z;此处为v9版本使用[]redis.Zlanguages := []redis.Z{{Score: 90.0, Member: "Golang"},{Score: 98.0, Member: "Java"},{Score: 95.0, Member: "Python"},{Score: 97.0, Member: "JavaScript"},{Score: 99.0, Member: "C/C++"},}ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)defer cancel()// ZADDerr := rdb.ZAdd(ctx, zsetKey, languages...).Err()if err != nil {fmt.Printf("zadd failed, err:%v\n", err)return}fmt.Println("zadd success")// 把Golang的分数加10newScore, err := rdb.ZIncrBy(ctx, zsetKey, 10.0, "Golang").Result()if err != nil {fmt.Printf("zincrby failed, err:%v\n", err)return}fmt.Printf("Golang's score is %f now.\n", newScore)// 取分数最高的3个ret := rdb.ZRevRangeWithScores(ctx, zsetKey, 0, 2).Val()for _, z := range ret {fmt.Println(z.Member, z.Score)}// 取95~100分的op := &redis.ZRangeBy{Min: "95",Max: "100",}ret, err = rdb.ZRangeByScoreWithScores(ctx, zsetKey, op).Result()if err != nil {fmt.Printf("zrangebyscore failed, err:%v\n", err)return}for _, z := range ret {fmt.Println(z.Member, z.Score)}
}
执行上面的函数将得到如下输出结果。
zadd success
Golang's score is 100.000000 now.
Golang 100
C/C++ 99
Java 98
Python 95
JavaScript 97
Java 98
C/C++ 99
Golang 100
扫描或遍历所有key
在Redis中可以使用KEYS prefix* 命令按前缀查询所有符合条件的 key,go-redis库中提供了Keys方法实现类似查询key的功能。
例如使用以下命令查询以user:为前缀的所有key(user:cart:00、user:order:2023等)。
vals, err := rdb.Keys(ctx, "user:*").Result()
但是如果需要扫描数百万的 key ,那速度就会比较慢。这种场景下你可以使用Scan命令来遍历所有符合要求的 key。
// scanKeysDemo1 按前缀查找所有key示例
func scanKeysDemo1() {ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)defer cancel()var cursor uint64for {var keys []stringvar err error// 将redis中所有以prefix:为前缀的key都扫描出来keys, cursor, err = rdb.Scan(ctx, cursor, "prefix:*", 0).Result()if err != nil {panic(err)}for _, key := range keys {fmt.Println("key", key)}if cursor == 0 { // no more keysbreak}}
}
针对这种需要遍历大量key的场景,go-redis中提供了一个简化方法——Iterator,其使用示例如下。
// scanKeysDemo2 按前缀扫描key示例
func scanKeysDemo2() {ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)defer cancel()// 按前缀扫描keyiter := rdb.Scan(ctx, 0, "prefix:*", 0).Iterator()for iter.Next(ctx) {fmt.Println("keys", iter.Val())}if err := iter.Err(); err != nil {panic(err)}
}
例如,我们可以写出一个将所有匹配指定模式的 key 删除的示例。
// delKeysByMatch 按match格式扫描所有key并删除
func delKeysByMatch(match string, timeout time.Duration) {ctx, cancel := context.WithTimeout(context.Background(), timeout)defer cancel()iter := rdb.Scan(ctx, 0, match, 0).Iterator()for iter.Next(ctx) {err := rdb.Del(ctx, iter.Val()).Err()if err != nil {panic(err)}}if err := iter.Err(); err != nil {panic(err)}
}
此外,对于 Redis 中的 set、hash、zset 数据类型,go-redis 也支持类似的遍历方法。
iter := rdb.SScan(ctx, "set-key", 0, "prefix:*", 0).Iterator()
iter := rdb.HScan(ctx, "hash-key", 0, "prefix:*", 0).Iterator()
iter := rdb.ZScan(ctx, "sorted-hash-key", 0, "prefix:*", 0).Iterator(
Pipeline
Redis Pipeline 允许通过使用单个 client-server-client 往返执行多个命令来提高性能。区别于一个接一个地执行100个命令,你可以将这些命令放入 pipeline 中,然后使用1次读写操作像执行单个命令一样执行它们。这样做的好处是节省了执行命令的网络往返时间(RTT)。
y在下面的示例代码中演示了使用 pipeline 通过一个 write + read 操作来执行多个命令。
pipe := rdb.Pipeline()incr := pipe.Incr(ctx, "pipeline_counter")
pipe.Expire(ctx, "pipeline_counter", time.Hour)cmds, err := pipe.Exec(ctx)
if err != nil {panic(err)
}// 在执行pipe.Exec之后才能获取到结果
fmt.Println(incr.Val())
上面的代码相当于将以下两个命令一次发给 Redis Server 端执行,与不使用 Pipeline 相比能减少一次RTT。
INCR pipeline_counter
EXPIRE pipeline_counts 3600
或者,你也可以使用Pipelined 方法,它会在函数退出时调用 Exec。
var incr *redis.IntCmdcmds, err := rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error {incr = pipe.Incr(ctx, "pipelined_counter")pipe.Expire(ctx, "pipelined_counter", time.Hour)return nil
})
if err != nil {panic(err)
}// 在pipeline执行后获取到结果
fmt.Println(incr.Val())
我们可以遍历 pipeline 命令的返回值依次获取每个命令的结果。下方的示例代码中使用pipiline一次执行了100个 Get 命令,在pipeline 执行后遍历取出100个命令的执行结果。
cmds, err := rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error {for i := 0; i < 100; i++ {pipe.Get(ctx, fmt.Sprintf("key%d", i))}return nil
})
if err != nil {panic(err)
}for _, cmd := range cmds {fmt.Println(cmd.(*redis.StringCmd).Val())
}
在那些我们需要一次性执行多个命令的场景下,就可以考虑使用 pipeline 来优化。
事务
Redis 是单线程执行命令的,因此单个命令始终是原子的,但是来自不同客户端的两个给定命令可以依次执行,例如在它们之间交替执行。但是,Multi/exec能够确保在multi/exec两个语句之间的命令之间没有其他客户端正在执行命令。
在这种场景我们需要使用 TxPipeline 或 TxPipelined 方法将 pipeline 命令使用 MULTI 和EXEC包裹起来。
// TxPipeline demo
pipe := rdb.TxPipeline()
incr := pipe.Incr(ctx, "tx_pipeline_counter")
pipe.Expire(ctx, "tx_pipeline_counter", time.Hour)
_, err := pipe.Exec(ctx)
fmt.Println(incr.Val(), err)// TxPipelined demo
var incr2 *redis.IntCmd
_, err = rdb.TxPipelined(ctx, func(pipe redis.Pipeliner) error {incr2 = pipe.Incr(ctx, "tx_pipeline_counter")pipe.Expire(ctx, "tx_pipeline_counter", time.Hour)return nil
})
fmt.Println(incr2.Val(), err)
上面代码相当于在一个RTT下执行了下面的redis命令:
MULTI
INCR pipeline_counter
EXPIRE pipeline_counts 3600
EXEC
Watch
我们通常搭配 WATCH命令来执行事务操作。从使用WATCH命令监视某个 key 开始,直到执行EXEC命令的这段时间里,如果有其他用户抢先对被监视的 key 进行了替换、更新、删除等操作,那么当用户尝试执行EXEC的时候,事务将失败并返回一个错误,用户可以根据这个错误选择重试事务或者放弃事务。
Watch方法接收一个函数和一个或多个key作为参数。
Watch(fn func(*Tx) error, keys ...string) error
下面的代码片段演示了 Watch 方法搭配 TxPipelined 的使用示例。
// watchDemo 在key值不变的情况下将其值+1
func watchDemo(ctx context.Context, key string) error {return rdb.Watch(ctx, func(tx *redis.Tx) error {n, err := tx.Get(ctx, key).Int()if err != nil && err != redis.Nil {return err}// 假设操作耗时5秒// 5秒内我们通过其他的客户端修改key,当前事务就会失败time.Sleep(5 * time.Second)_, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {pipe.Set(ctx, key, n+1, time.Hour)return nil})return err}, key)
}
将上面的函数执行并打印其返回值,如果我们在程序运行后的5秒内修改了被 watch 的 key 的值,那么该事务操作失败,返回redis: transaction failed错误。
最后我们来看一个 go-redis 官方文档中使用 GET 、SET和WATCH命令实现一个 INCR 命令的完整示例。
// 此处rdb为初始化的redis连接客户端
const routineCount = 100// 设置5秒超时
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()// increment 是一个自定义对key进行递增(+1)的函数
// 使用 GET + SET + WATCH 实现,类似 INCR
increment := func(key string) error {txf := func(tx *redis.Tx) error {// 获得当前值或零值n, err := tx.Get(ctx, key).Int()if err != nil && err != redis.Nil {return err}// 实际操作(乐观锁定中的本地操作)n++// 仅在监视的Key保持不变的情况下运行_, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {// pipe 处理错误情况pipe.Set(ctx, key, n, 0)return nil})return err}// 最多重试100次for retries := routineCount; retries > 0; retries-- {err := rdb.Watch(ctx, txf, key)if err != redis.TxFailedErr {return err}// 乐观锁丢失}return errors.New("increment reached maximum number of retries")
}// 开启100个goroutine并发调用increment
// 相当于对key执行100次递增
var wg sync.WaitGroup
wg.Add(routineCount)
for i := 0; i < routineCount; i++ {go func() {defer wg.Done()if err := increment("counter3"); err != nil {fmt.Println("increment error:", err)}}()
}
wg.Wait()n, err := rdb.Get(ctx, "counter3").Int()
fmt.Println("最终结果:", n, err)
copy
在这个示例中使用了 redis.TxFailedErr 来检查事务是否失败。
更多详情请查阅官方文档。
相关文章:
第六:go 操作 redis-go
Redis 在项目开发中redis的使用也比较频繁,本文介绍了Go语言中go-redis库的基本使用。 Redis介绍 Redis是一个开源的内存数据库,Redis提供了多种不同类型的数据结构,很多业务场景下的问题都可以很自然地映射到这些数据结构上。除此之外&am…...
【蓝桥杯】每天一题,理解逻辑(4/90)【Leetcode 二进制求和】
题目描述 我们解析一下题目 我们可以理解到两个主要信息 给的是二进制的字符串返回他们的和 我们知道,十进制的加减法需要进位,例如:9716是因为91之后进了一位,二进制也是如此,只不过十进制是逢10进1,二…...
CentOS 7 更换 YUM 源为国内
**CentOS 7 更换 YUM 源为国内源 ** 背景说明 更换 YUM 源可加速软件下载,解决官方源访问慢的问题。国内推荐镜像源:阿里云、清华、网易、中科大。 一、备份原有 YUM 源 # 备份系统默认源(避免操作失误可恢复) sudo cp /etc/yum…...
快速入手-基于Django的mysql配置(三)
Django开发操作数据库更简单,内部提供了ORM框架。比如mysql,旧版本用pymysql对比较多,新的版本采用mysqlclient。 1、安装mysql模块 pip install mysqlclient 2、Django的ORM主要做了两件事 (1)CRUD数据库中的表&am…...
docker部署dify
1.安装docker 参考链接 https://ascendking.blog.csdn.net/article/details/136407383 设置docker源 vim /etc/docker/daemon.json {"registry-mirrors": ["https://docker.registry.cyou", "https://docker-cf.registry.cyou", "http…...
网络安全红蓝对抗实战演练,沉浸式对抗训练场上线!
在网络安全的世界里,没有永恒的盾牌,只有不断磨砺的利剑。近年来,某金融机构因系统漏洞导致千万级用户数据泄露,某制造企业因生产线遭遇勒索攻击被迫停产数日——这些真实案例揭示了一个残酷现实:传统的理论教学已无法…...
舞狮表演(dp)
#include <bits/stdc.h> using namespace std; const int N1e35; int main() {int t;cin>>t;while(t--){int n;cin>>n;int a[N][N];for(int i1;i<n;i){for(int j1;j<n;j){int x;cin>>x;if(x&1) a[i][j]1; // 如果金额是奇数,a[i]…...
【Qt】Qt + Modbus 服务端学习笔记
《Qt Modbus 服务端学习笔记》 1.因为项目的需要,要写一个modbus通信,csdn上感觉有些回答,代码是人工智能生成的,有些细节不对。我这个经过实测,是可以直接用的。 首先要包含Qt 的相关模块 Qt Modbus 模块主要包含以…...
squirrel语言全面介绍
Squirrel 是一种较新的程序设计语言,由意大利人 Alberto Demichelis 开发,其设计目标是成为一个强大的脚本工具,适用于游戏等对大小、内存带宽和实时性有要求的应用程序。以下是对 Squirrel 语言的全面介绍: 语言特性 动态类型&a…...
SpringBoot配置文件加载优先级
目录 示例 配置文件&编写配置类 在Spring Boot项目中,配置属性的优先级是一个重要的概念,它决定了当存在多个配置源时,哪个配置源的属性将被应用。以下是SpringBoot中配置属性的优先级,从最高到最低: 命令行参数…...
【详细解决】pycharm 终端出现报错:“Failed : 无法将“Failed”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。
昨天在终端一顿操作后突然打开pycharm时就开始报错: 无法将“Failed”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。 所在位置 行:1 字符: 1 Failed to act…...
CXL协议之FM(Fabric Management)解释
CXL协议中的FM功能详解 1. FM的核心作用 FM是CXL(Compute Express Link)架构中的核心管理实体,负责协调和管理CXL设备之间的通信、资源分配及拓扑结构。其核心功能包括: 设备发现与枚举:识别CXL拓扑中的设备&#x…...
用Promise实现ajax的自动重试
有时候遇到网络错误,希望可以多试几次,可以利用Promise递归调用实现 以若依系统的登出举例 export function logout() {return request({url: /logout,method: post}) } 修改下原本的登出逻辑,遇到ERR_NETWORK错误,也就是网络问…...
Unity URP 实现场景和UI添加后处理
在更新到URP之后,之前内置的渲染管线的那一套后处理已经无法使用,接下来,我们使用URP的内置后处理实现对场景和UI的后处理。 设置UI 如果UI需要使用后处理,在Canvas里,我们要选择Screen Space - Camera,然…...
搭建ISCSI传输的配置与管理
前提是: windows server2019设置成桥接模式,因为要让虚拟机和主机设置成一个网段,才能通过网络进行新建虚拟磁盘。 1.添加ISCSI角色 安装位置 选择文件和存储服务----------文件和iscsl 服务------------iscsl目标服务器 2.右上角点击任务&a…...
华为参访预约,团队考察体验黑科技之旅
华为参观学习背景 全球第1,中国骄傲 成立于1987年的华为,早在2013年其销售收入就已超过爱立信,成为全球行业第1名。2019年福布斯世界500强排名第61位,全球唯1一家没有上市的民营企业。目前,华为5G技术已经走在世界前沿…...
Java 设计模式之享元模式(Flyweight Pattern)
享元模式(Flyweight Pattern) 是一种 结构型设计模式,旨在通过共享对象来有效支持大量细粒度对象的复用,从而减少内存占用和提高性能。其核心是 分离内部状态(可共享)与外部状态(不可共享&#…...
C#入门学习记录(三)C#中的隐式和显示转换
C#类型转换:隐式与显式转换的机制与应用 在C#的强类型体系中,数据类型转换是实现数据交互和算法逻辑的基础操作。当数值类型范围存在包含关系,或对象类型存在继承层次时,系统通过预定义的转换规则实现类型兼容处理。隐式转换&…...
Rk3568驱动开发_设备树_9
什么是设备树? 以我目前的理解,设备树更像日常生活中用的地图,用户能根据地图去寻找到相应位置 设备树也是如此它描述了硬件设备的连接关系和配置信息,供 CPU(或者更准确地说,是操作系统内核)…...
一和零 (leetcode 474
leetcode系列 文章目录 一、核心操作二、外层配合操作三、核心模式代码总结 本题是一个01背包问题,只是背包是一个二维数组的背包,分别为0的个数不能超过m,1的个数不能超过n,而物品就是题目中的字符串,其容量为0和1的…...
深度学习:从零开始的DeepSeek-R1-Distill有监督微调训练实战(SFT)
原文链接:从零开始的DeepSeek微调训练实战(SFT) 微调参考示例:由unsloth官方提供https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Qwen2.5_(7B)-Alpaca.ipynbhttps://colab.research.google.com/git…...
【AI News | 20250320】每日AI进展
AI Repos 1、servers 该仓库提供详细入门指南,用户可通过简单步骤连接Claude客户端,快速使用所有服务器功能。此项目由Anthropic管理,展示MCP的多样性与扩展性,助力开发者为大语言模型提供安全、可控的工具与数据访问。 2、awe…...
从零开始实现 C++ TinyWebServer 阻塞队列 BlockQueue类详解
文章目录 阻塞队列是什么?为什么需要阻塞队列?BlockQueue 成员变量实现 push() 函数实现 pop() 函数实现 close() 函数BlockQueue 代码BlockQueue 测试 从零开始实现 C TinyWebServer 项目总览 项目源码 阻塞队列是什么? 阻塞队列是一种线程…...
Linux驱动开发基础(can)
目录 1.can的介绍 2.can的硬件连接 2.1 CPU自带can控制器 2.2 CPU没有can控制器 3.电气属性 4.can的特点 5.can协议 5.1 can的种类 5.2 数据帧 5.2.1 标准数据帧格式 5.3.1 扩展数据帧格式 5.3 遥控帧 5.4 错误帧 5.5 过载帧 5.6 帧间隔 5.7 位填充 5.8 位时…...
5.2《生活中的透镜》——5.3《凸透镜成像规律》讲后再上
教会什么:照相机、投影仪、放大镜的原理 培养什么:(再说) 课标: (二)运动和相互作用 2.3 声和光 2.3.5了解凸透镜成像规律的应用。 例7 了解凸透镜成像规律在放大镜、照相机中的应用。 一、导入 提问:生活中有哪些透镜?(放大镜、照相机、投影仪/幻灯机) ——直接提出…...
【LangChain入门 3 Prompts组件】聊天提示词模板 ChatPromptTemplate
文章目录 一、 聊天信息提示词模板1.1 使用关键字1.2 使用SystemMessage, HumanMessage, AIMessage来定义消息1.3 使用MessagesPlaceholder 在特定未知添加消息列表 二、关键类介绍2.1 ChatPromptTemplate 类2.1.1 from_messages()2.1.2 format_messages()2.1.3 format_prompt(…...
fastadmin后台管理员日志指定方法不记录
做的订单提醒,只要在线会把日志自动存储进去,这个又是每30s执行一次,数据库没多久就爆掉了,最终找到一个处理方法,可能不是最好的,仅供大家参考 具体位置: application/admin/model/AdminLog.php里面的$ignoreRegex方法 protected static $ignoreRegex [/^(.*)\/(selectpage…...
leetcode热题100道——字母异位词分组
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。 字母异位词 是由重新排列源单词的所有字母得到的一个新单词。 示例 1: 输入: strs ["eat", "tea", "tan", "ate", "nat", &…...
MCU-芯片时钟与总线和定时器关系,举例QSPI
时钟源: 时钟源为系统时钟提供原始频率信号,系统时钟则通过(分频、倍频、选择器)成为整个芯片的“主时钟”,驱动 CPU 内核、总线(AHB、APB)及外设的运行。 内部时钟源: HSI&#x…...
力扣热题100(方便自己复习,自用)
力扣热题100 1. 两数之和 - 力扣(LeetCode) 查找两数之和是不是等于target也就是我们找到一个数之后,用target将其减掉,再寻找应当对应的元素是什么每找到一个数,我们就将其放在集合中,因为集合中可以去重…...
