当前位置: 首页 > news >正文

5.26 面经整理 360共有云 golang

select … for update

参考:https://www.cnblogs.com/goloving/p/13590955.html
select for update是一种常用的加锁机制,它可以在查询数据的同时对所选的数据行进行锁定,避免其他事务对这些数据行进行修改。
比如涉及到金钱、库存等。一般这些操作都是很长一串并且是开启事务的。如果库存刚开始读的时候是1,而立马另一个进程进行了update将库存更新为0了,而事务还没有结束,会将错的数据一直执行下去,就会有问题。所以需要for upate 进行数据加锁防止高并发时候数据出错
  排他锁的申请前提是需要:没有其他线程对该结果集中的任何行数据使用排他锁或共享锁,否则申请会阻塞
  for update仅适用于InnoDB,且必须在事务块(BEGIN/COMMIT)中才能生效。在进行事务操作时,通过“for update”语句,MySQL会对查询结果集中每行数据都添加排他锁,其他线程对该记录的更新与删除操作都会阻塞。
  排他锁包含行锁、表锁。

1、InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。无主键或主键不明确或无索引,表锁;明确指定主键或索引,且有数据,行锁;无数据,则无锁。

2、由于MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。

3、当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,另外,不论是使用主键索引、唯一索引或普通索引,InnoDB都会使用行锁来对数据加锁。

4、即便在条件中使用了索引字段,但是否使用索引来检索数据是由MySQL通过判断不同执行计划的代价来决定的,如果MySQL认为全表扫描效率更高,比如对一些很小的表,它就不会使用索引,这种情况下InnoDB将使用表锁,而不是行锁。因此,在分析锁冲突时,别忘了检查SQL的执行计划,以确认是否真正使用了索引。

5、检索值的数据类型与索引字段不同,虽然MySQL能够进行数据类型转换,但却不会使用索引,从而导致InnoDB使用表锁。通过用explain检查两条SQL的执行计划,我们可以清楚地看到了这一点。

例子

在这里插入图片描述
锁user_id 索引

BEGIN;
SELECT * FROM user_point_logs WHERE user_id = 1 ORDER BY id DESC FOR UPDATE;
INSERT INTO user_point_logs (user_id, change_type, scene, change_points, balance) VALUES (1, 1, "", 0, 0);
COMMIT;

并发时

BEGIN
> OK
> 查询时间: 0.02sSELECT * FROM user_point_logs WHERE user_id = 1 ORDER BY id DESC FOR UPDATE
> Affected rows: 0
> 查询时间: 0.024sSELECT SLEEP(15)
> OK
> 查询时间: 15.023sINSERT INTO user_point_logs (user_id, change_type, scene, change_points, balance) VALUES (1, 1, "", 0, 0)
> 1213 - Deadlock found when trying to get lock; try restarting transaction
> 查询时间: 0.022s

出现相互死锁

BEGIN
> OK
> 查询时间: 0.024sSELECT * FROM user_point_logs WHERE user_id = 1 ORDER BY id DESC FOR UPDATE
> Affected rows: 0
> 查询时间: 13.025sINSERT INTO user_point_logs (user_id, change_type, scene, change_points, balance) VALUES (1, 1, "", 0, 0)
> Affected rows: 1
> 查询时间: 0.026sCOMMIT
> OK
> 查询时间: 0.031s
BEGIN;
SELECT * FROM user_point_logs WHERE user_id = 1 ORDER BY id DESC LIMIT 1 FOR UPDATE;
SELECT SLEEP(15);
INSERT INTO user_point_logs (user_id, change_type, scene, change_points, balance) VALUES (1, 1, "", 0, 0);
COMMIT;
BEGIN
> OK
> 查询时间: 0.021sSELECT * FROM user_point_logs WHERE user_id = 1 ORDER BY id DESC LIMIT 1 FOR UPDATE
> Affected rows: 0
> 查询时间: 0.023sSELECT SLEEP(15)
> OK
> 查询时间: 15.021sINSERT INTO user_point_logs (user_id, change_type, scene, change_points, balance) VALUES (1, 1, "", 0, 0)
> Affected rows: 1
> 查询时间: 0.022sCOMMIT
> OK
> 查询时间: 0.095s

在这里插入图片描述
后一个事务没有查询结果,可能由于事务的可重复读的隔离级别? 不太懂,欢迎佬评论区指点~

为什么线程消耗比协程大,具体体现在哪些方面

目前主流语言基本上都选择了多线程作为并发设施,与线程相关的概念就是抢占式多任务(Preemptive multitasking),而与协程相关的是协作式多任务。
不管是进程还是线程,每次阻塞、切换都需要陷入系统调用(system call),先让CPU跑操作系统的调度程序,然后再由调度程序决定该跑哪一个进程(线程)。

  • 内存占用 1-8MB 2KB
  • 上下文切换
  • 同步与锁:互斥 channel
  • 调度机制 :抢占式 GMP

