你确定你真的了解redis系列文章第二篇

小X上一次面试勉强过关了,今天来进行了第二轮的面试,于是便有了如下对话。

面试官:我现在有个场景需求,比如我们的APP想做个用户签到功能,我想统计用户今年的签到次数,但是我有个特殊要求,你要使用redis来实现这个功能,你有什么想法呢?

面试者:这个简单啊,我可以使用简单的string来实现,只是在设计key的时候,用usercode+日期的格式,value随便都行,如果用户进行了点赞的动作,那么就往redis中缓存一条记录就行,比如set zhangsan20191225 1,然后当我按月统计或者按年统计用户签到次数的时候,就按照规则组装好key,然后依次去redis中获取对应的value,然后对value做一些统计就可以了。

面试官:嗯,你这个确实也是个好办法,但是你这个还是有点问题,我一个用户比如签到一年,那么同一个用户你就需要给他建365个key-value,内存占用太严重了,如果我们系统的用户上亿了,机器内存能存的下吗?还有没有什么其它能够省内存的方法呢?

面试者:额?这个嘛。。。

接下来就要引出我们今天的主角了,redis的位图,位图相信很多人都没有听过,这又是redis支持的新的数据结构?非也。其实位图就是我们上期文章介绍的string,通过上节的介绍,相信大家都知道了,string的底层数据结构存储其实是一个字节数组,既然是字节数组,那么我们可以不可以通过操作字节的位(bit)的方式去操作呢?答案当然是肯定的,redis就为我们提供了这样的命令,对应的就是位图。

回到上面的面试题,其实我们可以使用位图来进行处理,其实用户每次签到只占用一个位(bit),这样即使用户签到365天也只需占用365个位(bit),算下来也就46个字符,一个普通的字符串完全就可以容纳下来,这样就能节省很多内存了。

上面我们提到位图不是特殊的数据结构,就是redis的普通string,所以我们可以使用普通的get/set命令直接获取或者设置位图的内容,也可以使用位图操作的命令getbit/setbit命令将byte数组看成位数组进行操作,redis的位数组是自动扩容的,所以即使我们设置的某个偏移位置超出了现有内容的范围,redis也会自动将位数组进行零扩充。

接下来我们就通过位图操作来设置字符串"hello",不是使用set命令,首先我们需要知道"hello"字符串里面的每个字符的ascii码数的二进制数。

h:01101000 高位->低位

e:01100101

l:01101100

o:01101111

在设置之前,首先我们要明确一点,我们普通字符编码对应数字的二进制数的高位在左,低位在右,而位数组是从最左边开始的,所以低位在左,高位在右。例如下图上面一行是我们字符的二进制数的高低位位置,而下面一行是位数组的高低位的顺序。所以位数组存储的时候是从左到右,然后在每个字节或者字符内还是按照从右到左的顺序存储字符对应编码的二进制值。

file

接下来我们就先通过位图的命令将'h'和'e'这两个字符设置到位数组中,也就是位数组的头8位和第8位到第16位,但是在设置的时候,我们只需要设置对应位置为1的位就行了,为0的位置,redis会自动帮助我们填充0,所以h需要设置1,2,4位置,而e需要设置9,10,13,15位置,其他的依次类推设置就行。

file

上面我们这种方式是"按位存值按字符串取值"的方式,其实还有"按照字符串存值按位取值"或者"按位存值按位取值"等方式,按位存值就是使用redis的setbit命令依次进行位设置,而按字符串存值就是使用set命令直接设置一个字符串来填充整个位数组然后将整个旧值全部覆盖掉。

按位存值按位取值:

file

按照字符串设值按位取值:

file

其中如果对应的字节是不可打印的字符,那么redis就会返回其十六进制的数值

file

其实通过上面的讲解,我们已经完成我们想要的统计功能了,但是有个缺陷就是我们必须要把所有的值获取到程序中,然后用程序去统计,要是redis能够给我提供一些统计的命令,我们只需要使用这些命令就可以获取到我们想要的结果那该多爽,那么恭喜你,这个懒可以偷了,redis为我们提供了两个指令供我们使用。

bitcount:位图统计指令,统计指定范围内1出现的次数。

bitpos:用来查找指定范围内出现的第一个0或者1。

那么回到最开始的面试题,我们可以通过bitcount来统计用户当年的签到次数,使用bitpos命令来查找用户第一次签到的时间。并且我们在使用上面两个指令的时候还可以指定一个范围[start,end],用来统计某个范围内用户的签到次数,或者是从哪一天开始签到。但是有个缺陷是,start和end参数是字节索引,所以我们指定的范围必须是8的倍数,而不能任意的指定。所以我们想统计某个月,这个可能就不能实现了,因为那个月的数据可能分散在三个字节内,我们就必须将三个字节的数据全部取出来,然后取子串,在内存进行统计了。

file

最后上面我们的setbit和getbit都是只能操作单个值,如果我们想一次操作多个值,那么我们只能使用管道了,好在redis3以后给我们提供了一个批量操作的命令bitfield,这个指令有三个子指令,分别是set/get/incrby,可以对指定的位片段进行读写,但是一次只能连续处理64个位,如果超过64需要使用多个子指令,并且bitfield可以一次性使用多个不同的子指令。

一次性使用多个子指令

file

接下来我们使用set子命令将第一个字母h替换成a,其中a的ascii码位97,命令将会返回旧值。

file

最后我们来看看incrby子命令,incrby子命令用来对指定范围内的位进行自增操作,但是因为是自增操作,有可能就会超出二进制能够表示的范围,例如增加整数,就会向上溢出,而增加的是负数,就会出现向下溢出,redis默认采取的策略是折返,如果出现溢出,就将符号位直接丢弃,例如无符号的255自增1以后就溢出了,就全部变成了0,丢弃最高位的符号位之后,就是0,再比如8位无符号整数127自增1以后,就溢出了,我们丢弃最高的符号位之后,就是-128。redis除了折返wrap之外还为我们提供了饱和截断sat以及fail报错不执行,我们可以通过overflow子指令来手动选择某种方式。

默认为折返wrap的例子:

file

手动设置为饱和截断sat的例子:

file

最后再看一下手动设置为报错fail不执行的例子:

file
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • 转载:可能是目前最详细的Redis内存模型及应用解读 Redis是目前最火爆的内存数据库之一,通过在内存中读写数据...
    jwnba24阅读 620评论 0 4
  • 前言 Redis是目前最火爆的内存数据库之一,通过在内存中读写数据,大大提高了读写速度,可以说Redis是实现网站...
    小陈阿飞阅读 802评论 0 1
  • Redis基础入门篇可参考 Redis基础入门篇[https://www.jianshu.com/p/58ece9...
    AC编程阅读 680评论 0 2
  • 不知道什么时候起,睡眠与效率就结了仇,想要追求睡眠的人往往效率低,而要追求效率就不能多睡,于是为了追求效率,人类开...
    尚蒙寝具阅读 424评论 0 0
  • 2012年2月10日认识了我的先生,那是场阴差阳错的相识,我们都赴了一场并不想约的饭局,于是后来就有了我们的故事。...
    旺仔牛奶喵阅读 965评论 0 3