【Redis】字符串数据类型深入解析与应用实践

news2024/9/21 0:33:59

目录

      • String 字符串
        • 常⻅命令
        • 计数命令
        • 其他命令
        • 命令⼩结
        • 内部编码
        • 典型使⽤场景


String 字符串

字符串类型是 Redis 最基础的数据类型,关于字符串需要特别注意:1)⾸先 Redis 中所有的键的类型都是字符串类型,⽽且其他⼏种数据结构也都是在字符串类似基础上构建的,例如列表和集合的元素类型是字符串类型,所以字符串类型能为其他 4 种数据结构的学习奠定基础。2)其次,如图 2-7所⽰,字符串类型的值实际可以是字符串,包含⼀般格式的字符串或者类似 JSON、XML 格式的字符串;数字,可以是整型或者浮点型;甚⾄是⼆进制流数据,例如图⽚、⾳频、视频等。不过⼀个字符串的最⼤值不能超过 512 MB。

由于 Redis 内部存储字符串完全是按照⼆进制流的形式保存的,所以 Redis 是不处理字符集编码问题的,客⼾端传⼊的命令中使⽤的是什么字符集编码,就存储什么字符集编码(不会做任何的编码转换)。可以存储文本数据、整数、普通的文本字符串、JSON、xml、二进制数据(图片、视频、音频……),但对于音频视频这种太大了,存储起来有可能阻塞了

图 2-7 字符串数据类型

常⻅命令

SET

set

将 string 类型的 value 设置到 key 中。如果 key 之前存在,则覆盖,⽆论原来的数据类型是什么。之前关于此 key 的 TTL 也全部失效。

语法:

SET key value [expiration EX seconds|PX milliseconds] [NX|XX]

命令有效版本:1.0.0 之后

时间复杂度:O(1)

选项:

SET 命令⽀持多种选项来影响它的⾏为:

  • EX seconds⸺使⽤秒作为单位设置 key 的过期时间。
  • PX milliseconds⸺使⽤毫秒作为单位设置 key 的过期时间。
  • NX ⸺只在 key 不存在时才进⾏设置,即如果 key 之前已经存在,设置不执⾏。
  • XX ⸺只在 key 存在时才进⾏设置,即如果 key 之前不存在,设置不执⾏。

注意:由于带选项的 SET 命令可以被 SETNXSETEXPSETEX 等命令代替,所以之后的版本中,Redis 可能进⾏合并。

返回值:

  • 如果设置成功,返回 OK。
  • 如果由于 SET 指定了 NX 或者 XX 但条件不满⾜,SET 不会执⾏,并返回 (nil)。

⽰例:

 redis> EXISTS mykey
 (integer) 0
 redis> SET mykey "Hello"
 OK
 redis> GET mykey
 "Hello"
 redis> SET mykey "World" NX
 (nil)
 redis> DEL mykey
 (integer) 1
 redis> EXISTS mykey
 (integer) 0
 redis> SET mykey "World" XX
 (nil)
 redis> GET mykey
 (nil)
 redis> SET mykey "World" NX
 OK
 redis> GET mykey
 "World"
 redis> SET mykey "Will expire in 10s" EX 10
 OK
 redis> GET mykey
 "Will expire in 10s"
 redis> GET mykey # 10秒之后
 (nil)

GET

get

获取 key 对应的 value。如果 key 不存在,返回 nil。如果 value 的数据类型不是 string,会报错。

语法:

GET key

命令有效版本:1.0.0 之后

时间复杂度:O(1)

返回值:key 对应的 value,或者 nil 当 key 不存在。

⽰例:

 redis> GET nonexisting
 (nil)
 redis> SET mykey "Hello"
 "OK"
 redis> GET mykey
 "Hello"
 redis> DEL mykey
 (integer) 1
 redis> EXISTS mykey
 (integer) 0
 redis> HSET mykey name Bob
 (integer) 1
 redis> GET mykey
 (error) WRONGTYPE Operation against a key holding the wrong kind of value

MGET

mget

⼀次性获取多个 key 的值。如果对应的 key 不存在或者对应的数据类型不是 string,返回 nil。

语法:

MGET key [key ...]