生产中哪些服务用的 进程、线程,为什么要这么做,有什么好处

在这里插入图片描述
网络爬虫、高性能计算等等

可重复读能解决幻读吗,为什么

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

出现幻读

在这里插入图片描述
在这里插入图片描述

http1.0 - http3.0 改进,优缺点

  • http1.0:短连接
  • http1.1:长连接改善性能开销,支持管道;
  • http2.0:头部压缩消除重复部分,二进制增加传输效率,并发传输多个stream复用一条tcp连接但接收时TCP会阻塞,服务器推送
  • http3.0:无队头阻塞QUIC多个流,QUIC包含TLS握手1RTT建立连接,连接迁移防止切换网络时断开连接(四元组->连接ID)

程序从加载到运行的过程

1. 用户触发执行

  • 方式:通过命令行输入、图形界面双击或脚本调用。
  • 系统检查:操作系统验证文件存在性、用户权限(执行权限)及文件完整性。

2. 创建新进程

  • 进程控制块(PCB):操作系统分配PCB,存储进程状态、PID、优先级等信息。
  • 虚拟地址空间:为进程分配独立的虚拟内存空间,确保隔离性。

3. 加载可执行文件

  • 文件格式解析:解析ELF(Linux)或PE(Windows)等格式,提取代码段(.text)、数据段(.data)、BSS段(.bss)等信息。
  • 内存映射
    • 代码段:只读属性,映射到固定或随机地址(ASLR启用时)。
    • 数据段:可读写,包含已初始化全局变量。
    • BSS段:分配内存并初始化为零,无需磁盘内容。
  • 按需分页:物理内存延迟分配,触发缺页中断时加载所需页。

4. 初始化运行时环境

  • 栈分配:设置栈指针(SP),分配栈空间(局部变量、函数调用)。
  • 堆初始化:通过brk/sbrk或mmap分配堆空间,供动态内存分配(如malloc)。
  • 全局变量:.data段数据载入内存,.bss段清零。
  • 环境变量与参数:将命令行参数(argv)和环境变量压栈或存入特定寄存器。

5. 执行初始化代码

  • 入口点:控制权转至_start(由C运行时库定义),而非直接跳转到main。
  • C运行时(CRT)初始化
    • 调用全局构造函数(C++静态对象)。
    • 初始化标准I/O流(stdin/stdout/stderr)。
    • 设置线程局部存储(TLS)。
  • 参数传递:准备argc、argv并调用main函数。

6. 程序执行

  • 主函数运行:执行main函数逻辑,处理用户代码。
  • 系统调用:通过中断或syscall指令请求操作系统服务(如文件操作、网络通信)。
  • 异常处理:信号处理、缺页中断等由操作系统接管。

流程图概览

用户触发 → 权限检查 → 创建进程 → 加载ELF/PE → 动态链接 → 初始化运行时 → 执行main → 终止清理

zookeeper、mysql、redis 、etcd 怎么实现分布式锁,各有什么优缺点,生产中一般用那个

1. Zookeeper 分布式锁

实现原理
  • 临时顺序节点 + Watcher 机制
    客户端在指定父节点(如 /lock)下创建临时顺序节点(如 /lock/seq-000000001),若自身节点序号最小则获取锁;否则监听前一个节点的删除事件,触发重新判断锁归属。
  • 自动释放:会话断开或客户端崩溃时,临时节点自动删除,避免死锁。
优点
  • 强一致性:基于 ZAB 协议,保证数据强一致性。
  • 公平锁:顺序节点天然支持公平性,避免饥饿问题。
  • 可靠性高:自动释放锁机制完善,无死锁风险。
缺点
  • 性能较低:频繁的节点创建、监听和事件回调导致延迟较高。
  • 复杂度高:需维护 Zookeeper 集群,运维成本较高。

2. MySQL 分布式锁

实现原理
  • 乐观锁/悲观锁
    • 乐观锁:通过版本号字段实现,更新时判断版本号是否匹配。
    • 悲观锁:使用 SELECT ... FOR UPDATE 或行级锁,限制并发访问。
优点
  • 实现简单:无需额外中间件,适合已有 MySQL 的场景。
缺点
  • 性能差:高并发下频繁操作数据库易成瓶颈。
  • 可靠性低:死锁检测困难,需手动处理锁超时。
  • 扩展性差:主从架构下同步延迟可能导致锁失效。

3. Redis 分布式锁

实现原理
  • SETNX + EXPIRE:通过 SET key value NX PX 30000 原子命令加锁,设置过期时间避免死锁。
  • Redisson 框架:封装锁续期、可重入等逻辑,支持 RedLock 算法(多节点容错)。
