文章目录
Redis
说一下 Redis 和 Memcached 的区别和共同点?
🙋♂答:
相同点
- 都是基于内存的数据库,一般都用来当做缓存使用。
- 都有过期淘汰策略。
- 两者的性能都非常高。
区别
- Redis的数据结构更加丰富,Memcached只支持key-value数据类型;
- Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而 Memcached 没有持久化功能,数据全部存在内存之中,Memcached 重启或者挂掉后,数据就没了;
- Redis 支持发布订阅模型、Lua 脚本、事务等功能,而 Memcached 不支持;
- Redis 可以通过 MULTI,EXEC,DISCARD 和 WATCH 等命令来实现事务功能,Memcached不支持事务。
总之,Memcached是解决简单缓存问题的可靠选择。然而,一般来说,「Redis通过提供更丰富的功能和各种各样的特性而优于Memcached,这些特性对于解决复杂的场景更有优势」。
为什么用Redis作为MySQL的缓存?
-
主要是因为Redis 具备「高性能」和「高并发」两种特性。
-
高性能:假如用户第一次访问 MySQL 中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户访问的数据缓存在 Redis 中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了,操作 Redis 缓存就是直接操作内存,所以速度相当快。
-
高并发:单台设备的 Redis 的 QPS(Query Per Second,每秒钟处理完请求的次数) 是 MySQL 的 10 倍,Redis 单机的 QPS 能轻松破 10w,而 MySQL 单机的 QPS 很难破 1w。直接访问 Redis 能够承受的请求是远远大于直接访问 MySQL 的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。
-
项目中哪里用到了Redis?
- 使用Redis作为缓存数据库来保存数据,比如点赞的数据、关注的数据。
- 验证码、登录凭证、用户信息都缓存在Redis中。
- 统计网站DAU、UV的数据也保存到了Redis中。
为什么用Redis保存点赞/关注的数据?使用了哪些数据类型?用到了哪些命令?
因为点赞、关注是一个很高频的操作,如果直接在数据库中使用Count,效率很低,所以可以使用Redis来提升性能,从缓存中查询数据,会比直接在硬盘中查询数据要快。
使用的数据类型:
- 给帖子、评论点赞使用Set来存储。key代表某个帖子或者评论,value表示点赞的用户Id。
- 使用Set进行存储,可以防止同一个用户多次点赞的情况,因为Set可以去重。
- 用户收到的赞使用String来存储。key代表具体用户,value就是收到的赞数。
- 比如用户的帖子获得一条点赞,那么就在给帖子增加点赞数的同时,给用户收到的赞 + 1 即可,所以就使用String来进行存储。
- 关注、粉丝数据使用Zset来存储,key代表用户的关注 / 粉丝 ,value代表关注 / 粉丝 userId。关联的score 来保存关注时间,这样我们可以按照关注时间进行排序。
- 使用Zset可以防止重复关注的问题,并且我们可以根据关注事件进行排序。
使用的命令:
- Set:add、remove、ismember、size
- String:decrement、increment
- Zset:add、remove、ZCard【统计元素个数】、ZREVRANGE【时间从大到小排序】,ZRANGE【从小到大排序】
为什么用Redis保存验证码、登录凭证、用户信息?
用Redis保存验证码:
- 验证码需要频繁的刷新和访问,对性能的要求比较高
- 并且验证码不需要永久保存,需要设计过期时间来节约空间。
- 之前验证码都是保存在session中,在分布式部署中session会出现共享问题。将验证码保存到Redis中避免分布式session不可用的问题。
用Redis保存登录凭证、用户信息:
- 之前将登录凭证保存到MySQL中,导致每次都需要去login_ticket表中查询登录凭证,由于每次访问页面都需要在拦截器中对登录凭证进行检查,访问频率很高,所以用Redis进行存储。Redis是内存型数据库,访问很快。
- 需要在拦截器中根据登录凭证去查询用户信息,访问频率很高,所以用Redis进行存储。
为什么用Redis保存UV/DAU?使用了哪些数据类型?用到了哪些命令?
用户每访问一次网页,就需要向数据库中插入一条数据,如果日活很高,会消耗数据库资源。
并且如果要进行月度、年度的统计会使用count、group by这样效率很低,所以使用Redis来保存UV / DAU。
- UV:独立访客,通过IP进行排重计算。HyperLogLog就可以满足去重要求。并且每个 HyperLogLog 键只需要花费 12 KB 内存,和元素越多就越耗费内存的 Set 和 Hash 类型相比,HyperLogLog 就非常节省空间。
- ADD方法:传入VALUE = IP进行统计。
- UNION方法:合并HyperLogLog的集合到新的HyperLogLog。
- DAU:日活跃用户,通过用户ID进行去重。使用Bitmap一连串的二进制数组,可以统计精确结果,并且占用空间很小。
- SETBIT方法:通过一个偏移值 offset 对 bit 数组的 offset 位置的 bit 位进行读写操作。
SETBIT login_status 101 1
表示ID = 101 的用户 已登录。 - OR运算:统计一周、月度的活跃用户。
- SETBIT方法:通过一个偏移值 offset 对 bit 数组的 offset 位置的 bit 位进行读写操作。
Bitmap 本身是用 String 类型作为底层数据结构实现的一种统计二值状态的数据类型。String 类型是会保存为二进制的字节数组,所以,Redis 就把字节数组的每个 bit 位利用起来,用来表示一个元素的二值状态。
用过Redis的事务吗?怎么使用的?
Redis的事务提供了三个操作:
- MULTI:事务开始执行的命令。
- 在MULTI和EXEC之间发送的所有命令都会被记录下来,并在执行EXEC命令时按照顺序执行。这些命令并不会立即执行,而是被放入一个队列中等待执行。
- EXEC:执行队列中的命令。
Redis事务的主要作用就是串联多个命令防止别的命令插队。项目中点赞操作就使用了事务。当用户执行点赞操作,我们需要在Set中增加一条数据,并且给被点赞的用户获赞数 + 1。这是两个操作,防止别的命令插队,所以使用事务。
注意:Redis不提供事务的回滚。所以这里实际的作用不大。
Redis 有哪些数据类型?SDS 了解么?
String、List、Hash、Set、Zet都是比较常用的数据类型。其中 String 数据类型的底层数据结构是 SDS(simple dynamic string,SDS)。
SDS这个数据结构中存在几个字段:
- len,记录了字符串的实际长度。这样获取字符串长度的时候,只需要返回这个成员变量值就行,时间复杂度只需要 O(1)。
- alloc,分配给字符数组的空间长度。这样在修改字符串的时候,可以通过
alloc - len
计算出剩余的空间大小,并且根据需求自动修改空间大小,也不会出现前面所说的缓冲区溢出的问题。 - buf[],字符数组,用来保存实际数据。不仅可以保存字符串,也可以保存二进制数据。不会出现二进制安全的问题。
- flags,用来表示不同类型的 SDS。
对比C语言,主要有以下优势:
- O(1)复杂度获取字符串长度
- C 语言的字符串长度获取 strlen 函数,需要通过遍历的方式来统计字符串长度,时间复杂度是 O(N)。
- 而 Redis 的 SDS 结构因为加入了 len 成员变量,那么获取字符串长度的时候,直接返回这个成员变量的值就行,所以复杂度只有 O(1)。
- 二进制安全
- 因此, SDS 的 API 都是以处理二进制的方式来处理 SDS 存放在 buf[] 里的数据,程序不会对其中的数据做任何限制,数据写入的时候时什么样的,它被读取时就是什么样的。
通过使用二进制安全的 SDS,而不是 C 字符串,使得 Redis 不仅可以保存文本数据,也可以保存任意格式的二进制数据。
- 因此, SDS 的 API 都是以处理二进制的方式来处理 SDS 存放在 buf[] 里的数据,程序不会对其中的数据做任何限制,数据写入的时候时什么样的,它被读取时就是什么样的。
- 不会发生缓冲区溢出
- Redis 的 SDS 结构里引入了 alloc 和 len 成员变量,这样 SDS API 通过
alloc - len
计算,可以算出剩余可用的空间大小,这样在对字符串做修改操作的时候,就可以由程序内部判断缓冲区大小是否足够用。 - 而且,当判断出缓冲区大小不够用时,Redis 会自动将扩大 SDS 的空间大小,以满足修改所需的大小。
- Redis 的 SDS 结构里引入了 alloc 和 len 成员变量,这样 SDS API 通过
- 节省内存空间
- SDS 设计了不同类型的结构体,是为了能灵活保存不同大小的字符串,从而有效节省内存空间。比如,在保存小字符串时,结构头占用空间也比较少。
- 使用了专门的编译优化来节省内存空间,即在 struct 声明了
__attribute__ ((packed))
,它的作用是:告诉编译器取消结构体在编译过程中的优化对齐,按照实际占用字节数进行对齐。
完。