命令有效版本:1.0.0 之后

时间复杂度:O(N) N 是命令中 key 的数量,也可以认为是 O(1)

返回值:对应 value 的列表

⽰例:

 redis> SET key1 "Hello"
 "OK"
 redis> SET key2 "World"
 "OK"
 redis> MGET key1 key2 nonexisting
 1) "Hello"
 2) "World"
 3) (nil)

MSET

mset

⼀次性设置多个 key 的值。

语法:

MSET key value [key value ...]

命令有效版本:1.0.1 之后

时间复杂度:O(N) N 是命令中 key 的数量,也可以认为是 O(1)

返回值:永远是 OK

⽰例:

 redis> MSET key1 "Hello" key2 "World"
 "OK"
 redis> GET key1
 "Hello"
 redis> GET key2
 "World"

图 2-8 多次 get vs 单次 mget

如图 2-8 所⽰,使⽤ mget / mset 由于可以有效地减少了⽹络时间,所以性能相较更⾼。假设⽹络耗时 1 毫秒,命令执⾏时间耗时 0.1 毫秒,则执⾏时间如表 2-2 所⽰。

表 2-2 1000 次 get 和 1 次 mget 对⽐

操作时间
1000 次 get1000 x 1 + 1000 x 0.1 = 1100 毫秒
1 次 mget 1000 个键1 x 1 + 1000 x 0.1 = 101 毫秒

学会使⽤批量操作,可以有效提⾼业务处理效率,但是要注意,每次批量操作所发送的键的数量也不是⽆节制的,否则可能造成单⼀命令执⾏时间过⻓,导致 Redis 阻塞。

SETNX

setnx

设置 key-value 但只允许在 key 之前不存在的情况下。

语法:

SETNX key value

命令有效版本:1.0.0 之后

时间复杂度:O(1)

返回值:1 表⽰设置成功。0 表⽰没有设置。

⽰例:

redis> SETNX mykey "Hello"
(integer) 1
redis> SETNX mykey "World"
(integer) 0
redis> GET mykey
"Hello"

SET、SET NX 和 SET XX 的执⾏流程如图 2-9 所⽰。

图 2-9 SET、SET NX、SET XX 执⾏流程

计数命令

INCR

incr

将 key 对应的 string 表⽰的数字加⼀。如果 key 不存在,则视为 key 对应的 value 是 0。如果 key 对应的 string 不是⼀个整型或者范围超过了 64 位有符号整型,则报错。

语法:

INCR key

命令有效版本:1.0.0 之后

时间复杂度:O(1)

返回值:integer 类型的加完后的数值。

⽰例:

 redis> EXISTS mykey
 (integer) 0
 redis> INCR mykey
 (integer) 1
 redis> SET mykey "10"
 "OK"
 redis> INCR mykey
 (integer) 11
 redis> SET mykey "234293482390480948029348230948"
 "OK"
 redis> INCR mykey
 (error) value is not an integer or out of range
 redis> SET mykey 'not a number'
 "OK"
 redis> INCR mykey
 (error) value is not an integer or out of range

INCRBY

incrby

将 key 对应的 string 表⽰的数字加上对应的值。如果 key 不存在,则视为 key 对应的 value 是 0。如果 key 对应的 string 不是⼀个整型或者范围超过了 64 位有符号整型,则报错。

语法:

INCRBY key decrement

命令有效版本:1.0.0 之后

时间复杂度:O(1)

返回值:integer 类型的加完后的数值。

⽰例:

 redis> EXISTS mykey
 (integer) 0
 redis> INCRBY mykey 3
 (integer) 3
 redis> SET mykey "10"
 "OK"
 redis> INCRBY mykey 3
 (integer) 13
 redis> INCRBY mykey "not a number"
 (error) ERR value is not an integer or out of range
 redis> SET mykey "234293482390480948029348230948"
 "OK"
 redis> INCRBY mykey 3
 (error) value is not an integer or out of range
 redis> SET mykey 'not a number'
 "OK"
 redis> INCRBY mykey 3
 (error) value is not an integer or out of range

DECR

decr