优点
  • 高性能:基于内存操作,支持高并发场景。
  • 易用性高:结合 Redisson 实现简单,社区支持完善。
缺点

在这里插入图片描述

  • 弱一致性:主从切换时可能导致锁失效(AP 特性)。
  • 锁续期复杂:需额外处理锁超时与业务执行时间的平衡。
  • RedLock 争议:部分场景下仍存在安全性问题(如时钟漂移)。

4. Etcd 分布式锁

实现原理
  • 租约(Lease) + 临时键:客户端创建带租约的临时键,通过 Revision 机制判断是否为最小键,监听前一个键的删除事件。
  • 心跳续约:定期续约防止租约过期,崩溃时自动释放锁。
优点
  • 强一致性:基于 Raft 协议,数据强一致。
  • 可靠性高:自动释放锁,避免死锁。
缺点
  • 性能中等:低于 Redis,但高于 Zookeeper。
  • 复杂度高:需管理租约和监听逻辑,实现成本较高。

生产环境选型建议

  1. Redis

    • 适用场景:高并发、对一致性要求不严格的场景(如秒杀、缓存更新)。
    • 推荐工具:Redisson 框架,支持锁续期和 RedLock 算法。
  2. Zookeeper/Etcd

    • 适用场景:强一致性要求的场景(如金融交易、配置管理)。
    • 推荐工具:Zookeeper 用 Curator 框架;Etcd 用 Jetcd 或官方 SDK。
  3. MySQL

    • 适用场景:低并发、无其他中间件的简单系统。

总结

  • 性能优先选 Redis:适用于大多数高并发场景,需结合业务兜底(如幂等性处理)。
  • 强一致选 Zookeeper/Etcd:适用于对可靠性要求极高的场景,如分布式协调服务。
  • MySQL 慎用:仅作为临时方案或低并发场景的补充。

实现LRU算法

在这里插入图片描述

type LRUCache struct {Cap intList *list.ListMp map[int]*list.Element
}
type node struct{key, value int
}func Constructor(capacity int) LRUCache {return LRUCache{capacity, list.New(), map[int]*list.Element{}}
}func (this *LRUCache) Get(key int) int {item := this.Mp[key]if item==nil{return -1}this.List.MoveToFront(item)return item.Value.(node).value
}func (this *LRUCache) Put(key int, value int)  {item := this.Mp[key]if item!=nil{item.Value = node{key, value}this.List.MoveToFront(item)}else{this.Mp[key] = this.List.PushFront(node{key, value})this.List.MoveToFront(this.Mp[key])if len(this.Mp)>this.Cap{delete(this.Mp, this.List.Remove(this.List.Back()).(node).key)}}
}/*** Your LRUCache object will be instantiated and called as such:* obj := Constructor(capacity);* param_1 := obj.Get(key);* obj.Put(key,value);*/
http://www.xdnf.cn/news/650053.html

相关文章:

  • Java大师成长计划之第31天:Docker与Java应用容器化
  • 基于matlab版本的三维直流电法反演算法
  • 论文阅读: 2023 NeurIPS Jailbroken: How does llm safety training fail?
  • 支持selenium的chrome driver更新到136.0.7103.113
  • C++寻位映射的究极密码:哈希扩展
  • ubuntu 22.04 配置静态IP、网关、DNS
  • 鸿蒙OSUniApp 实现的日期选择器与时间选择器组件#三方框架 #Uniapp
  • 对数的运算困惑
  • 鸿蒙OSUniApp 开发带有通知提示的功能组件#三方框架 #Uniapp
  • Linux《基础IO》
  • 深入Java TCP流套接字编程:高效服务器构建与高并发实战优化指南​
  • Kafka自定义分区策略实战避坑指南
  • 论文阅读笔记:YOLO-World: Real-Time Open-Vocabulary Object Detection
  • nginx安全防护与https部署实战
  • 简述各类机器学习问题
  • 机器学习k近邻,高斯朴素贝叶斯分类器
  • html使用JS实现账号密码登录的简单案例
  • uboot常用命令之eMMC/SD卡命令
  • rpm安装jenkins-2.452
  • 关于vue结合elementUI输入框回车刷新问题
  • API Gateway CLI 实操入门笔记(基于 LocalStack)
  • SQL注入原理及防护方案
  • 如何用 SQL 找到最受欢迎的用户?
  • 基数排序---Java版本
  • PcVue助力常熟三爱富实现FFKM后处理工艺智能化升级
  • 达梦JNI方式调用Logmnr接口调用示例
  • Node.js全局对象详解:console、process与核心功能
  • 每日Prompt:黄沙大圣
  • 将网页带格式转化为PDF
  • python网络编程