Redis 入门到五大类型实现,java 基础知识点大全
key 没有类型区分,value 有五种类型 string(byte)包含 字符串、数值、bitmap
Object 相关指令 object encoding key 查看 key 的 value 的类型(例:set k1 99, type k1 = int,int 不是数据库五大类型之一,他是 string 类型的一种数值,查看 value 这个值的类型,跟设置的方法无关)
string 类型(String 一共分为数值,字符串和 bitmap 三种)
①:字符串相关
set key value nx 新建 key 的 value,失败返回 nil(nx 代表 key 不存在时才可以新建 key:value,使用场景:分布式锁,一群人调用一个 redis,一个成功其他都为 nil)
set key value xx 更新 key 的 value,失败返回 nil(xx 代表 key 存在时才可以更新 key:value)
mset key value key value … … 批量设置 key value
mget key key … … 批量获取 key 的 value
append key “” 在 key 的 value 后追加 str 字符串
getrange key 取出 key 从 start 下标到 end 下标的字符串(索引和 java 字符串一样是从 0 开始下标) 元素的正向索引(从左往右)从 0 开始,逆向索引(从右往左)从-1 开始-2 -3 …
setrange key 将 key 的 value 从 offset 索引位以后都替换为 str strlen key 获取 key 的 value 的字节长度
type key 查看当初设置 key:value 的组的类型(例:set k1 99, type k1 = string,因为 Set 方法属于 string 类型的组,type 只会查看当初设置这个 value 所用方法属于是什么类型)
getset key newValue 把 key 的 value 改为 newValue 并返回原 value
msetnx key value key value … … 批量设置多个 key 的 value,只有 key 不存在时才可以设置,这个方法具有原子性,多个 key value 只要有一个 key 存在,这方法所有 key value 设置无效
②:数值相关
incr key 若 value 值为 int 类型,直接+1,存入 key 的 value
incrby key 给 value 值加 int,存入 key 的 value decr key -1,存入 decrby key -int 存入
incrbyfloat key +float(比如 0.5),存入 float 相关类似上面
redis 中二进制安全
①:因为在不同的语言中对 int 的宽度的理解是不同的,所以一般来讲跨语言都比较喜欢实用 json,xml 这种文本的
传输,而不使用序列化,因为容易发生数据的溢出截断。 由于这种情况,redis 的二进制安全就是这种意思,redis 只取字节流而不取字符流,因为编码、语言的不同造成的数据截断等情况不会发生,给什么存什么,不会发生字节流转换字符流出现溢出阶段,所以读取的时候客户端的编码和 redis 的编码一致就不会乱码,但内容实际上不会发生改变,即存的时候的编码风格只不过看的时候因为编码不一致而乱码,保证了数据存储的安全。
②: key 有
两个过程。
1)先进行 type:value 的过程,如果类型不对报错,防止程序中进行计算再出现字符串和数值相加的异常
2)在进行 endcoding,防止后续字节流转换字符流计算问题,提速 当设置了一个 key 的 value 为 int 时,就会标记这个 value 为 int,下次调用 incr 等计算时就直接判断 encoding,就不需要转码,而是判断类型如果不合适计算就报错了,直接进入计算,这也就是 redis 快的一个原因,跳过了一个转码过程
③:所以在多人客户端连接一个 redis 时,客户端之间要沟通好编码
一个字节 8 个二进制位
中间两个长方形代表两个字节,里面的 0 代表位,上面圈代表一个字节的一个索引,下面的长方形中的数字代表二进制位的索引,在 Redis 中二进制位也有索引,虽然字节是割裂的但是二进制位的索引是连续的。
bitMap 类型(属于 String 类型中,也是 8、String 中的③:)
①:setbit key value 设置 key 的值的二进制位偏移量为 offset 的值为 value(在二进制中值只能填 0 或者 1) 直接连接下面 11、项 bitmap 来解释 (例:setbit k1 1 1 代表 k1 的二进制位的 1 偏移量的值改为 1 此时 strlen k1 = 1 因为目前两个二进制位,8 个二进制位才为 1 字节,strlen 取的是字节长度 get k1 = @ 因为 1 偏移量也就是 1 索引位置被改成了 1,所以二进制位为 01,而 01 在 ASCII 码中为 @)
②:man ascii 查询 ASCII 码表
③:bitpos key 在 key 的 value 的 start 到 end 索引的字节之中,寻找二进制位 bit 的第一个索引
④:bitcount key 在 key 的 value 的 start 到 end 索引的字节之中的二进制位中,寻找 1 出现的次数(只能专门搜索 1 出现的次数)
⑤:0100 0001 = A 0100 0010 = B 1)二进制位操作: 与:有 0 则 0,全 1 为 1 或:有 1 则 1,全 0 为 0
⑥:bitop newKey key1 key2 对 key1 和 key2 的值进行二进制的 operate 操作,结果存入 newKey 的值 例如:k1 存的为 01000001=A,k2 存的为 01000010=B, 则:bitop and k3 k1 k2 get k3 = 01000000 = @
⑦:bitmap 的重要性: 场景:
1)需求:用户系统,统计用户的登陆天数且窗口随机。
实现:若是使用关系型数据库,需要一张用户登录表,登陆一次产生一条记录,表中至少一个存储用户数据的外键字段(保守说 4 个字节需要),还需要一个登录日期字段(保守四个字节),这就需要四个字节来存储一条登录数据,在大数据情况下查询成本高,一年用户天天登录需要 8_365 字节存储
优化:使用 redis 数据,用每个二进制位表示一天,一个用户仅需 365/8 的字节存储登陆天数,setbit allen 364 1,设置 allen 这个人第 365 天登录了,需求中的窗口随机代表,突然老板说想看最近这两周半个月登录情况(随机什么时间的登录情况就叫窗口随机),因为一个二进制位为一天,只需数出最后十五天的二进制位为 1 的数量,bitcount allen -2 -1 = 1,因为这里的-2,-1 都是字节的索引位,-1 是倒数第一个,-2 是倒数第二个,也就是 2_8=16 二进制位=16 天的为 1 的二进制位数量,也就是最近十六天登陆天数。此时的二进制位代表的是登陆的天数,横向排列。
2)需求:商城 618 活动,这一天登陆送礼物,每人唯一,大库备货多少礼物,假设京东一共 2 亿用户账号
常识:用户分为僵尸用户、冷热用户/忠诚用户
实现:活跃用户统计,实际上有多少用户,随机窗口,统计 1 号 2 号 3 号用户登录数量,去重。
优化:redis。
比如第一天:setbit 20190101 1 1,20190101 后面的第一个 1 事先做好一个映射到那个用户,也就是说 1 代表 allen 或者谁,2 代表谁…,上面表示 20190101 这天,1 号(约定他就是 allen)登陆了
第二天:setbit 20190102 1 1, setbit 20190102 7 1,20190102 这天 1 号又登陆了,7 号(约定是谁)也登陆了
以此类推。最后数出这两天活跃用户:bitop or destkey 20190101 20190102 ,20190101 和 20190102 的二进制位进行逻辑或运算(有 1 为 1),存入 destkey 中。
bitcount destkey 0 -1 = 2 ,查询从头到最后一个的 1 的数量为 2,这就是这两天活跃用户(这是因为数据少所以写 0 -1,具体可以根据需求缩小范围)。
这个需求的优化跟第一个相比把代表天数的二进制位旋转了 90 度,竖向排列,此时每个 key(也就是每行)代表的是这一天所有登陆的用户,然后对需要的天数的 key 的 value 进行逻辑运算压缩到一条 value 中,最后相同位置的用户的 1 都被压缩为 1,最终的 destkey 的 value 的 1 的数量就是活跃用户的数量。
以上两个场景总结:
映射可以是用户表的 id
第一个二进制矩阵:
天数:????????12345678
用户:allen 01010101
用户: json 01011111
第二个二进制矩阵:
用户: 12345678(粗略表示,用户 1-8)
20190101 日期: 01000001
20190102 日期: 11010101
字符集一般是 ascii,其他的叫扩展字符集(其它字符集不再对 ascii 重新编码),在 ascii 中二进制位第一个必须是 0,根据剩余七个的不同来代表不同字符(一个字节,8 个二进制位时)
redis 在对一个 key 的 value 改变长度后会把长度存在 key 中一份,以后查询长度直接返回,这个地方也是 redis 快的一点
List 类型(插入、弹出元素有序)
key 有 head(头)、tail(尾)两个指针,head 可以迅速定位到 list(链表)的第一个元素,tail 可以到最后一个元素,图中 abc 是双向无环链表(双向指的是来回皆可,无环指的是不能从 a 直接到 c 形成一个环)
lpush key 往 key 的 value 中加入一个或多个元素(因为是 L 开头的命令,所以是从左往右依次防止),,上述图片放入后为 f e d c b a,因为先推进去的 a 然后推 b,以此类推。
rpush key 往 key 的 value 中加入一个或多个元素(因为是 R 开头的命令,所以是从右往左依次防止)
lpop key 弹出 key 中的 value 的左侧第一个元素(弹出来后元 value 就没有这个被弹出的元素了,所以再执行一次弹出的是未被弹出状态的第二个元素也是当前状态的第一个元素)
rpop key 弹出 key 中的 value 的右侧第一个元素(弹出来后元 value 就没有这个被弹出的元素了,所以再执行一次弹出的是未被弹出状态的第二个元素也是当前状态的第一个元素)
由以上得出,同进同出,同向命令,类似 java 中的栈;
反向命令,类似 java 中的队列。 所以其实很多命令不需要在 java 的 api 中进行
lrange key 取出 key 的 value 的 start 索引到 end 索引中的元素(这里的 L 开头不是 left 的意思,就是 List 的 L,无意义)
lindex key 取出 key 的 index 索引处的元素
lset key 将 key 的 index 索引处元素改为 value
由以上得出,对索引操作,类似 java 中的数组。
lrem key <index_count> 将 key 的 value 元素移除(具体移除需看 index_count 的值的正负或 0。
index_count>0 时移除从左【前】往右【后】index_count 个 value 元素;
index_count<0 时移除从右【后】往左【前】index_count 个 value 元素)
linsert key <after/before> 在 key 的 oldValue 的前面或后面插入一个 newValue 元素(如果有两个 oldValue,只能在第一个 oldValue 的对应位置插入)
blpop key 阻塞的弹出 key 的 value(后面的 timeout 指的是阻塞时间。填 0 则为一直阻塞)
如果有三个客户端都连接了 6379 端口的 redis,如果 redis 当前还没有 ooxx,第一个客户端 blpop ooxx 0,取不出 ooxx 的 value,阻塞住,第二个客户端 blpop ooxx 0,同样阻塞住,第三个客户端 lpush ooxx hello,压入一个 hello 到 ooxx,切换回第一个客户端发现已经通畅并取出 hello,而第二个依然还在阻塞,第三个客户端再次 lpush ooxx world ,压入 world 到 ooxx,此时第二个客户端才通畅取出 world。
由以上得出,list 也可以是阻塞的,单播队列,阻塞队列,谁先阻塞的谁先取到先到的值,也体现 redis 在处理数据方面的单线程。
ltrim key 删除 key 的 start 的左面的元素和 end 的右面的元素(也就是 Start 和 End 的两端以外的所有元素)
hash 类型(key-value,这个 hash 指的是 key 库的 value 的类型是 hash,也就是 value 里面有 key-value 格式)
①:hset key 将 key 中设置一个 key:value 的值(这就是 hash 类型) ???????②:mset key 批量设置 key:value
③:hget key 取出 key 的 key1 的 value
④:hmget key 批量取出
⑤:hkeys key 取出 key 的所有 key
⑥:hvalues key 取出 key 的所有 value
⑦:hgetall key 取出 Key 的所有 key,value
评论