将 key 对应的 string 表⽰的数字减⼀。如果 key 不存在,则视为 key 对应的 value 是 0。如果 key 对应的 string 不是⼀个整型或者范围超过了 64 位有符号整型,则报错。

语法:

DECR key

命令有效版本:1.0.0 之后

时间复杂度:O(1)

返回值:integer 类型的减完后的数值。

⽰例:

 redis> EXISTS mykey
 (integer) 0
 redis> DECR mykey
 (integer) -1
 redis> SET mykey "10"
 "OK"
 redis> DECR mykey
 (integer) 9
 redis> SET mykey "234293482390480948029348230948"
 "OK"
 redis> DECR mykey
 (error) value is not an integer or out of range
 redis> SET mykey 'not a number'
 "OK"
 redis> DECR mykey
 (error) value is not an integer or out of range

DECRBY

decrby

将 key 对应的 string 表⽰的数字减去对应的值。如果 key 不存在,则视为 key 对应的 value 是 0。如果 key 对应的 string 不是⼀个整型或者范围超过了 64 位有符号整型,则报错。

语法:

DECRBY key decrement

命令有效版本:1.0.0 之后

时间复杂度:O(1)

返回值:integer 类型的减完后的数值。

⽰例:

 redis> EXISTS mykey
 (integer) 0
 redis> DECRBY mykey 3
 (integer) -3 
 redis> SET mykey "10"
 "OK"
 redis> DECRBY mykey 3
 (integer) 7
 redis> DECRBY mykey "not a number"
 (error) ERR value is not an integer or out of range
 redis> SET mykey "234293482390480948029348230948"
 "OK"
 redis> DECRBY mykey 3
 (error) value is not an integer or out of range
 redis> SET mykey 'not a number'
 "OK"
 redis> DECRBY mykey 3
 (error) value is not an integer or out of range

INCRBYFLOAT

incrbyfloat

将 key 对应的 string 表⽰的浮点数加上对应的值。如果对应的值是负数,则视为减去对应的值。如果key 不存在,则视为 key 对应的 value 是 0。如果 key 对应的不是 string,或者不是⼀个浮点数,则报错。允许采⽤科学计数法表⽰浮点数。

语法:

INCRBYFLOAT key increment

命令有效版本:2.6.0 之后

时间复杂度:O(1)

返回值:加/减完后的数值。

⽰例:

 redis> SET mykey 10.50
 "OK"
 redis> INCRBYFLOAT mykey 0.1
 "10.6"
 redis> INCRBYFLOAT mykey -5
 "5.6"
 redis> SET mykey 5.0e3
 "OK"
 redis> INCRBYFLOAT mykey 2.0e2
 "5200"

很多存储系统和编程语⾔内部使⽤ CAS 机制实现计数功能,会有⼀定的 CPU 开销,但在 Redis 中完全不存在这个问题,因为 Redis 是单线程架构,任何命令到了 Redis 服务端都要顺序执⾏。

其他命令

APPEND

append

如果 key 已经存在并且是⼀个 string,命令会将 value 追加到原有 string 的后边。如果 key 不存在,则效果等同于 SET 命令。

语法:

APPEND KEY VALUE

命令有效版本:2.0.0 之后

时间复杂度:O(1). 追加的字符串⼀般⻓度较短, 可以视为 O(1).

返回值:追加完成之后 string 的⻓度(单位是字节)。

在启动 Redis 客户端的时候,加上一个 --raw,就可以使 Redis 客户端能够自动把二进制数据尝试翻译

⽰例:

 redis> EXISTS mykey
 (integer) 0
 redis> APPEND mykey "Hello"
 (integer) 5
 redis> GET mykey
 "Hello"
 redis> APPEND mykey " World"
 (integer) 11
 redis> GET mykey
 "Hello World"

GETRANGE

getrange

返回 key 对应的 string 的⼦串,由 start 和 end 确定(左闭右闭)。可以使⽤负数表⽰倒数。-1 代表倒数第⼀个字符,-2 代表倒数第⼆个,其他的与此类似。超过范围的偏移量会根据 string 的⻓度调整成正确的值。

如果字符串中保存的是汉字,此时进行子串切分,很可能切出来的就不是完整的汉字了

语法:

GETRANGE key start end

