今天聊聊 redis 的数据类型和集群相关的知识,冲~。
string 类型简单方便,支持空间预分配,也就是每次会多分配点空间,这样 string 如果下次变长的话,就不需要额外的申请空了,当然前提是剩余的空间够用。
List 类型可以实现简单的消息队列,但是注意可能存在消息丢失哦,它并不持 ACK 模式。
Hash 表有点像关系型数据库,但是当 hash 表越来越大的时候,请注意,避免使用 hgetall 之类的语句,因为请求大量的数据会导致redis阻塞,这样后面的兄弟们就得等待了。
set 集合类型可以帮你做一些统计,比如你要统计某天活跃的用户,可以直接把用户ID扔到集合里,集合支持一些骚操作,比如 sdiff 可以获取集合之间的差集,sunion 可以获取集合之间的并集,功能很多,但是一定需要谨慎,因为牛逼的功能是有代价的,这些操作需要耗费一些 CPU 和IO 资源,可能会导致阻塞,因此大集合之间的骚操作要慎用,
zset 可以说是最闪耀的星,可以做排序,因为可以排序,因此应用场景挺多,比如点赞前xx名用户,延时队列等等。
bitmap 位图的好处就是在于节省空间,特别在做一些统计类的方面,比如要统计某一天有多少个用户签到了并且某个用户是否签到了,如果不用bitmap的话,你可能会想到用set。
SADD day 1234//签到就添加到集合
SISMEMBER day 1234//判断1234是否签到
SCARD day //有多少个签到的
set 在功能上可以满足,但是相比bitmap的话,set要更耗费存储空间,set的底层主要是由整数集合或者 hashtable 组成,整数集合只有在数据量非常小的情况下才会使用,一般是小于512个元素,同时元素必须都是整数,对于set来说,整数集合的数据更加紧凑,他们在内存是上连续的,查询的话只能是二分查找了,时间复杂度是O(logN),而 hashtable 就不同了,这里的 hashtable 和 redis 的5大数据类型中的hash是一样的,只不过没有 value 而已,value 指向个 null,同时也不存在冲突,因为这里是集合,但是需要考虑 rehash 相关问题。ok,扯的有点远,我们说的用户签到问题,在用户非常多的情况下,set 的话肯定会用到 hashtable,hashtable 的话,其实每个元素都是个 dictEntry 结构体
typedef struct dictEntry {
// 键
void *key;
// 值
union {
void *val;
uint64_t u64;
int64_t s64;
} v;
// 指向下个哈希表节点,形成链表
struct dictEntry *next;
} dictEntry;
从这个结构体可以看到什么呢?首先虽然值 union(没有 value)和 next(没有冲突)是空的,但是结构体本身需要空间,还需要加上个 key,这个占用空间是实打实的,而如果用 bitmap 的话,一个bit位就可以代表一个数字,很省空间,我们来看看 bitmap 的方式如何设置和统计。
SETBIT day 1234 1//签到
GETBIT day 1234//判断1234是否签到
BITCOUNT day//有多少个签到的
bf 这是 redis4.0 之后支持的布隆过滤器 RedisBloom,但是需要单独加载对应的 module,当然我们也可以基于上述的 bitmap 来实现自己的布隆过滤器,不过既然 redis 已经支持了,通过 RedisBloom 可以减少我们的开发时间,布隆过滤器是干嘛的,我这里就不赘述了,直接来看看 RedisBloom 相关的用法吧。
# 可以通过docker的方式快速拉取镜像来玩耍
docker run -p 6379:6379 --name redis-redisbloom redislabs/rebloom:latest
docker exec -it redis-redisbloom bash
redis-cli
# 相关操作
bf.reserve sign 0.001 10000
bf.add sign 99 //99这个用户加入
bf.add exists 99//判断99这个用户是否存在
因为布隆过滤器是存在误判的,所有 bf 支持自定义误判率,0.001就代表误判率,10000 代表布隆过滤器可以存储的元素个数,当实际存储的元素个数超过这个值的时候,误判率会提高。
HyperLogLog 可以用于统计,它的优点就是占用的存储空间极小,只需要 12KB 的内存就可以统计 2^64 个元素,那它主要统计什么呢?其实主要就是基数统计,比如像 UV 这种,从功能上来说 UV 可以用 set 或者 hash 来存储,但是缺点就是耗费存储,容易使之变成大 key,如果想要节省空间,bitmap 也可以,12KB 空间的 bitmap 只能统计 12*1024*8=98304个元素,而 HyperLogLog 却可以统计 2^64 个元素,但是这么牛逼的技术其实是有误差的,HyperLogLog 是基于概率来统计的,标准误算率是 0.81%,在统计海量数据并且对精度要求不那么高的场景下,HyperLogLog 在节省空间这块还是很优秀的。
PFADD uv 1 2 3 //1 2 3是活跃用户
PFCOUNT uv //统计
GEO 是可以应用在地理位置的业务上,比如微信附近的人或者附近的车辆等等,先来看一下如果没有GEO 这种数据结构,你如何知道你附近的人?首先得上报自己的地理位置信息吧,比如经度 116.397128,纬度 39.916527,此时可以用 string、hash 数据类型存储,但是如果要查找你附近的人,string 和 hash 这种就无能为例了,你不可能每次都要遍历全部的数据来判断,这样太耗时了,当然你也不可能通过 zset 这种数据结构来把经纬度信息当成权重,但是如果我们能把经纬度信息通过某种方式转换成一个数字,然后当成权重好像也可以,这时我们只需通过zrangebyscore key v1 v2
也可以找到附近的人。真的需要这么麻烦吗?于是 GEO 出现了,GEO 转换经纬度为数字的方法是“二分区间,区间编码”,这是什么意思呢?以经度为例,它的范围是[-180,180],如果要采用3位编码值,那么就是需要二分3次,二分后落在左边的用0表示,右边的用1表示,以经度是121.48941 来说,第一次是在[0,180]这个区间,因此记1,第二次是在[90,180],因此再记1,第三次是在[90,135],因此记0。纬度也是同样的逻辑,假设此时对应的纬度编码后是010,最后把经纬度合并在一起,需要注意的是经度的每个值在偶数位,纬度的每个值在奇数位。
1 1 0 //经度
0 1 0 //纬度
------------
101100 //经纬度对应的数值
原理是这样,我们再来看看 redis 如何使用 GEO:
GEOADD location 112.123456 41.112345 99 //上报用户99的地理位置信息
GEORADIUS location 112.123456 41.112345 1 km ASC COUNT 10 //获取附近1KM的人
生产环境用单实例 redis 的应该比较少,单实例的风险在于:
于是我们首先想到的就是经典的主从模式,而且往往是一主多从,这是因为大部分应用都是读多写少的情况,我们的主负责更新,从负责提供读,就算我们的主宕机了,我们也可以选择一个从来充当主,这样整个应用依然可以提供服务。
当一个 redis 实例首次成为某个主的从的时候,这时主得把数据发给它,也就是 rdb 文件,这个过程 master 是要 fork 一个子进程来处理的,这个子进程会执行 bgsave 把当前的数据重新保存一下,然后准备发给新来的从,bgsave 的本质是读取当前内存中的数据然后保存到 rdb 文件中,这个过程涉及大量的 IO,如果直接在主进程中来处理的话,大概率会阻塞正常的请求,因此使用个子进程是个明智的选择。
那 fork 的子进程在 bgsave 过程中如果有新的变更请求会怎么办?
严格来说子进程出来的一瞬间,要保存的数据应该就是当时那个点的快照数据,所以是直接把当时的内存再复制一份吗?不复制的话,如果这期间又有变更改怎么办?其实这要说到写实复制(COW)机制,首先从表象上来看内存是一整块空间,其实这不太好维护,因此操作系统会把内存分成一小块一小块的,也就是内存分页管理,一页的大小一般是4K、8K或者16K等等,redis 的数据都是分布在这些页面上的,出于效率问题,fork 出来的子进程是和主进程是共享同一块的内存的,并不会复制内存,如果这期间主进程有数据变更,那么为了区分,这时最快捷的做法就是把对应的数据页重新复制一下,然后主的变更就在这个新的数据页上修改,并不会修改来的数据页,这样就保证了子进程处理的还是当时的快照。
以上说的变更是从快照的角度来考虑的,如果从数据的一致性来说,当快照的 rdb 被从库应用之后,这期间的变更该如何同步给从库?答案是缓冲区,这个缓冲区叫做 replication buffer
,主库在收到需要同步的命令之后,会把期间的变更都先保存在这个缓冲区中,这样在把 rdb 发给从库之后,紧接着会再把 replication buffer 的数据也发给从库,最终主从就保持了一致。
replication buffer
不是万能的补给剂
我们来看看 replication buffer 持续写入的时间有多长。
replication buffer 既然是个 buffer,那么它的大小就是有限的,如果说上面3个步骤中,只要有一个耗时长,就会导致 replication buffer 快速增长(前提是有正常的写入),当 replication buffer 超过了限制之后就会导致主库和从库之间的连接断开,断开之后如果从库再次连接上来就会导致重新开始复制,然后重复同样的漫长的复制步骤,因此这个 replication buffer 的大小还是很关键的,一般需要根据写入的速度、每秒写入的量和网络传输的速度等因素来综合判断。
从库网络不好和主库断了该怎么办?
正常来说,只要主从之间的连接建立好了,后面主库的变更可以直接发给从库,让从库直接回放,但是我们并不能保证网络环境是百分百的通畅的,因此也要考虑从库和主库之间的断联问题。
应该是在 redis2.8 以前,只要从库断联,哪怕只有很短的时间,后面从库再次连接上来的时候,主库也会直接无脑的进行全量同步。在 2.8 版本及以后,开始支持增量复制了,增量复制的原理就是得有个缓冲区来保存变更的记录,这里这个缓冲区叫做repl_backlog_buffer
,这个缓冲区从逻辑上来说是个环形缓冲区,写满了就会从头开始覆盖,所以也有大小限制。在从库重新连接上来的时候,从库会告诉主库:“我当前已经复制到了xx位置”,主库收到从库的消息之后开始查看xx位置的数据是否还在 repl_backlog_buffer 中,如果在的话,直接把xx后面的数据发给从库即可,如果不在的话,那无能为力了,只能再次进行全量同步。
在主从模式下,如果主库挂了,我们可以把一个从库升级成主库,但是这个过程是手动的,靠人力来操作,不能使损失降到最低,还是需要一套自动管理和选举的机制,这就是哨兵,哨兵它本身也是个服务,只不过它不处理数据的读写而已,它只负责管理所有的 redis 实例,哨兵每隔一段时间会和各个 redis 通信(ping 操作),每个 redis 实例只要在规定的时间内及时回复,就可以表明自己的立场。当然哨兵本身也可能存在宕机或者网络不通的情况,因此一般哨兵也会搭建个哨兵集群,这个集群的个数最好是奇数,比如3个或者5这个这种,奇数的目的主要就是为了选举(少数服从多数)。
当某个哨兵在发起 ping 后没有及时收到 pong,那么就会把这个 redis 实例标记下线,此时它还是不是真正的下线,这时其他的哨兵也会判定当前这个哨兵是不是真正的下线,当大多数哨兵都认定这个 redis 是下线状态,那么就会把它从集群中踢出去,如果下线的是从库,那么还好,直接踢出去就ok,如果是主库还要触发选举,选举也不是盲目选举,肯定是要选出最合适的那个从来充当新的主库。这个最合适充当主库的库,一般会按照以下优先级来确定:
主从模式解决了单点故障问题,同时读写分离技术使得应用支撑能力更强,哨兵模式可以自动监管集群,实现自动选主,自动剔除故障节点的能力。
正常来说只要读的压力越来越大,我们可以添加从库来缓解,那如果主库压力很大怎么办?这就得提到接下来要说的分片技术了,我们只需要把主库切成几片,部署到不同的机器上即可。这个分片就是 redis 中的槽概念了,当分片的时候,redis 会默认分成 0~16383 也就是一共 16384 个槽,然后把这些槽平均分到每个分片节点上就可以起到负载均衡的作用了。每个 key 具体该分到哪个槽中,主要是先 CRC16 得到一个 16bit 的数字,然后这个数字再对 16384 取模即可:
crc16(key)%16384
然后客户端会缓存槽信息,这样每当一个 key 到来时,只要通过计算就知道该发给哪个实例来处理来了。但是客户端缓存的槽信息并不是一成不变的,比如在增加实例的时候,这时候会导致重新分片,那么原来客户端缓存的信息就会不准确,一般这时候会发生两个常见的错误,严格来说也不是错误,更像一种信息,一个叫做MOVED
,一个叫做ASK
。moved的意思就说,原来是实例A负责的数据,现在被迁移到了实例B,MOVED 代表的是迁移完成的,但是 ASK 代表的是正在迁移过程中,比如原来是实例A负责的部分数据,现在被迁移到了实例B,剩下的还在等待迁移中,当数据迁移完毕之后 ASK 就会变成 MOVED,然后客户端收到 MOVED 信息之后就会再次更新下本地缓存,这样下次就不会出现这两个错误了。
这是我 2022 年的第一篇文章,祝大家新年快乐,同时还是不要脸的求各位的「点赞」和「在看」,我们下期见。
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/5QSLUtbyCI3tiB0papRhIA
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。