go模拟经典面试题

讲下MySQL事务

(1)事务的概念

事务就是对数据库执行一系列操作,这些操作要么全部成功执行,要么全部失败,不会存在部分成功的情况。

(2)事务的ACID特点

  1. 原子性:一个事务中的所有操作,要么全部完成,要么全部不完成.
  2. 一致性:事务操作前和操作后,数据满足完整性约束,数据库保持一致性状态。
  3. 隔离性:多个事务并发执行时,一个事务的执行不应影响其他事务。
  4. 持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

(3)MySQL提供了四种事务隔离级别

  1. 读未提交:一个事务可以读取另一个未提交事务的数据。这可能导致脏读、不可重复读和幻读问题。
  2. 读已提交:一个事务只能读取另一个已提交事务的数据。这可以防止脏读,但可能出现不可重复读和幻读问题。
  3. 可重复读:对同一字段的多次读取结果都是一致的。这是MySQL的默认隔离级别,可以防止脏读和不可重复读问题,但可能出现幻读问题。
  4. 串行化:最高的隔离级别,所有的事务依次逐个执行,这样事务之间就不可能产生干扰。但这种方式性能最低,效率最差。

(4)事务控制语句

在MySQL中,可以使用BEGIN、COMMIT和ROLLBACK等语句来控制事务的开启、提交和回滚。通过这些语句,我们可以确保事务的ACID特性得到满足,从而保证数据的一致性和完整性。

Session、Cookie、JWT的区别

  1. Session:
  • 定义:Session通常用于跟踪用户与Web应用程序之间的交互状态。服务器会为每个会话创建一个唯一的会话标识符(Session ID),并将其通过Cookie或URL重写的方式发送给客户端。
  • 存储方式:Session数据存储在服务器端,而Session ID则存储在客户端(通常是通过Cookie)。
  • 安全性:由于Session数据存储在服务器端,因此相对于存储在客户端的JWT或Cookie来说,Session在数据安全性方面更高。但是,如果Session ID被窃取,攻击者可能会利用它进行会话劫持。
  • 跨域性:Session通常只能在单个Web应用程序内部使用,不支持跨域。
  1. Cookie:
  • 定义:Cookie是一种在客户端存储数据并在每次请求时发送给服务器的机制。它主要用于跟踪用户状态、记录用户偏好等信息。
  • 存储方式:Cookie数据存储在客户端的浏览器中。
  • 安全性:Cookie数据在传输过程中可能会被截获或篡改,因此需要注意对Cookie进行加密和签名以保证数据的完整性和安全性。同时,由于Cookie存储在客户端,因此也存在被窃取的风险。
  • 跨域性:Cookie通常受到同源策略的限制,即只能被与其创建时相同的域名、协议和端口的页面访问。但是,可以通过设置Cookie的Domain和Path属性来实现跨子域或跨目录的访问。
  1. JWT:
  • 定义:JWT是一种基于JSON的开放标准(RFC 7519),用于在网络应用程序之间安全地传输信息。它主要用于身份验证和授权,可以在多个节点之间传递用户状态信息。
  • 存储方式:JWT通常作为请求头或请求体的一部分发送给服务器,而不是存储在客户端或服务器端的内存中。这使得JWT具有无状态的特点,即服务器不需要保存用户的会话信息。
  • 安全性:JWT使用加密算法和签名算法来保证数据的完整性和安全性。由于JWT是在客户端生成的,因此可以避免服务器端的Session管理开销。但是,如果JWT被窃取,攻击者可能会利用它进行身份验证。
  • 跨域性:JWT支持跨域认证,可以在多个应用程序之间共享用户状态信息。这使得JWT在分布式和微服务架构中特别有用。

综上所述,Session、Cookie和JWT在Web开发中各有特点。Session适用于需要跟踪用户状态的场景,但存在会话劫持的风险;Cookie适用于需要在客户端存储少量数据的场景,但需要注意数据的安全性和跨域性;JWT适用于需要跨域认证和授权的场景,具有无状态的特点和较高的安全性。