命令有效版本:2.4.0 之后

时间复杂度:O(N). N 为 [start, end] 区间的⻓度. 由于 string 通常⽐较短, 可以视为是 O(1)

返回值:string 类型的⼦串

⽰例:

 redis> SET mykey "This is a string"
 "OK"
 redis> GETRANGE mykey 0 3
 "This"
 redis> GETRANGE mykey -3 -1
 "ing"
 redis> GETRANGE mykey 0 -1
 "This is a string"
 redis> GETRANGE mykey 10 100
 "string"

SETRANGE

setrange

覆盖字符串的⼀部分,从指定的偏移开始(从第几个字节开始,开始进行替换,因为是字节,如果 value 是中文很可能出问题的)。

语法:

SETRANGE key offset value

命令有效版本:2.2.0 之后

时间复杂度:O(N), N 为 value 的⻓度. 由于⼀般给的 value ⽐较短, 通常视为 O(1).

返回值:替换后的 string 的⻓度。

⽰例:

 redis> SET key1 "Hello World"
 "OK"
 redis> SETRANGE key1 6 "Redis"
 (integer) 11
 redis> GET key1
 "Hello Redis"

STRLEN

strlen

获取 key 对应的 string 的⻓度(单位是字节)。当 key 存放的类型不是 string 时,报错。

Java 中的 char 是用 unicode,一个汉字是 2 个字节,String 是用 utf8,一个汉字是 3 字节

语法:

STRLEN key

命令有效版本:2.2.0 之后

时间复杂度:O(1)

返回值:string 的⻓度。或者当 key 不存在时,返回 0。

⽰例:

redis> SET mykey "Hello world"
"OK"
redis> STRLEN mykey
(integer) 11
redis> STRLEN nonexisting
(integer) 0
命令⼩结

表 2-3 是字符串类型命令的效果、时间复杂度,开发⼈员可以参考此表,结合⾃⾝业务需求和数据⼤⼩选择合适的命令。

表 2-3 字符串类型命令⼩结

命令执⾏效果时间复杂度
set key value [key value…]设置 key 的值是 valueO(k), k 是键个数
get key获取 key 的值O(1)
del key [key …]删除指定的 keyO(k), k 是键个数
mset key value [key value…]批量设置指定的 key 和 valueO(k), k 是键个数
mget key [key …]批量获取 key 的值O(k), k 是键个数
incr key指定的 key 的值 +1O(1)
decr key指定的 key 的值 -1O(1)
incrby key n指定的 key 的值 +nO(1)
decrby key n指定的 key 的值 -nO(1)
incrbyfloat key n指定的 key 的值 +nO(1)
append key value指定的 key 的值追加 valueO(1)
strlen key获取指定 key 的值的⻓度O(1)
setrange key offset value覆盖指定 key 的从 offset 开始的部分值O(n),n 是字符串⻓度, 通常视为 O(1)
getrange key start end获取指定 key 的从 start 到 end 的部分值O(n),n 是字符串⻓度, 通常视为 O(1)
内部编码

字符串类型的内部编码有 3 种:

  • int:8 个字节的⻓整型。
  • embstr:⼩于等于 39 个字节的字符串。(如果是一个浮点数,则是一个字符串)
  • raw:⼤于 39 个字节的字符串。

为什么是39?

如果实际业务场景有很多个 key,都是 string,每个 value 的长度都是 100 左右,关注与整体的内存空间,这样的字符串用 embstr 存储也不是不能考虑。

如果实现?

  1. 先看 Redis 是否提供了对应的配置项,可以修改这个数字
  2. 如果没有,就针对 Redis 源码进行修改

Redis 会根据当前值的类型和⻓度动态决定使⽤哪种内部编码实现。

Redis 存储小数本质上还是当做字符串来存储的

# 小数
127.0.0.1:6379> set key 1.5
OK
127.0.0.1:6379> object encoding key
"embstr"

整型类型⽰例如下:

 127.0.0.1:6379> set key 6379
 OK
 127.0.0.1:6379> object encoding key
 "int"

短字符串⽰例如下:

 # ⼩于等于 39 个字节的字符串
 127.0.0.1:6379> set key "hello"
 OK
 127.0.0.1:6379> object encoding key
 "embstr"

