做网站容易还是app,pico笔克品牌介绍,html5 中文网站模板,正邦设计公司string类型
类型介绍
在Redis中的所有的key都是string类型#xff0c;而value的类型有多种。
Redis中的字符串是直接按照二进制的方式进行存储的#xff0c;也就是不会做任何的编码转换#xff0c;存的是什么#xff0c;取出来的就是什么。这样一般来说#xff0c;Redi…string类型
类型介绍
在Redis中的所有的key都是string类型而value的类型有多种。
Redis中的字符串是直接按照二进制的方式进行存储的也就是不会做任何的编码转换存的是什么取出来的就是什么。这样一般来说Redis遇见乱码的可能性会变小。因为这里会出现乱码就只跟存入端和读取端有关而像mysql这样自己会做编码转换的那么出现的乱码的原因还可能跟mysql自身的编码转换有关。
另外因为Redis存储字符串是按二进制存储的所以不仅仅只可以存文本数据 甚至还可以存图片音频。 set/get
再看一下set的语法
SET key value [expiration EX seconds|PX milliseconds] [NX|XX] 对于后面的选项语法格式 [expiration EX seconds|PX milliseconds] 也就是在设置key的时候同时设置超时时间EX就是以秒级单位的PX就是毫秒。
[NX|XX] NX表示 如果key存在那么就不设置 返回nil如果不存在那么才设置。
XX表示 key存在才设置相当于更新key如果key不存在那么就不设置。 这样的额外选项可以减少网络轮次在客户端-服务器这样的程序下还是很有用的。 并且对于一条命令来说它的执行效果是原子的而分开的话就不好说了。
用命令
flushall 可以进行删库操作将所有的数据全部删除。这个操作非常危险几乎不会用到。 mset/mget
mset⼀次性设置多个 key 的值。
MSET key value [key value ...] mget⼀次性获取多个 key 的值。如果对应的 key 不存在或者对应的数据类型不是 string返回 nil。
MGET key [key ...]
这里的时间复杂度官方给的是ON不过这里的N跟我们之前说的一样指的是key的数目。
这两个命令同样是为了减少请求的网络轮次的。 setnx/setex/psetex
这三个命令其实就是对set的一些常见用法进行了缩写。 incr/incrby/decr/decrby/incrbyfloat incr只能针对一个整数使用返回这个数加一后的值。相当于 i其他的命令同理。
incrby可以针对value进行 n的操作。并且这里的n可以是负数如果是负数就相当于减法操作。
另外在Redis中的int是64位的。
为什么Redis明明可以使用一套命令就可以实现加减法为什么要设计两套呢这一点跟刚刚的setnx那些命令是一样的是为了让Redis的使用更加符合人的直觉从而降低用户的使用门槛。
另外如果我们 incr/incrby的key不存在时它会帮我们插入一个value为0的值然后进行对应的加减操作。 剩下三个命令的使用原理跟incr和incrby差不多。这些命令的时间复杂度都是O1。 另外这个 incrbyfloat 中的float其实是按double的标准来的。
append 如果 key 已经存在并且是⼀个 string命令会将 value 追加到原有 string 的后边。如果 key 不存在则效果等同于 SET 命令。 APPEND KEY VALUE 时间复杂度O(1). 追加的字符串⼀般⻓度较短, 可以视为 O(1). 返回值追加完成之后 string 的字节大小。 注意append这里的返回值 的单位是字节。 如果我们想让Redis客户端能够自动的把二进制数据尝试翻译那么在启动客户端的时候要加上一个 --raw的选项 可以看到当我们启动时没有带 --raw时我们查出来的汉字是十六进制显示的其中每个十六进制前的 \x 是转义字符表示这是一个十六进制。 getrange 返回 key 对应的 string 的⼦串由 start 和 end 确定左闭右闭。可以使⽤负数表⽰倒数。-1 代表倒数第⼀个字符-2 代表倒数第⼆个其他的与此类似。超过范围的偏移量会根据 string 的⻓度调整成正确的值。 GETRANGE key start end 在C和Java中谈到一个区间大多都是左闭右开的 [x,y) 。而Redis这里都是闭区间 [x,y] 时间复杂度O(N). N 为 [start, end] 区间的⻓度. 由于 string 通常⽐较短, 可以视为是 O(1) 返回值string 类型的⼦串 没有就返回空串 注意当我们存的值是汉字时因为一个汉字占3个字节所以使用这个切分的时候还需要考虑到这一点不然切出来的可能是乱码 setrange
覆盖字符串的⼀部分从指定的偏移开始。 如果从指定偏移位置覆盖的字符串超出了原本能覆盖的大小那么多余的字符串会以追加的形式覆盖。
SETRANGE key offset value 时间复杂度O(N), N 为 value 的⻓度. 由于⼀般给的 value ⽐较短, 通常视为 O(1). 返回值替换后的 string 的⻓度。 注意当value是一个中文字符串的时候进行setrange是有可能出问题的。 另外setrange对于不存在的key也是可以操作的 不过会把 offset偏移位置之前的内容填充成0x00。 strlen 获取 key 对应的 string 的⻓度。当 key 存放的类似不是 string 时报错。
STRLEN key 时间复杂度O(1) 返回值string 的⻓度单位依旧是字节。或者当 key 不存在时返回 0。 string编码方式 int8 个字节的⻓整型。 • embstr⼩于等于 39 个字节的字符串。 • raw⼤于 39 个字节的字符串。 Redis 会根据当前值的类型和⻓度动态决定使⽤哪种内部编码实现。 不过不建议记住这样的数字记住数字是没有意义的。
根据业务场景的不同最佳的长度不一定是39字节此时就需要自行修改这个数字的大小
关于修改 另外我们发现Redis存储整形会用int类型8字节大小但是存储小数其实是用embstr的也就是说存储小数本质还是当作字符串来存储的。 这样的差别其实是很大的因为对于整形 进行算术运算的时候是比较方便的但是如果是字符串存储那么进行算术运算的时候要先将字符串转化为小数然后再进行算术运算最后还要再转回字符串开销就大多了。 string的应用场景
作为缓存 作为缓存的思路应用服务器访问数据时先查询Redis如果Redis上有这个数据那么就直接返回给应用服务器如果没有那么就去MySQL中去查找在MySQL中找到后再返回给应用服务器同时会把这个数据写入到Redis中这个写入到Redis中的值作为key往往被设置了过期时间
Redis这样的缓存经常存储热点数据也就是高频被访问的数据。这样能够加速查询和缓解MySQL服务器的压力。 伪代码模拟业务数据访问过程
1.根据用户uid获取用户信息
UserInfo getUserInfo(long uid) {...
} 2.⾸先从 Redis 获取⽤⼾信息我们假设⽤⼾信息保存在 user:info:uid 对应的键中
// 根据 uid 得到 Redis 的键
String key user:info: uid;
// 尝试从 Redis 中获取对应的值String value Redis 执⾏命令get key;
// 如果缓存命中hit
if (value ! null) {// 假设我们的⽤⼾信息按照 JSON 格式存储UserInfo userInfo JSON 反序列化(value);return userInfo;
}3. 如果没有从 Redis 中得到⽤⼾信息及缓存 miss则进⼀步从 MySQL 中获取对应的信息随后写⼊缓存并返回 // 如果缓存未命中miss
if (value null) {// 从数据库中根据 uid 获取⽤⼾信息UserInfo userInfo MySQL 执⾏ SQLselect * from user_info where uid
uid// 如果表中没有 uid 对应的⽤⼾信息if (userInfo null) {响应 404return 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 的性能明显下降的。 计数功能 许多应⽤都会使⽤ Redis 作为计数的基础⼯具它可以实现快速计数、查询缓存的功能同时数 据可以异步处理或者落地到其他数据源。 Redis将播放量同步到其他数据源的方式是异步的。 不过Redis用作计数功能是挺不错的但是让Redis统计数据就不行了比如让Redis统计播放量前百的视频就很麻烦如果让MySQL来的话一行sql就搞定了 排序加limit 100 实际中要开发⼀个成熟、稳定的真实计数系统要⾯临的挑战远不⽌如此简单防作弊、按 照不同维度计数、避免单点问题、数据持久化到底层数据源等。 比如要防作弊一个用户对某一个视频播放量的贡献就有几百上千这明显不合理。 不同的维度比如视频的完播率点赞量转发量评论数等 共享会话 ⼀个分布式 Web 服务将⽤⼾的 Session 信息例如⽤⼾登录信息保存在各⾃的服务器中但这样会造成⼀个问题出于负载均衡的考虑分布式服务会将⽤⼾的访问请求均衡到不同的服务器上并且通常⽆法保证⽤⼾每次请求都会被均衡到同⼀台服务器上这样当⽤⼾刷新⼀次访问是可能会发现需要重新登录这个问题是⽤⼾⽆法容忍的。 为了解决这个问题可以使⽤ Redis 将⽤⼾的 Session 信息进⾏集中管理如图 2-13 所⽰在这种模式下只要保证 Redis 是⾼可⽤和可扩展性的⽆论⽤⼾被均衡到哪台 Web 服务器上都集中从Redis 中查询、更新 Session 信息。 手机验证码 很多应⽤出于安全考虑会在每次进⾏登录时让⽤⼾输⼊⼿机号并且配合给⼿机发送验证码 然后让⽤⼾再次输⼊收到的验证码并进⾏验证从⽽确定是否是⽤⼾本⼈。为了短信接⼝不会频繁访问会限制⽤⼾每分钟获取验证码的频率例如⼀分钟不能超过 5 次。 伪代码实现思路 String 发送验证码(phoneNumber) {key shortMsg:limit: phoneNumber;// 设置过期时间为 1 分钟60 秒// 使⽤ NX只在不存在 key 时才能设置成功bool r Redis 执⾏命令set key 1 ex 60 nxif (r false) {// 说明之前设置过该⼿机的验证码了long c Redis 执⾏命令incr keyif (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;}
} 以上介绍了使⽤ Redis 的字符串数据类型可以使⽤的⼏个场景但其适⽤场景远不⽌于此开发 ⼈员可以结合字符串类型的特点以及提供的命令充分发挥⾃⼰的想象⼒在⾃⼰的业务中去找到合适的场景去使⽤ Redis 的字符串类型。 hash类型
类型介绍 ⼏乎所有的主流编程语⾔都提供了哈希hash类型它们的叫法可能是哈希、字典、关联数 组、映射。在 Redis 中哈希类型是指值本⾝⼜是⼀个键值对结构形如 key keyvalue { { field1, value1 }, ..., {fieldN, valueN } }。 注意 哈希类型中的映射关系通常称为 field-value⽤于区分 Redis 整体的键值对key-value 注意这⾥的 value 是指 field 对应的值不是键key对应的值请注意 value 在不同上下 ⽂的作⽤。 基础命令 1hset/hget/hexists/hdel
hset
设置 hash 中指定的字段field的值value。
HSET key field value [field value ...] 时间复杂度插⼊⼀组 field 为 O(1), 插⼊ N 组 field 为 O(N) 返回值添加的字段的个数。 hget
获取 hash 中指定字段的值。
HGET key field 时间复杂度O(1) 返回值字段对应的值或者 nil。 hexists
判断 hash 中是否有指定的字段。
HEXISTS key field 时间复杂度O(1) 返回值1 表⽰存在0 表⽰不存在。 hdel
删除 hash 中指定的字段。
HDEL key field [field ...] 时间复杂度删除⼀个元素为 O(1). 删除 N 个元素为 O(N). 返回值本次操作删除的字段个数。 如果要删除所有字段那么就是
del key 基础命令2hkeys / hvals / hgetall
hkeys
获取 hash 中的所有字段。
HKEYS key 时间复杂度O(N), N 为 field 的个数. 返回值字段列表。也就是这个key对应的所有的field。 hvals 获取 hash 中的所有的值。 HVALS key 时间复杂度O(N), N 为 field 的个数. 返回值所有的值。与hkeys相对这里返回的是这个key对应的所有的value。 hgetall
获取 hash 中的所有字段以及对应的值。
HGETALL key 时间复杂度O(N), N 为 field 的个数. 返回值字段和对应的值。 可以看到查询出来的结果 是一对 field和value 的形式规律显示的。
上述的操作都是风险比较大的数据量多大时就可能会导致Redis阻塞。 基础命令3hmget / hlen / hsetnx / hincrby
hmget
⼀次获取 hash 中多个字段的值
HMGET key field [field ...] 时间复杂度只查询⼀个元素为 O(1), 查询多个元素为 O(N), N 为查询元素个数. 返回值字段对应的值或者 nil。 对于hmget命令同样也还有一个hmset命令可以同时设置多个 filed value但是没必要因为hset本身就已经有这个功能了。 hlen 获取 hash 中的所有字段的个数。 HLEN key 时间复杂度O(1) 返回值字段个数。 hsetnx
在字段不存在的情况下设置 hash 中的字段和值。
HSETNX key field value 时间复杂度O(1) 返回值1 表⽰设置成功0 表⽰失败。 hincrby
将 hash 中字段对应的数值添加指定的值。
HINCRBY key field increment 时间复杂度O(1) 返回值该字段变化之后的值。 hincrbyfloat HINCRBY 的浮点数版本。 HINCRBYFLOAT key field increment 时间复杂度O(1) 返回值该字段变化之后的值。 使用示例 hash的编码方式 哈希的内部编码有两种 • ziplist压缩列表当哈希类型元素个数⼩于 hash-max-ziplist-entries 配置默认 512 个、 同时所有值都⼩于 hash-max-ziplist-value 配置默认 64 字节时Redis 会使⽤ ziplist 作为哈 希的内部实现ziplist 使⽤更加紧凑的结构实现多个元素的连续存储所以在节省内存⽅⾯⽐ hashtable 更加优秀。 • hashtable哈希表当哈希类型⽆法满⾜ ziplist 的条件时Redis 会使⽤ hashtable 作为哈希 的内部实现因为此时 ziplist 的读写效率会下降⽽ hashtable 的读写时间复杂度为 O(1)。 下⾯的⽰例演⽰了哈希类型的内部编码以及响应的变化。 压缩的本质就是对数据进行重新编码不同的数据有不同的特点结合这些特点进行精妙的设计重新编码之后就可以缩小体积。
示例 哈希类型的应用场景
作为缓存
之前说过string类型也可以作为缓存哈希类型也可以作为缓存。
当存储结构化的数据时使用hash作为缓存就更合适一些。
比如 如果用string来存储这样的具有表结构的数据的话那么就得把数据转化为json的格式。
但是如果我们只想要操作某一对filed value那么就需要将整个key json读取出来然后解析成对象然后对其进行修改修改完之后又需要将字符串重新转为json再写回去。
但是如果此时是用hash存储的数据那么就可以很方便的取出和修改数据了。
总结使用hash的方式确实可以高效的对filed进行读写但是hash也付出了更大的空间上的代价 其实还有一种缓存方式那就是用原生字符串类型。每一个属性用一个键key 但是这种方式是不推荐的因为它把同一个数据的各个属性给分散开了也就是低内聚。这里不要把内聚和耦合混淆了。 一般写代码追求的是高内聚低耦合。 但是需要注意的是哈希类型和关系型数据库有两点不同之处 • 哈希类型是稀疏的⽽关系型数据库是完全结构化的例如哈希类型每个键可以有不同的 field⽽ 关系型数据库⼀旦添加新的列所有⾏都要为其设置值即使为 null如图 所⽰。 • 关系数据库可以做复杂的关系查询⽽ Redis 去模拟关系型复杂查询例如联表查询、聚合查询等 基本不可能维护成本⾼。 还有一个问题就是 缓存方式对比
string类型缓存
序列化字符串类型例如JSON格式 优点针对总是以整体作为操作的信息⽐较合适编程也简单。同时如果序列化⽅案选择合适内存的使⽤效率很⾼。 缺点本⾝序列化和反序列需要⼀定开销同时如果总是操作个别属性则⾮常不灵活。 哈希类型缓存 优点简单、直观、灵活。尤其是针对信息的局部变更或者获取操作。 缺点需要控制哈希在 ziplist 和 hashtable 两种内部编码的转换可能会造成内存的较⼤消耗。 原生字符串类型
使⽤字符串类型每个属性⼀个键。 优点实现简单针对个别属性变更也很灵活。 缺点占⽤过多的键内存占⽤量较⼤同时⽤⼾信息在 Redis 中⽐较分散缺少内聚性所以这种⽅案基本没有实⽤性。