讲下操作系统里的锁

在操作系统中,锁是用于协调多个线程或进程并发访问共享资源的一种机制。当多个线程或进程试图同时访问同一资源时,如果没有锁的保护,就可能导致数据的不一致或其他并发问题。

锁的基本思想是:当一个线程或进程需要访问共享资源时,它首先会尝试获取该资源的锁。如果锁当前没有被其他线程或进程持有(即处于空闲状态),那么该线程或进程就可以获取锁并访问资源。如果锁已经被其他线程或进程持有,那么该线程或进程就需要等待,直到锁被释放。

通过这种机制,操作系统可以确保在同一时间只有一个线程或进程能够访问特定的共享资源,从而保证了数据的一致性和正确性。这就是操作系统中锁的基本概念和作用

slice的扩容规则

Slice在Go语言中是一个引用类型,它是对底层数组的抽象,包含三个主要部分:data(元素的存储位置)、len(已存储的元素数量)和cap(可以存储的元素的最大数量)。当slice的容量不足以容纳新添加的元素时,它会进行扩容。

扩容规则:

  1. Go 1.17 及之前版本
  1. 当slice的容量小于1024时,扩容会使其容量翻倍。
  2. 当slice的容量大于或等于1024时,每次扩容会增加其容量的25%,直到新的容量超过或等于所需的容量。
  1. Go 1.18 及之后版本

Go 1.18引入了一个新的扩容策略,设定了一个阈值(threshold)为256。

  1. 当slice的容量小于或等于256时,扩容会使其容量翻倍。
  2. 当slice的容量大于256时,每次扩容会增加(旧容量 + 3 * 256) / 4。

如果新切片需要的容量大于两倍扩容的容量,则直接按照新切片需要的容量进行扩容。

map是线程安全的么,使用安全map的方法

在Go语言中,map 类型本身并不是线程安全的。这意味着如果有多个goroutine并发地读写同一个map,可能会导致竞态条件(race condition)和未定义的行为,如panic或数据损坏。

使用安全map的方法:
(1)使用sync.Mutex或sync.RWMutex(读写互斥锁)来保护对map的访问。在访问map之前先锁定互斥锁,访问完成后释放锁。这样可以确保同一时间只有一个goroutine能够访问map。
(2)Go 1.9引入了一个新的并发安全的map类型sync.Map。这个类型使用了一种特殊的机制来优化并发读写操作,避免了互斥锁的开销,但在某些场景下可能不如传统的互斥锁保护下的map高效。它适用于读多写少的场景。

讲下go里的锁

