Redis-大厂程序员是怎么用的 | 青训营笔记

90 阅读4分钟

这是我参与「第五届青训营 」笔记创作活动的第13天。

一、本堂课重点内容:

理解为什么需要Redis,Redis的基本原理;然后通过案例,了解Redis的实际应用场景,并介绍Redis常用数据结构。最后介绍在字节跳动使用Redis有哪些注意事项。

二、详细知识点介绍:

(1)Redis是什么

数据从表单,演进出了分库分表;MySQL从单机演进出了集群,数据量增大,读写数据压力的不断增加。
数据分冷热,热数据:经常被访问到的数据。将热数据存储到内存中。
Redis基本工作原理:数据从内存中读写,数据保存到硬盘上防止重启数据丢失。增量数据保存到AOF文件,全量数据RDB文件。单线程处理所有操作命令。

(2)Redis应用案例

1、连续签到:用户每日有一次签到的机会,如果断签,连续签到计数将归零。连续签到的定义:每天必须在23:59:59前签到。
String数据结构:可以存储字符串、数字、二进制数据。通常和expire配合使用。用于存储计数,session。
2、消息通知:用list作为消息队列。例如当文章更新时,将更新后的文章推送到ES,用户就能搜索到最新的文章数据。List数据结构Quicklist,由一个双向链表和listpack实现。一个节点会存多个数据,放到一个listpack中。
3、计数:一个用户有多项计数需求,可通过hash结构存储。分为rehash和渐进式rehash。
4、排行榜:积分变换时,排名要实时变更。
5、限流:要求1秒内放行的请求为N,超过N则禁止访问。
6、分布式锁:并发场景,要求一次只能有一个协程执行。执行完成后,其他等待中的协程才能执行。可以使用redis的setnx实现,利用了两个特性:redis是单线程执行命令,setnx只有在未设置过才能执行成功。

(3)Redis使用注意事项

1、大Key、热Key
string类型的value的字节数大于10kB即为大Key,复杂数据结构类型的元素个数大于5000个或总value字节数大于10MB即为大key。
大key的危害有读取成本高,容易导致慢查询,主从复制异常,服务阻塞无法正常响应请求。请求redis超时报错。
消除大key的方法:差分,将大key拆分为小key。压缩,将value压缩后写入redis,读取时解压后再使用。冷热分离。
2、慢查询场景
批量操作一次性传入过多的key/value。
zset大部分命令都是O(log(n)),当大小超过5k以上时,简单的zadd、zrem也可能导致慢查询。
操作的单个value过大,超过10kB。
对大key的delete、expire操作也可能导致慢查询。
3、缓存穿透、缓存雪崩
缓存穿透:热点数据查询绕过缓存,直接查询数据库。
缓存雪崩:大量缓存同时过期。

三、实践练习例子:

(1)获取用户连续签到天数

func getUserCheckInDays(ctx context.Context, userID int64) (int64, error) {

	key := fmt.Sprintf(continuesCheckKey, userID)
	days, err := RedisClient.Get(ctx, key).Result()
	if err != nil {
		return 0, err
	}
	if daysInt, err := strconv.ParseInt(days, 10, 64); err != nil {
		panic(err)
	} else {
		return daysInt, nil
	}
}

(2)获取pipeline中的数据

func GetUserCounter(ctx context.Context, userID int64) {
	pipe := RedisClient.Pipeline()
	GetUserCounterKey(userID)
	pipe.HGetAll(ctx, GetUserCounterKey(userID))
	cmders, err := pipe.Exec(ctx)
	if err != nil {
		panic(err)
	}
	for _, cmder := range cmders {
		counterMap, err := cmder.(*redis.MapStringStringCmd).Result()
		if err != nil {
			panic(err)
		}
		for field, value := range counterMap {
			fmt.Printf("%s: %s\n", field, value)
		}
	}
}

四、课后个人总结:

Hash特别适合存储对象,相对于将对象的每个字段存成单个string 类型。一个对象存储在Hash类型中会占用更少的内存,并且可以更方便的存取整个对象。这种方式的缺点是,增加了序列化/反序列化的开销,并且在需要修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护,引入CAS等复杂问题。
针对存储型服务器,比如mysql、redis,专门存放数据。虽然集群后,存储型的服务器有很多个,但是每个上面的内容不一样,如果坏了,数据就会丢失。为了保证这些数据的高可用,每个存储型的服务器主机需要做备份,这些备份称为从机。有了主从后,可以实现读写分离(主分片写,副本读)等扩展功能。但读写分离并不是主从的目的,主从只是为了实现数据的高可用性。

五、引用参考:

(1)Redis中的hash存储

blog.csdn.net/qq_34412985…

(2)节点,集群和主从

blog.csdn.net/weixin_4575…