⻓字符串⽰例如下:

 # ⼤于 39 个字节的字符串
 127.0.0.1:6379> set key "one string greater than 39 bytes ........"
 OK
 127.0.0.1:6379> object encoding key
 "raw"
典型使⽤场景

缓存(Cache)功能

图 2-10 是⽐较典型的缓存使⽤场景,其中 Redis 作为缓冲层,MySQL 作为存储层,绝⼤部分请求的数据都是从 Redis 中获取。由于 Redis 具有⽀撑⾼并发的特性,所以缓存通常能起到加速读写和降低后端压力的作⽤。

这个策略,随着时间的推移,一定有越来越多的 key 在 Redis 上访问不到,从而从 MySQL 中读取并写入 Redis 了,此时 Redis 的数据是不是就积累得越来越多了?

  1. 把数据写给 Redis 的同时,根据实际情况分析给 key 设置一个过期时间
  2. Redis 在内存不足的时候, 提供了淘汰机制

图 2-10 Redis + MySQL 组成的缓存存储架构

下⾯的伪代码模拟了图 2-10 的业务数据访问过程:

  1. 假设业务是根据⽤⼾ uid 获取⽤⼾信息
UserInfo getUserInfo(long uid) {
 ...
}
  1. ⾸先从 Redis 获取⽤⼾信息,我们假设⽤⼾信息保存在 “user:info:” 对应的键中:

这里的 key 一般都需要搭配一些前缀来区分不同种类的数据,比如这里是 user:info

 // 根据 uid 得到 Redis 的键
 String key = "user:info:" + uid;
 
 // 尝试从 Redis 中获取对应的值
 String value = Redis 执⾏命令:get key;

 // 如果缓存命中(hit)
 if (value != null) {
 // 假设我们的⽤⼾信息按照 JSON 格式存储
 	UserInfo userInfo = JSON 反序列化(value);
 	return userInfo;
 }
  1. 如果没有从 Redis 中得到⽤⼾信息,及缓存 miss,则进⼀步从 MySQL 中获取对应的信息,随后写⼊缓存并返回:
 // 如果缓存未命中(miss)
 if (value == null) {
 	// 从数据库中,根据 uid 获取⽤⼾信息
 	UserInfo userInfo = MySQL 执⾏ SQL:select * from user_info where uid = <uid>
 
 	// 如果表中没有 uid 对应的⽤⼾信息
 	if (userInfo == null) {
 		响应 404
 		return null;
 	}
 
 	// 将⽤⼾信息序列化成 JSON 格式
 	String value = JSON 序列化(userInfo);
 
 	// 写⼊缓存,为了防⽌数据腐烂(rot),设置过期时间为 1 ⼩时(3600 秒)
 	Redis 执⾏命令:set key value ex 3600
 
 	// 返回⽤⼾信息
 	return userInfo;
 }

通过增加缓存功能,在理想情况下,每个⽤⼾信息,⼀个⼩时期间只会有⼀次 MySQL 查询,极⼤地提升了查询效率,也降低了 MySQL 的访问数。

与 MySQL 等关系型数据库不同的是,Redis 没有表、字段这种命名空间,⽽且也没有对键名有强制要求(除了不能使⽤⼀些特殊字符)。但设计合理的键名,有利于防⽌键冲突和项⽬的可维护性,⽐较推荐的⽅式是使⽤ “业务名:对象名:唯⼀标识:属性” 作为键名。例如 MySQL 的数据库名为 vs,⽤⼾表名为 user_info,那么对应的键可以使⽤"vs:user_info:6379"、“vs:user_info:6379:name” 来表⽰,如果当前 Redis 只会被⼀个业务使⽤可以省略业务名 “vs:”。如果键名过程则可以使⽤团队内部都认同的缩写替代例如 “user:6379:friends:messages:5217” 可以被 “u:6379:fr:m:5217” 代替。毕竟键名过⻓,还是会导致 Redis 的性能明显下降的。

计数(Counter)功能