在Go语言中,锁是用于同步并发访问共享资源的重要机制。Go提供了几种类型的锁来支持并发编程中的同步需求。

  1. 互斥锁( sync.Mutex
  1. 互斥锁是最简单且最常见的锁,用于实现对共享资源的排他访问。
  2. 同一时刻只能有一个goroutine获得互斥锁,其他goroutine必须等待,直到锁被释放。
  3. 互斥锁通过sync.Mutex结构体表示,包含Lock()和Unlock()两个方法,分别用于加锁和解锁。
  4. 使用互斥锁时,应避免重复加锁或解锁,以防止运行时错误或死锁。
  5. 可以通过defer语句来确保在函数结束时释放锁,避免忘记解锁导致的问题。
  1. 读写锁( sync.RWMutex
  1. 读写锁是一种特殊的互斥锁,允许多个goroutine同时读取共享资源,但只允许一个goroutine写入共享资源。
  2. 读写锁通过sync.RWMutex结构体表示,提供了RLock()、RUnlock()、Lock()和Unlock()等方法。
  3. 在读锁占用的情况下,会阻止写操作,但不会阻止其他读操作。
  4. 当多个goroutine需要频繁读取共享资源且并发性要求较高时,可以考虑使用读写锁来提高性能。
  1. 其他类型的锁
  1. Go的sync包还提供了其他一些类型的锁,如sync.WaitGroup(用于等待一组goroutine完成)、sync.Once(确保某个操作只执行一次)和sync.Cond(条件变量,用于实现基于条件的等待/通知)。

讲下context包

context包是Go语言中处理并发编程和API边界传递信息的重要工具,它使得在goroutine之间传递截止日期、取消信号和其他请求范围的值变得更加简单和安全。context包主要用于以下场景:

  1. 传递请求范围的值 :context包允许你将请求范围的值与上下文关联。这些值可以通过上下文在整个调用链中传递,而不需要显式地传递给每个函数。这在处理请求范围的数据、用户身份验证信息等方面非常有用。
  2. 取消操作 :通过上下文,可以在请求被取消时传递取消信号给整个调用链中的goroutine。这对于及时释放资源、停止进行中的操作等场景非常有用。
  3. 超时控制 :context包提供了设置超时时间的机制,可以在一定时间内自动取消请求。这对于避免长时间阻塞或超时的操作非常有用,例如网络请求、数据库查询等。
  4. 并发控制 :context包可以协调并发操作,允许多个goroutine之间共享上下文并处理竞态条件。它提供了安全地传递上下文和取消信号的机制,以确保并发操作的正确性和可靠性。

如何使多个goroutine同时停止或会退出。

当需要确保多个goroutine同时停止或会退出时:

1 )使用 context

  1. 创建一个父级context,并使用context.WithCancel(), context.WithDeadline(), 或 context.WithTimeout()创建子context。
  2. 将子context传递给每个goroutine。
  3. 当需要停止时,调用cancel函数或等待截止时间到达。
  4. 在每个goroutine中,监听context的Done() channel,并在收到关闭信号时退出。

2 )使用 channel 通信

  1. 创建一个停止信号channel(如stopCh := make(chan struct{}))。
  2. 将停止信号channel传递给每个goroutine。
  3. 当需要停止时,向channel发送一个信号。
  4. 在每个goroutine中,监听停止信号channel,并在收到信号时退出。

3 )使用 sync.WaitGroup 等待退出 (可选):

  1. 如果需要等待所有goroutine完成后再继续执行其他操作,可以使用WaitGroup。
  2. 在每个goroutine开始之前调用wg.Add(1),并在goroutine结束时调用wg.Done()。
  3. 在主goroutine中调用wg.Wait()等待所有goroutine完成。

注意:

  • 确保goroutine在退出时清理了它们使用的资源。
  • 避免在goroutine之间共享可变状态,除非它们被适当地同步。

以上步骤提供了一种简单而灵活的方式来控制多个goroutine的停止和退出。根据具体需求,可以选择使用context、channel或WaitGroup,或者它们的组合。

讲一下mysql里面的索引

