ap的Key一定要是可比较的吗?为什么?
切片、map 和函数不可比较
ap的Key一定要是可比较的吗?为什么?
切片、map 和函数不可比较
但如果在函数内部进行扩容操作,会分配新的底层数组,但原始Slice不会引用新的数组
解决方式:1. 返回新的数组2. 调用方保证容量充足3 使用指针
因为使用命名管道的前提,需要在文件系统创建一个类型为 p 的设备文件,那么毫无关系的进程就可以通过这个设备文件进行通信
设备文件在磁盘上的原因 持久化标识:让不相关的进程能找到这个通道
权限控制:利用文件系统的权限机制(rwx)
命名空间:利用文件系统的目录结构管理管道 这个设备文件确实会写入磁盘,但它非常小,只包含:
文件元数据(文件名、权限、时间戳)
文件类型标记(p 表示 pipe)
不包含传输的数据!
后台服务消费消息:后台服务从消息队列中消费秒杀成功的消息,执行以下操作: 1、为用户创建订单记录; 2、使用乐观锁将数据库中的库存数量减少1; 3、通过唯一标识(如用户ID+商品ID+时间戳)防止重复消费。
go语言代码
数据库事务 + 乐观锁:
?
流量吐刺
窗口切换期间
Redis 异步方式的同步数据造成锁丢失无法忍受
?
它追求的是在这个集群里堆更多的机器,来扛住更高的并发,保证一台机器宕机了别的机器还能顶上(高可用)
多机房部署
布隆过滤器:我们可以在写入数据库数据时,使用布隆过滤器做个标记,然后在用户请求到来时,业务线程确认缓存失效后,可以通过查询布隆过滤器快速判断数据是否存在,如果不存在,就不用通过查询数据库来判断数据是否存在。即使发生了缓存穿透,大量请求只会查询 Redis 和布隆过滤器,而不会查询数据库,保证了数据库能正常运行,Redis 自身也是支持布隆过滤器的。 #
?
集群
集群:解决数据量大的问题 主从:解决高可用的问题,避免单点故障 生产环境 = 集群中的每个分片内部都是主从架构,既解决了数据量大,又解决了高可用
了 save 命令
空库和停机维护才使用save,否则使用bgsave进行日常自动备份
Redis 提供了 3 种写回硬盘的策略, 在 Redis.conf 配置文件中的 appendfsync 配置项可以有以下 3 种参数可填:
// 简化的Redis AOF写入流程 void writeAOF(char* command) { // 1. 先写入Redis自己的缓冲区(用户空间) redis_aof_buffer.append(command);
// 2. write()系统调用:从用户空间拷贝到内核空间
// 这个调用很快,只是拷贝数据
write(redis_aof_fd, redis_aof_buffer);
// 数据现在在内核缓冲区(Page Cache)中
// 3. 根据appendfsync策略处理
switch (appendfsync) {
case ALWAYS:
// 调用fsync(),强制内核立即把数据刷到硬盘
// 系统会阻塞直到硬盘确认写入完成
fsync(redis_aof_fd);
break;
case EVERYSEC:
// 不立即刷盘,而是记录一下"需要刷盘"
// 后台线程每秒执行一次fsync()
mark_for_async_fsync();
break;
case NO:
// 什么都不做,完全交给操作系统
// 操作系统自己决定什么时候刷盘(通常缓冲区满了或系统空闲)
break;
}
}
就将网络IO的处理改成多线程的方式了
主线程是“事件分发器”:主线程的核心工作不再是亲自读写数据,而是专注于通过 I/O 多路复用机制(epoll_wait)高效地监听海量连接,快速发现哪些连接已经准备好数据了。它的作用就像一个“事件感知器”和“任务分发器”。
I/O 线程是“数据搬运工”:主线程一旦感知到有数据就绪,就将具体的“搬运”任务(读取、解析、写回)交给多个 I/O 线程并行执行。这充分利用了多核 CPU 的能力来加速网络数据的处理,从而让主线程可以腾出手来,更快地处理源源不断的就绪事件。
就将网络IO的处理改成多线程的方式了
当主线程通过 epoll 发现有多个 socket 已经准备好数据后,它会将这些 读/写任务分发给一个 I/O 线程池,由这些线程并行地去执行数据的读取、解析和写入操作。
所以 Redis 采用单线程(网络 I/O 和执行命令)那么快
network_io_time = 70% # 网络传输 memory_access_time = 28% # 内存操作 cpu_compute_time = 2% # 实际CPU计算
# ZSet用过吗
要实现一个能实时获取近一天内浏览量最高数据的“天维度”排行榜,核心在于解决数据的“时效性”与“持久性”之间的矛盾。
你提到的“数据一旦写入便永久存储”确实是使用Redis ZSET时需要考虑的问题。针对“近一天”这个滑动的时间窗口,业界主要有两种经典的设计模式,你可以根据业务对实时性和精确度的要求来选择。
这个方案的核心思想是将数据按照固定的时间片(如小时)切分存储,查询时再动态合并近24小时的数据。它能够精确地反映“从当前时间往前推24小时”这个动态变化的窗口,非常适合需要高实时性和高精度的场景。
views:article:20250401:14。Key中包含日期和小时,这样既清晰又便于管理。ZINCRBY 命令,将对应小时Key中对应文章的分数(score)加1。同时,可以为这个Key设置一个过期时间(例如48小时或72小时),让Redis自动清理旧数据,避免内存无限增长。ZUNIONSTORE 命令,将这些小时Key的分数进行聚合(SUM),结果存储到一个临时的ZSET中。ZREVRANGE 获取浏览量最高的前N个文章ID。这种方式的优点是数据精确,但由于需要动态聚合多个Key,如果并发量极高,频繁执行 ZUNIONSTORE 可能会对Redis造成一定压力。作为一种优化,可以考虑将排行榜结果缓存几秒到几十秒。
这个方案更加简单直接,以“天”为粒度进行切割。它的逻辑很符合直觉:“今天的排行榜”就用一个单独的Key来存储。
ranking:article:20250401,代表某一天的排行榜。ZINCRBY ranking:article:20250401 {articleId} 1,累加当天的分数。这种方案最大的优点是实现简单,查询性能极高(无需聚合)。但它无法做到真正的“实时滑动窗口”,因为每天的榜单在午夜0点会准时重置。如果你所说的“近一天”严格指“过去24小时”(例如,周一上午10点看的是周日上午10点到现在的数据),那么这个方案就不够精确了。
为了让你更清晰地选择,我将两种方案的核心区别整理如下:
| 维度 | 方案一:按时间片分割 + 动态聚合 | 方案二:单Key + Key过期(日维度轮替) | | :--- | :--- | :--- | | 时间窗口精度 | 高。是真正的、精确到秒的“过去24小时”滑动窗口。 | 低。是固定的“自然日”窗口,每天0点重置。 | | 查询性能 | 中等。需要动态聚合多个Key,可能产生额外开销。 | 极高。直接读取单个Key即可。 | | 实现复杂度 | 较高。需要维护多个Key,并编写聚合逻辑。 | 非常简单。逻辑清晰易懂。 | | 内存效率 | 高。可以对每个小时Key设置短TTL,及时释放内存。 | 一般。单个Key数据量大,但过期后才会释放。 | | 适用场景 | 对实时性要求极高的场景,如实时热点新闻榜、实时商品热销榜。 | 业务意义与自然日对齐的场景,如每日热销商品榜、每日用户积分榜。 |
在实际的大型系统中,这两种模式也常常结合使用。例如,可以按小时粒度存储原始数据(方案一),同时通过定时任务提前聚合好“今日榜单”(方案二的结果)以加速查询,从而兼顾精度与性能。
希望以上的分析和方案能帮助你设计出合适的排行榜系统。如果你对某个方案的代码实现细节,或者如何对聚合查询做进一步优化感兴趣,我们可以继续深入探讨。
Doublewrite Buffer
关于是否影响性能:顺序写入:第一阶段将多个页合并成一次顺序写入,效率很高
批量操作:通常是一次性写入几十个页(默认128个)
收益大于成本:虽然增加了一次写入,但换来了数据安全保障
索引优化详细讲讲
索引优化的cese
前缀索引
用空间换时间
给学生表、课程成绩表,求不存在01课程但存在02课程的学生的成绩
std_id , course_id, score
如果需要插入和更新结合可以使用ON DUPLICATE KEY UPDATE
更新文章每天的阅读量
数据库三大范式是什么?
设计数据库表时需要满足三大范式,从而减少数据冗余、避免数据异常(保证一致性),并确保数据完整性。实际的大数据量、高并发场景下,下严格遵循会导致查询速度慢(join)链接,故常常选择存储一些冗余数据
P
P持有G的上下文,M运行G需通过P分配(同意)
有更多的 P 可以一起工作,加速执行完所有的 G。
G是groutine, P负责调度,P的数量与内核线程数相等?M又是什么
GC
垃圾回收
Runtime 维护所有的 goroutines,并通过 scheduler 来进行调度
runtime通过scheduler来高效调度goruntine, 实现goruntine到线程间映射?管理?,最大程度利用资源
Runtime
用户程序与操作系统交互的中介,内存分配、创建协程,通道间通信都由runtime完成。
Thread 创建和销毀都会有巨大的消耗
用户态到内核态的切换