许多应⽤都会使⽤ Redis 作为计数的基础⼯具,它可以实现快速计数、查询缓存的功能,同时数据可以异步处理或者落地到其他数据源。如图 2-11 所⽰,例如视频⽹站的视频播放次数可以使⽤ Redis 来完成:⽤⼾每播放⼀次视频,相应的视频播放数就会⾃增 1。

不过其实 Redis 并不擅长数据统计,基于 Redis 实现是很麻烦的,图里 Redis 只是计数,要想更好得统计,还是要把数据存储到统计数据仓库,让别的功能实现

图 2-11 记录视频播放次数

// 在 Redis 中统计某视频的播放次数
long incrVideoCounter(long vid) {
 key = "video:" + vid;
 long count = Redis 执⾏命令:incr key
 return counter;
}

实际中要开发⼀个成熟、稳定的真实计数系统,要⾯临的挑战远不⽌如此简单:防作弊、按照不同维度计数、避免单点问题、数据持久化到底层数据源等。

共享会话(Session)

如图 2-12 所⽰,⼀个分布式 Web 服务将⽤⼾的 Session 信息(例如⽤⼾登录信息)保存在各自的服务器中,但这样会造成⼀个问题:出于负载均衡的考虑,分布式服务会将⽤⼾的访问请求均衡到不同的服务器上,并且通常⽆法保证⽤⼾每次请求都会被均衡到同⼀台服务器上,这样当⽤⼾刷新⼀次访问是可能会发现需要重新登录,这个问题是⽤⼾⽆法容忍的。

Cookie 是浏览器存储数据的机制,Session 是服务器存储数据的机制

如果每个应用服务器维护自己的会话数据,但彼此直接不共享,用户请求访问到不同的服务器上,就可能出现不能正确处理的情况

图 2-12 Session 分散存储

为了解决这个问题,可以使⽤ Redis 将用户的 Session 信息进⾏集中管理,如图 2-13 所⽰,在这种模式下,只要保证 Redis 是⾼可⽤和可扩展性的,⽆论⽤⼾被均衡到哪台 Web 服务器上,都集中从 Redis 中查询、更新 Session 信息。

图 2-13 Redis 集中管理 Session

手机验证码

很多应⽤出于安全考虑,会在每次进⾏登录时,让⽤⼾输⼊⼿机号并且配合给⼿机发送验证码,然后让⽤⼾再次输⼊收到的验证码并进⾏验证,从⽽确定是否是⽤⼾本⼈。为了短信接口不会频繁访问,会限制用户每分钟获取验证码的频率,例如⼀分钟不能超过 5 次,如图 2-14 所⽰。

图 2-14 短信验证码

此功能可以⽤以下伪代码说明基本实现思路:

String 发送验证码(phoneNumber) {
    key = "shortMsg:limit:" + phoneNumber;
    // 设置过期时间为 1 分钟(60 秒)
    // 使⽤ NX,只在不存在 key 时才能设置成功
    bool r = Redis 执⾏命令:set key 1 ex 60 nx
    if (r == false) {
        // 说明之前设置过该⼿机的验证码了
        long c = Redis 执⾏命令:incr key
        if (c > 5) {
            // 说明超过了⼀分钟 5 次的限制了
            // 限制发送
            return null;
        }
    }
    
    // 说明要么之前没有设置过⼿机的验证码;要么次数没有超过 5 次
    String validationCode = ⽣成随机的 6 位数的验证码();
    
    validationKey = "validation:" + phoneNumber;
    // 验证码 5 分钟(300 秒)内有效
    Redis 执⾏命令:set validationKey validationCode ex 300;
    
    // 返回验证码,随后通过⼿机短信发送给⽤⼾
    return validationCode;
}

// 验证⽤⼾输⼊的验证码是否正确
bool 验证验证码(phoneNumber, validationCode) {
    validationKey = "validation:" + phoneNumber;
    
    String value = Redis 执⾏命令:get validationKey;
    if (value == null) {
        // 说明没有这个⼿机的验证码记录,验证失败
        return false;
    }
    
    if (value == validationCode) {
        return true;
    } else {
        return false;
    }
}

短信发送的操作,是有专门的 SDK 来实现的,即第三方提供的短信平台服务