MySQL中的索引是一种数据结构,旨在提高数据库查询的性能。

  1. 索引的概念
  1. 索引是帮助MySQL高效获取数据的数据结构,它允许数据库系统根据特定的查找算法快速定位到表中的记录。
  2. 可以类比为字典中的页码,通过索引可以快速定位到所需的信息。
  1. 索引的类型
  1. B- 树索引( B-tree Index :最常见的索引类型,MySQL中的大多数存储引擎都使用B-树或其变种B+树来存储索引。
  2. 哈希索引( Hash Index :基于哈希表的索引,通过计算哈希值来定位数据。但哈希索引只支持等值查询,不支持范围查询。
  3. 普通索引( Normal Index :允许在定义索引的列中插入重复值和空值。
  4. 唯一索引( Unique Index :确保索引列中的值是唯一的,避免数据出现重复。
  5. 主键索引( Primary Key Index :为主键字段创建的索引,确保数据的唯一性。
  6. 空间索引( Spatial Index :用于地理空间数据类型的字段。
  7. 全文索引( Full-text Index :用于在文本字段上执行全文搜索。
  8. 单列索引( Single-column Index :只包含原表的一个列的索引。
  1. 索引的特性
  1. 提高查询效率 :通过索引,数据库系统可以避免全表扫描,从而加快查询速度。
  2. 加速连接操作 :在连接多个表时,使用索引的列可以加速连接操作。
  3. 维护开销 :虽然索引可以提高查询性能,但索引的创建和维护也需要额外的开销。
  4. 主键索引与非主键索引 :主键索引的叶子节点存储的是一行完整的数据,而非主键索引的叶子节点存储的是主键的值。在查询时,如果使用了非主键索引,可能需要先找到主键值,然后再通过主键索引找到完整的数据,这被称为"回表"操作。
  1. 使用索引的注意事项
  1. 并非所有列都需要建立索引,应根据实际情况选择适当的列建立索引。
  2. 过度索引可能导致性能下降,因为每次插入、更新或删除数据时都需要维护索引结构。
  3. 在选择使用何种类型的索引时,应根据数据的特性和查询需求进行选择。
  4. 定期分析和优化索引,以确保其始终保持最佳状态。

讲下redis里两种可持久化方式

  1. RDB (Redis DataBase)
  1. 方式:创建数据集的快照并保存到磁盘。
  2. 优点:恢复速度快,文件小。
  3. 缺点:可能丢失最后一次快照后的数据,创建快照时可能会消耗额外内存。
  1. AOF (Append Only File)
  1. 方式:记录Redis执行的写命令到日志文件中。
  2. 优点:数据恢复完整度高,可以选择不同的写入策略。
  3. 缺点:日志文件可能较大,恢复数据时需要重放所有写命令,速度较慢。

结合使用
Redis可以同时使用RDB和AOF,但恢复数据时优先使用AOF。这种方式可以提供更好的数据恢复完整度,但可能会增加磁盘I/O和CPU的负担。

讲下redis里的高可用

Redis 的高可用主要依赖于以下几种机制和技术。

  1. 数据持久化
  1. Redis 支持两种主要的数据持久化方式:RDB(Redis DataBase)和 AOF(Append Only File)。RDB 是通过创建数据集的快照来持久化数据,而 AOF 则记录所有的写命令。这两种方式都旨在确保在系统重启或崩溃后,数据不会完全丢失。
  1. 主从同步(主从复制)
  1. Redis 支持主从复制模式,其中一个 Redis 实例作为主节点(master)处理写操作,而一个或多个 Redis 实例作为从节点(slave)复制主节点的数据,并处理读操作。这样可以提高系统的可扩展性和可靠性,因为从节点可以分担读操作的负载,并且在主节点发生故障时,可以从从节点中选举出新的主节点。
  1. Redis 哨兵模式( Sentinel
  1. Redis Sentinel 是一个用于监控 Redis 集群的高可用性解决方案。它可以自动发现主从节点,并在主节点出现故障时自动进行故障转移,选择一个新的主节点。Sentinel 还提供了监控、通知和配置管理的功能。
  1. Redis 集群( Cluster
  1. Redis Cluster 提供了自动分片和数据复制的功能,可以将数据分散到多个 Redis 节点上,从而实现水平扩展。Cluster 还支持在多个节点之间进行故障转移,确保系统的可用性。
  1. 配置优化
  1. 为了实现高可用,还需要对 Redis 进行一些配置优化。例如,可以调整持久化策略、设置合理的超时时间、启用连接池等。
  1. 监控和告警
  1. 使用监控工具(如 Redis Insight、Grafana 等)来监控 Redis 的运行状态,包括内存使用、性能指标、集群状态等。当检测到异常或故障时,可以及时发出告警,以便及时处理。

总之,Redis 的高可用主要通过数据持久化、主从同步、哨兵模式、集群等多种机制和技术来实现。在配置和优化 Redis 时,需要根据实际需求和场景来选择合适的方案,并进行合理的监控和告警设置,以确保系统的稳定性和可用性。

new与make的区别

通常在 Go 语言中,new 和 make 用于内存分配和初始化时有所区别。

  • new 分配内存并返回一个指向该内存的指针,不进行初始化(零值初始化除外)。
  • make 用于创建切片、映射和通道,并返回已初始化的引用。
  • 在 Go 语言中,如果你需要分配并初始化一个切片、映射或通道,应该使用 make;如果你需要分配内存并返回一个指向该内存的指针(不进行初始化),应该使用 new。

讲下什么场景下会发生panic

在Go语言中,panic是一种内建的机制,用于表示程序遇到了无法恢复的错误,需要立即停止执行。

  1. 越界错误 :这包括数组越界和字符串越界。当尝试访问数组或字符串的非法索引时,会触发panic。
  2. 空指针引用 :如果直接引用一个空指针结构体的字段,将会导致panic。但请注意,如果在一个成员方法中并未引用结构体的字段,即使该方法的接收者是一个空指针,也不会触发panic。
  3. 断言失败 :在Go语言中,断言用于将接口值转换为指定的类型。如果断言失败(即接口值不包含指定的类型),则会触发panic。
  4. Map 操作错误 :如果对一个未初始化的map进行读写操作,或者map的value是结构体指针且在使用前未进行判空检查,都可能导致panic。
  5. 除数为零 :在Go语言中,除数为零是一个运行时错误,会导致panic。
  6. 调用未实现的方法 :如果尝试调用一个未实现的方法,Go运行时系统会触发panic。
  7. 通道操作错误 :向已经关闭的通道写入数据会触发panic,但读取已关闭的通道则不会。此外,多次关闭同一个通道也会导致panic。
  8. Goroutine 竞争资源 :当多个goroutine试图同时访问和修改共享资源时,可能会引发死锁或其他并发问题,这些问题也可能导致panic。
  9. 内存不足 :如果程序尝试分配的内存超过了系统的可用内存,Go运行时系统可能会触发panic。
  10. 栈溢出 :递归死循环或超出栈空间都可能导致栈溢出,从而触发panic。

此外,当程序遇到无法恢复的错误时,开发者也可以选择主动触发panic来中断程序的正常执行流程。例如,当检测到非法参数、不可预料的状态或不可修复的运行时错误时,可以使用panic来停止当前函数的执行并向上传播错误信息。

注意,虽然panic可以用于处理一些严重的错误情况,但它并不应该被用作常规的错误处理机制。在大多数情况下,应该使用Go的错误处理机制(即返回error类型的值)来处理可恢复的错误。

讲下defer的使用场景,以及执行顺序

在Go语言中,defer语句用于在函数返回之前执行一些清理工作,比如关闭文件、解锁互斥锁等。defer语句会将函数调用的执行推迟到包含该defer语句的函数执行完毕时。无论包含defer的函数是通过正常返回还是由于发生panic异常而退出,defer语句都会被执行。

使用场景

  1. 资源释放 :当函数使用了一些需要显式释放的资源(如文件句柄、网络连接、互斥锁等)时,可以使用defer来确保这些资源在函数结束时被正确释放。
  2. 错误处理 :在函数中,可以使用defer来包裹一些错误处理逻辑,确保即使在函数执行过程中发生错误,也能执行必要的清理工作。
  3. 日志记录 :使用defer可以在函数结束时记录日志,包括函数的执行时间、执行结果等信息。
  4. 关闭数据库连接 :在数据库操作函数中,可以使用defer来关闭数据库连接,确保即使函数在执行过程中发生错误,数据库连接也能被正确关闭。
  5. 解锁互斥锁 :在涉及并发编程的场景中,可以使用defer来确保互斥锁在函数结束时被正确解锁,避免死锁等问题。

执行顺序

defer语句的执行顺序是后进先出(LIFO,Last In First Out)的。也就是说,后声明的defer语句会先执行。这是因为Go语言将defer语句的调用压入一个栈中,当函数返回时,再从栈中弹出并执行这些defer语句。

gorm与sql的区别

  1. 定义和用途
  1. GORM:GORM是一个用于Go语言的ORM(对象关系映射)库。它提供了一种简单、优雅的方式来操作数据库,支持多种数据库系统,如MySQL、PostgreSQL、SQLite和SQL Server。通过GORM,开发者可以使用Go语言的结构体来表示数据库中的表,并使用GORM提供的方法来执行增、删、改、查等操作。
  2. SQL:SQL是一种用于管理和操作关系数据库的编程语言。它具有数据操纵(如SELECT、INSERT、UPDATE、DELETE等)和数据定义(如CREATE、ALTER、DROP等)等多种功能。SQL语言可以独立应用于终端,也可以作为子语言为其他程序设计提供有效助力。
  1. 特点和优势
    1. GORM:
      1. 全功能ORM:支持关联、事务、迁移、批量插入、预加载、复合主键等。
      2. 开箱即用:提供了许多默认行为,使开发者可以快速上手。
      3. 链式操作:支持链式操作,使代码更加简洁和易读。
      4. 自动迁移:可以自动迁移数据库,通过代码创建、更新和删除数据库表。
      5. Hooks:支持在执行数据库操作前后执行自定义的Hook函数。
    2. SQL:
      1. 交互性:具有交互性特点,能为用户提供极大的便利。
      2. 灵活性:可以直接与数据库进行交互,提供丰富的功能和灵活的查询能力。
      3. 标准化:SQL是一种标准化的语言,可以在多种数据库系统中使用。
  2. 使用方式
  1. GORM:通过Go语言的结构体和GORM提供的方法来操作数据库,无需直接编写SQL语句。
  2. SQL:需要直接编写SQL语句来操作数据库,可以与多种编程语言结合使用。
  1. 性能
  1. 在性能方面,ORM库(如GORM)通常会引入一些额外的开销,因为它们需要在Go语言和数据库之间建立映射关系并执行相应的转换操作。而直接使用SQL语句通常具有更高的性能。但是,这并不意味着ORM库的性能一定比直接使用SQL差,具体取决于使用场景和查询的复杂度。

综上所述,GORM和SQL在数据库操作领域各有优势和适用场景。GORM提供了简单、优雅的方式来操作数据库,适用于需要快速开发、维护和扩展的应用场景;而SQL则提供了丰富的功能和灵活的查询能力,适用于需要直接操作数据库和进行复杂查询的场景。

了解beggo么,和 Gin有什么区别

beego 和 Gin 都是 Go 语言的 Web 框架,但它们在设计和用途上有所不同:

1 beego

  • 它是一个全功能的 Web 框架,提供了丰富的功能和模块,如ORM、会话管理、缓存、日志等。
  • 它适合用于构建复杂的大型 Web 应用程序。
  • 提供了开发工具 bee,用于快速开发、测试、打包和部署应用。

2 Gin

  • 它是一个轻量级的 Web 框架,专注于性能和易用性。
  • 提供了快速的路由和丰富的中间件支持。
  • 非常适合构建 API 服务或需要高性能的 Web 应用程序。
  • 简洁的设计使得开发者可以快速地构建和扩展应用。

简单来说,如果你需要构建一个功能丰富的 Web 应用,beego 可能更适合你;而如果你只需要一个简单的 API 服务或高性能的 Web 应用,那么 Gin 可能是更好的选择。

相关推荐
零炻大礼包40 分钟前
【SQL server】数据库远程连接配置
数据库
zmgst1 小时前
canal1.1.7使用canal-adapter进行mysql同步数据
java·数据库·mysql
随心............1 小时前
python操作MySQL以及SQL综合案例
数据库·mysql
€☞扫地僧☜€1 小时前
docker 拉取MySQL8.0镜像以及安装
运维·数据库·docker·容器
CopyDragon1 小时前
设置域名跨越访问
数据库·sqlite
xjjeffery1 小时前
MySQL 基础
数据库·mysql
写bug的小屁孩1 小时前
前后端交互接口(三)
运维·服务器·数据库·windows·用户界面·qt6.3
恒辉信达1 小时前
hhdb数据库介绍(8-4)
服务器·数据库·mysql
独行soc2 小时前
#渗透测试#SRC漏洞挖掘#深入挖掘XSS漏洞02之测试流程
web安全·面试·渗透测试·xss·漏洞挖掘·1024程序员节
理想不理想v2 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试