以上介绍了使⽤ Redis 的字符串数据类型可以使⽤的⼏个场景,但其适⽤场景远不⽌于此,开发⼈员可以结合字符串类型的特点以及提供的命令,充分发挥⾃⼰的想象⼒,在⾃⼰的业务中去找到合适的场景去使⽤ Redis 的字符串类型。

谈谈业务

业务就是一个公司/产品,是如何解决一个/一系列问题的。

解决问题的过程,就可以称为是“业务”。

一个公司/产品要想存在,就得赚钱,就得帮比人解决问题。

不同的公司,不同的产品,就有不同的业务。不同的业务就需要不同的技术作为支撑。很多时候,优化技术解决不了的问题,可以通过优化业务来解决。

实际开发过程中,必须要结合实际业务场景,做一些技术上的调整。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2049009.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

牙科就诊管理系统--论文pf

TOC springboot399牙科就诊管理系统--论文pf 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;…

企业考勤管理神器:9款精选软件推荐

本文将介绍9款考勤管理软件&#xff1a;Moka、超人HR、慧点、云易通、麦勤通、TeKard考勤管理系统、Toggl Track、ZoomShift、Kronos Workforce Ready。 选择合适的考勤管理软件对企业来说可不是件小事。面对市场上琳琅满目的工具&#xff0c;选错了不仅浪费时间和金钱&#xf…

【二分查找】--- 初阶题目赏析

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; 算法Joureny 上篇我们讲解了关于二分的朴素模板和边界模板&#xff0c;本篇博客我们试着运用这些模板。 &#x1f3e0; 搜索插入位置 &#x1f4cc; 题目…

如何使用和配置 AWS CLI 环境变量?

欢迎来到雲闪世界。环境变量在配置和保护应用程序方面起着至关重要的作用&#xff0c;在使用 AWS CLI&#xff08;命令行界面&#xff09;时&#xff0c;它们的使用尤其重要。在这篇博客文章中&#xff0c;我们将深入探讨环境变量的世界&#xff0c;探索它们的用途、它们在 AWS…

【Python】OBS 脚本

文章目录 一、指定python解释器和脚本二、特殊函数名三、obspython API四、 实际应用示例(mkv转mp4封装后自动删除mkv)OBS Studio是一个流行的开源软件,用于视频录制和直播。除了其内置功能外,OBS还支持通过Python脚本(以及lua脚本)进行扩展,允许用户自定义和自动化各种…

JS模块化总结 | CommonJS、ES6

BV13W42197jR 个人笔记 目录 JS模块化基础知识1. 概述1.1 什么是模块化1.2 为什么需要模块化? 2 模块化规范3 导入&导出4 CommonJS规范4.1 初步体验4.2 导出数据4.3 导入数据4.4 扩展理解4.5 浏览器端运行 5 ES6模块化规范5.1 初步体验5.2 Node中运行ES65.3 导出数据①分别…

C++ 设计模式——工厂方法模式

工厂方法模式 工厂方法模式主要组成部分代码实现工厂方法模式模式的 UML 图工厂方法模式 UML 图解析优点和缺点适用场景 工厂方法模式 工厂方法模式是一种创建型设计模式&#xff0c;它通过定义一个接口用于创建对象&#xff0c;但由子类决定实例化哪个类。与简单工厂模式不同…

笔记 5 : 彭老师课本第 5 章 , 开始代码编程

&#xff08;49&#xff09;大纲&#xff1a; &#xff08;50&#xff09; 系统&#xff1a; &#xff08;51&#xff09; 学习路线&#xff1a; &#xff08;52&#xff09;该款 soc 的内存布局&#xff1a; 细化的 SFR 地址空间 &#xff1a; &#xff08;53&#xff09; soc…

IMX8M核心板偶发系统启动失败排查实录

一. 基本情况介绍 1. 硬件方案组成 产品中采用的是IMX8M核心板的方案&#xff0c;如图1所示是产品的硬件系统整体组成示意图。 图1 产品硬件组成框图 2. 问题描述 今年5月份有一台设备出现系统偶发启动失败&#xff0c;uboot阶段都无法通过&#xff0c;表面现象是显示屏黑屏…

Android持久化技术—SharedPreferences存储

文章目录 Android持久化技术—SharedPreferences存储将数据存储到SharedPreferences中Context类中的getSharedPreferences()方法Activity类中的getPreferences()方法PreferenceManager类中的getDefaultSharedPreferences()方法 从SharedPreferences中读取数据 Android持久化技术…

【自动驾驶】ROS远程节点的分布式通信

目录 固定IP的设置将IP地址改成手动配置文件修改配置主从机的~/.bashrc 文件配置主机的 IP 地址配置从机IP ROS是一个分布式计算环境。一个运行中的ROS系统可以包含分布在多台计算机上多个节点。根据系统的配置方式&#xff0c;任何节点可能随时需要与任何其他节点进行通信。 …

XSS靶场————XSS.pwnfunction

目录 第一关&#xff1a;Ma Spaghet! 第二关&#xff1a;Jefff 第三关&#xff1a;Ugandan Knuckles​编辑 第四关&#xff1a;Ricardo Milos ​编辑 第五关&#xff1a;Ah Thats Hawt​编辑 第六关&#xff1a;Ligma 第七关&#xff1a;Mafia​编辑 第八关&#xff1a…

交易系统JVM内存优化

背景 新交易系统上线以后&#xff0c;业务指标&#xff08;成单率&#xff09;和系统指标&#xff08;CPU、QPS、JVM内存&#xff09;是我们重点关注的指标。 CPU较高&#xff1a;可以通过Arthas等工具查看繁忙线程的堆栈信息&#xff0c;定位具体的代码&#xff0c;具体分析…

谷歌浏览器字体模糊不清怎么办

许多小伙伴在使用谷歌浏览器时&#xff0c;可能都遇见过字体模糊不清的情况&#xff0c;这将对我们的浏览体验大打折扣。为了解决这以问题&#xff0c;本文将为大家带来谷歌浏览器字体模糊不清的解决方法&#xff0c;帮助你享受到更清晰舒适的阅读环境。&#xff08;本文由chro…

聚星文社AI工具官方

聚星文社是一键AI推文生成工具超强功能极速版介绍聚星文社AI工具官方https://docs.qq.com/doc/DRU1vcUZlanBKR2xy 同时集成原创和反推搬运功能 并可一键切换MJ或SD进行批量出库 一键自动智能分镜或手动快速分镜 精准排布镜头功能全预设 超简化流程 3秒即可完成参数配置全网最全…

Java 并发(五)—— 线程池

线程池核心参数&#xff1f;&#xff08;核心线程数、最大线程数、任务队列&#xff09;线程池构造方法中除了保存参数以外还要做什么事&#xff1f;&#xff08;设置线程工厂、任务拒绝策略&#xff09;提交任务时线程池要做什么&#xff1f;&#xff08;任务执行机制&#xf…

Golang Map 深度剖析:原理、实践与面试要点

嘿,小伙伴们!我是 k 哥。今天,咱们来聊聊 Map 。 在 Go 语言这个神奇的世界里,Map 这个有点神秘的数据结构一直都是开发者们特别关注的。 你是不是在用 Map 的时候,对它里面咋工作的感到好奇?是不是碰到复杂操作的时候,特别想弄明白它背后的原理?别着急,今天这篇文章…

Java流程控制06:嵌套for循环

本节教学视频链接&#xff1a;https://www.bilibili.com/video/BV12J41137hu?p41&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5https://www.bilibili.com/video/BV12J41137hu?p41&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 在Java中&#xff0c;‌嵌套for循环是指将…

使用三菱PLC源码进行PLC读取写入操作

安装 MX Component 。 我的安装地址在&#xff1a; 打开 utl 文件夹下的 Communication Settings Utility 执行。 配置PLC 添加当前需要配置的PLC 注意 logical station Namber 就是程序里需要对接的逻辑站点编号 5.配置选择对应的COM操作选择对应的cpu型型号&#xff0c;…

Ah That‘s Hawt

目录 一、题目 二、思路 三、payload 3.1 方案一 3.1 方案二 四、思考与总结 一、题目 <!-- Challenge --> <h2 id"will"></h2> <script>smith (new URL(location).searchParams.get(markassbrownlee) || "Ah Thats Hawt")sm…