Golang | Вопросы собесов
4.72K subscribers
29 photos
965 links
Download Telegram
🤔 Что такое триггер?

Триггер — это специальная процедура в СУБД, которая автоматически выполняется при определённом событии (например, INSERT, UPDATE, DELETE) в таблице.

🚩Как работают триггеры?

Когда выполняется определённое действие с таблицей, триггер срабатывает автоматически и выполняет заданную логику. Это полезно для:
- Автоматической проверки данных.
- Поддержания целостности информации.
- Логирования изменений.

🚩Пример триггера в PostgreSQL

Допустим, у нас есть таблица orders, и мы хотим автоматически сохранять историю изменений заказов в таблицу orders_log.
CREATE TABLE orders_log (
id SERIAL PRIMARY KEY,
order_id INT,
old_status TEXT,
new_status TEXT,
changed_at TIMESTAMP DEFAULT now()
);

CREATE OR REPLACE FUNCTION log_order_update()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO orders_log (order_id, old_status, new_status)
VALUES (OLD.id, OLD.status, NEW.status);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER order_status_change
AFTER UPDATE ON orders
FOR EACH ROW
WHEN (OLD.status IS DISTINCT FROM NEW.status)
EXECUTE FUNCTION log_order_update();


🚩Разновидности триггеров

🟠До выполнения операции (`BEFORE`)
Используется для проверки или модификации данных перед изменением.

🟠После выполнения операции (`AFTER`)
Чаще всего используется для логирования или дополнительных действий.

🟠INSTEAD OF (для представлений)
Позволяет заменить выполнение операции (INSERT, UPDATE, DELETE).

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Что такое указатели?

Указатели — это переменные, которые хранят адреса других переменных в памяти. Они позволяют изменять данные в памяти напрямую, что экономит ресурсы, избегая копирования. Указатели полезны для работы с большими структурами или данных, передаваемых между функциями.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Как закрыть канал?

В Go каналы (chan) можно закрывать с помощью close(channel), чтобы показать, что больше не будет отправляться данных.

Как закрыть канал?
ch := make(chan int)
close(ch) // Закрываем канал


Полный пример
package main

import "fmt"

func main() {
ch := make(chan int)

go func() {
for i := 1; i <= 3; i++ {
ch <- i // Отправляем данные
}
close(ch) // Закрываем канал
}()

for val := range ch { // Читаем пока канал не закроется
fmt.Println(val)
}

fmt.Println("Канал закрыт, чтение завершено")
}


Выход
1
2
3
Канал закрыт, чтение завершено


🚩Как проверить, закрыт ли канал?

Используем val, ok := <-ch:
- ok == true → канал открыт, есть данные.
- ok == false → канал закрыт.
package main

import "fmt"

func main() {
ch := make(chan int)
close(ch)

val, ok := <-ch
fmt.Println("val:", val, "ok:", ok) // val: 0 ok: false
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Как встроить стандартный профайлер в своё приложение?

Интегрируйте профайлер с помощью пакета net/http/pprof, добавив HTTP-сервер в приложение. После запуска профайлер будет доступен через веб-интерфейс.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1
🤔 Как называется механизм сборки мусора?

В Go механизм сборки мусора называется Garbage Collector (GC).

🚩Как работает сборщик мусора в Go?

Go использует автоматический сборщик мусора с конкурентной, трицветной, инкрементальной стратегией.

🟠Конкурентный (Concurrent)
выполняется параллельно с работой программы.
🟠Трёхцветный (Tri-color)
объекты помечаются как белые, серые и чёрные.
🟠Инкрементальный (Incremental)
работает порциями, а не останавливает программу надолго.

🚩Алгоритм трёхцветной маркировки

🟠Белые объекты
мусор, который будет удалён.
🟠Серые объекты
те, которые находятся в обработке.
🟠Чёрные объекты
используемые объекты, которые не подлежат удалению.

🚩Оптимизация GC в Go

Чтобы уменьшить нагрузку на GC:
Используйте пул объектов (sync.Pool).
Минимизируйте аллокации в куче (например, используйте []byte вместо string, если можно).
Ограничивайте долгоживущие объекты, так как они реже сканируются.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Что будет происходить при конкурентной записи в map?

Конкурентная запись в обычную map приведет к состоянию гонки данных, что может вызвать некорректные результаты, панику или крах программы, так как map не является потокобезопасной.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Что такое HAVING?

HAVING — это оператор в SQL, который фильтрует группированные (GROUP BY) данные по агрегатным функциям (SUM, COUNT, AVG, MAX, MIN).

🚩Чем отличается `HAVING` от `WHERE`?

WHERE фильтрует отдельные строки до группировки.
HAVING фильтрует группы строк после GROUP BY.

Теперь посчитаем сумму продаж по категориям и оставим только те, где сумма > 250
SELECT category, SUM(amount) AS total_sales
FROM sales
GROUP BY category
HAVING SUM(amount) > 250;


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Что из себя представляет тип данных string в языке Go?

Тип string в Go — это последовательность байтов, закодированных в UTF-8.
Он реализован как структура:
- указатель на массив байтов;
- длина строки.
Строки неизменяемы. Любая операция, которая кажется «изменением», на самом деле создаёт новую строку.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Для чего используют индексы?

Это целочисленные значения, которые используются для доступа к элементам упорядоченных структур данных. В контексте Go индексы чаще всего применяются для работы со строками, массивами, срезами, а также картами (косвенно, через ключи).

🚩Для чего нужны индексы?

🟠Доступ к элементам по их позиции
Индексы позволяют обращаться к конкретным элементам массива, строки или среза. Например, если у нас есть массив чисел, индекс указывает, какой именно элемент извлечь.

🟠Навигация внутри коллекций
С помощью индексов можно перебирать элементы массива, строки или среза, например, используя циклы.

🟠Изменение значений (если структура изменяема)
В изменяемых структурах данных, таких как срезы или массивы, индекс позволяет присвоить новое значение конкретному элементу.

🟠Оптимизация поиска
Индексы упрощают и ускоряют доступ к данным, потому что доступ осуществляется за O(1) (константное время) в массивах или срезах.

🟠Индексы в строках
В строках индексы используются для доступа к конкретным байтам.
package main

import "fmt"

func main() {
str := "Привет"

fmt.Println(str[0]) // 208 (байт, не символ!)
fmt.Printf("%c\n", str[0]) // П (символ, представленный первым байтом UTF-8)
}


🟠Индексы в массивах и срезах
В массивах и срезах индексы используются для извлечения и изменения значений
package main

import "fmt"

func main() {
arr := [5]int{10, 20, 30, 40, 50}

fmt.Println(arr[2]) // 30

// Изменение значения по индексу
arr[2] = 100
fmt.Println(arr) // [10 20 100 40 50]
}


🟠Как использовать индексы в циклах
Обычно индексы используются для итерации по элементам коллекции с помощью цикла for.
package main

import "fmt"

func main() {
nums := []int{10, 20, 30, 40, 50}

for i, v := range nums {
fmt.Printf("Индекс: %d, Значение: %d\n", i, v)
}
}


🟠Индексы и подстроки
Индексы полезны для извлечения подстрок с использованием срезов:
package main

import "fmt"

func main() {
str := "Привет, Мир!"

fmt.Println(str[8:12]) // Мир
}


🚩Ошибки работы с индексами

🟠Выход за границы
Если попытаться обратиться к элементу по индексу, который выходит за пределы коллекции, Go выдаст runtime panic:

   package main

func main() {
nums := []int{1, 2, 3}
fmt.Println(nums[5]) // panic: runtime error: index out of range
}


🟠Работа с многобайтовыми символами в строках
Если неверно учитывать байтовое представление символов UTF-8, можно получить некорректный результат.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Как избежать deadlock?

- Фиксировать транзакции быстрее – минимизировать время блокировки.
- Обращаться к таблицам в одном порядке во всех транзакциях.
- Использовать LOCK TABLES осторожно – избегать чрезмерного блокирования.
- Рассмотреть уровень изоляции – READ COMMITTED или REPEATABLE READ могут уменьшить вероятность дедлоков.
- Анализировать логи и SHOW ENGINE INNODB STATUS для выявления конфликтов.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥2
🤔 Что делает HAVING?

HAVING — это оператор SQL, который фильтрует результаты после GROUP BY, аналогично WHERE, но работает с агрегатными функциями (COUNT(), SUM(), AVG(), MAX(), MIN()).

🚩Ключевая разница

WHERE фильтрует до GROUP BY (по отдельным строкам).
HAVING фильтрует после GROUP BY (по сгруппированным данным).

Пример 1: Фильтрация по HAVING
Задача: Вывести товары, у которых продано более 10 единиц.
SELECT product, SUM(quantity) as total_sold
FROM sales
GROUP BY product
HAVING SUM(quantity) > 10;


Пример 2: Разница между WHERE и HAVING
SELECT category, COUNT(*) as total_products
FROM products
WHERE price > 100 -- Убирает дешёвые товары ДО группировки
GROUP BY category
HAVING COUNT(*) > 5; -- Оставляет только категории с более 5 товаров


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Какие отличия у быстрого объявления и объявления через var?

В Go есть два способа объявления переменной:
1. Быстрое объявление (:=) — используется внутри функций, тип выводится автоматически:
2. name := "Alice"
3. Объявление через var — даёт больше контроля, можно использовать вне функции и указывать тип:
4. var age int = 30
5. var city string
Краткое объявление невозможно на уровне пакета и не подходит для заранее объявленных, но ещё не инициализированных переменных.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
🤔 Чем stream отличается от unary?

В контексте gRPC (Google Remote Procedure Call) unary и stream — это два разных типа взаимодействия между клиентом и сервером.

🚩Unary RPC (Обычный вызов)

🟠Unary (унарный) вызов
это стандартный запрос-ответ:
Клиент отправляет одно сообщение → сервер отвечает одним сообщением.
service UserService {
rpc GetUserInfo(UserRequest) returns (UserResponse);
}


Go-реализация Unary RPC
func (s *server) GetUserInfo(ctx context.Context, req *pb.UserRequest) (*pb.UserResponse, error) {
user := &pb.UserResponse{
Id: req.Id,
Name: "John Doe",
Email: "[email protected]",
}
return user, nil // Обычный ответ
}


🚩Streaming RPC (Потоковый вызов)

В streaming RPC передача данных идёт потоком. gRPC поддерживает три вида стримов:

🟠Server Streaming (Поток от сервера)
Клиент отправляет один запрос → сервер возвращает поток ответов.
service UserService {
rpc GetUserActivity(UserRequest) returns (stream ActivityResponse);
}


Go-реализация Server Streaming
func (s *server) GetUserActivity(req *pb.UserRequest, stream pb.UserService_GetUserActivityServer) error {
activities := []string{"Login", "Upload File", "Logout"}
for _, activity := range activities {
err := stream.Send(&pb.ActivityResponse{Message: activity})
if err != nil {
return err
}
time.Sleep(time.Second) // Имитация задержки
}
return nil
}


🟠Client Streaming (Поток от клиента)
Клиент отправляет поток данных → сервер отвечает одним ответом.
service UploadService {
rpc UploadFile(stream FileChunk) returns (UploadResponse);
}


Go-реализация Client Streaming
func (s *server) UploadFile(stream pb.UploadService_UploadFileServer) error {
var totalSize int64
for {
chunk, err := stream.Recv()
if err == io.EOF {
return stream.SendAndClose(&pb.UploadResponse{Size: totalSize})
}
if err != nil {
return err
}
totalSize += chunk.Size
}
}


🟠Bidirectional Streaming (Двунаправленный поток)
Клиент и сервер обмениваются данными в потоке одновременно.
service ChatService {
rpc Chat(stream ChatMessage) returns (stream ChatMessage);
}


Go-реализация Bi-directional Streaming
func (s *server) Chat(stream pb.ChatService_ChatServer) error {
for {
msg, err := stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
response := &pb.ChatMessage{Text: "Echo: " + msg.Text}
if err := stream.Send(response); err != nil {
return err
}
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
🤔 Что такое дедлоки (deadlock)?

Это ситуация, когда две или более транзакции блокируют друг друга, ожидая ресурс, который уже заблокирован другой транзакцией. Это приводит к зависанию операций и невозможности завершить выполнение запросов.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Как можно проверить тип интерфейса?

В Go, проверка типа интерфейса может быть выполнена несколькими способами: с помощью утверждения типа (type assertion) и с помощью конструкции switch для выбора типа.

🚩Утверждение типа (Type Assertion)

Утверждение типа позволяет проверить, является ли значение определенного интерфейса конкретным типом. Если да, то оно преобразует интерфейс в этот тип.
value, ok := interfaceValue.(ConcreteType)


🟠value
Значение типа ConcreteType, если утверждение типа успешно.
🟠ok
Булевое значение, указывающее, удалось ли преобразование.

package main

import "fmt"

func main() {
var i interface{} = "hello"

// Утверждение типа
s, ok := i.(string)
if ok {
fmt.Println("String:", s)
} else {
fmt.Println("Not a string")
}

// Утверждение типа, которое вызовет панику, если тип не соответствует
// Uncomment the line below to see the panic
// s := i.(string)
// fmt.Println(s)
}


🚩Использование `switch` для выбора типа

Конструкция switch позволяет проверить значение интерфейса на соответствие нескольким возможным типам.
switch v := interfaceValue.(type) {
case ConcreteType1:
// v имеет тип ConcreteType1
case ConcreteType2:
// v имеет тип ConcreteType2
default:
// v имеет другой тип
}


package main

import "fmt"

func printType(i interface{}) {
switch v := i.(type) {
case string:
fmt.Println("String:", v)
case int:
fmt.Println("Integer:", v)
case bool:
fmt.Println("Boolean:", v)
default:
fmt.Printf("Unknown type: %T\n", v)
}
}

func main() {
printType("hello")
printType(42)
printType(true)
printType(3.14)
}


Пример использования для проверки и работы с интерфейсами
package main

import (
"fmt"
)

type Person struct {
Name string
Age int
}

// Реализация интерфейса fmt.Stringer для типа Person
func (p Person) String() string {
return fmt.Sprintf("%s (%d years old)", p.Name, p.Age)
}

func printValue(i interface{}) {
if str, ok := i.(fmt.Stringer); ok {
fmt.Println("Stringer:", str.String())
} else {
fmt.Println("Not a Stringer")
}
}

func main() {
p := Person{Name: "Alice", Age: 30}
printValue(p) // Проверка типа fmt.Stringer
printValue("Hello, world!") // Строка не реализует fmt.Stringer
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Можно ли сделать int(string) и string(int) соответственно?

int(string) невозможно, так как это несопоставимые типы, но можно конвертировать строку в число через strconv.Atoi. string(int) возможно, но результатом будет символ, соответствующий числовому значению в таблице Unicode.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Какие основные отличия есть у Go перед языками Java, Python?

Обладает несколькими ключевыми отличиями от Java и Python, что делает его уникальным и подходящим для определенных задач.

🟠Компиляция и выполнение
Go: Компилируемый язык, компилируется в машинный код, что обеспечивает высокую производительность и быстрое время выполнения.
Java: Компилируется в байт-код, который выполняется на виртуальной машине Java (JVM). Это обеспечивает переносимость, но может добавлять накладные расходы.
Python: Интерпретируемый язык, что делает его менее производительным по сравнению с компилируемыми языками.

🟠Простота и лаконичность
Go: Разработан для простоты и читаемости, минимизирует синтаксическую сложность, избегает избыточности.
Java: Сложный и многословный синтаксис, требует больше кода для выполнения тех же задач.
Python: Простой и читаемый синтаксис, который делает его легким для изучения и использования.

🟠Управление памятью
Go: Автоматическая сборка мусора, но с управляемыми задержками для обеспечения высокой производительности.
Java: Автоматическая сборка мусора на JVM, что может приводить к задержкам в критических приложениях.
Python: Автоматическая сборка мусора с использованием подсчета ссылок и циклического сборщика мусора.

🟠Конкурентность и параллелизм
Go: Встроенная поддержка конкурентности через горутины и каналы, легковесные потоки с низкими накладными расходами.
Java: Многопоточность с использованием потоков, сложное управление потоками и синхронизацией.
Python: Поддержка многопоточности, но ограниченная глобальной блокировкой интерпретатора (GIL), что затрудняет использование многопоточности для параллельных вычислений.

🟠Статическая и динамическая типизация
Go: Статически типизированный язык, ошибки типа обнаруживаются на этапе компиляции, что повышает надежность кода.
Java: Статически типизированный язык, что позволяет обнаруживать ошибки типа на этапе компиляции.
Python: Динамически типизированный язык, типы проверяются во время выполнения, что может приводить к ошибкам времени выполнения.

🟠Интерфейсы и наследование
Go: Использует интерфейсы для определения поведения без наследования. Интерфейсы реализуются неявно.
Java: Использует классы и интерфейсы, поддерживает множественное наследование через интерфейсы и одиночное наследование классов.
Python: Поддерживает множественное наследование классов, что может усложнять структуру программы.

🟠Стандартная библиотека
Go: Богатая стандартная библиотека с встроенной поддержкой работы с сетью, веб-серверами и другими задачами.
Java: Широкая стандартная библиотека с обширной поддержкой различных API и утилит.
Python: Обширная стандартная библиотека, особенно сильная в области научных вычислений, обработки данных и веб-разработки.

🟠Экосистема и инструменты
Go: Современные и мощные инструменты для сборки, тестирования и профилирования. Простая система управления зависимостями.
Java: Зрелая экосистема с множеством фреймворков и инструментов (например, Maven, Gradle, Spring).
Python: Обширная экосистема пакетов и библиотек (например, pip, virtualenv, Django).

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
🤔 Опишите алгоритм, как будет происходить вставка в Map.

1. Вычисляется хэш-значение ключа с использованием хэш-функции.
2. На основе хэш-значения определяется позиция (bucket) для хранения пары ключ-значение.
3. Если bucket пустой, пара помещается в этот bucket.
4. Если bucket занят (коллизия), данные добавляются в структуру разрешения коллизий (например, связанный список или дерево).
5. Если объём данных превышает определённый порог, структура может быть преобразована для улучшения производительности (например, из списка в дерево).


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔3👍2💊1
🤔 Почему нельзя брать ссылку на значение, хранящееся по ключу в map?

Нельзя напрямую взять ссылку на значение, хранящееся по ключу в карте (map), из-за особенностей реализации карт и управления памятью. Рассмотрим подробнее, почему это так.

🚩Причины, почему нельзя брать ссылку на значение в карте

🟠Внутреннее устройство карты (map)
Карты реализованы на основе хеш-таблиц. Внутреннее устройство карты предполагает, что значения могут перемещаться в памяти при выполнении операций, таких как добавление или удаление элементов. Хеш-таблица может перераспределять (реорганизовывать) свои внутренние структуры для оптимизации доступа к элементам. Это может происходить, например, когда карта увеличивается в размере.

🟠Потенциальная недействительность ссылок
Если бы была возможность брать ссылки на значения, хранящиеся в карте, то при любой операции изменения карты (добавление, удаление элементов) ссылки могли бы становиться недействительными. Это привело бы к потенциально небезопасному поведению программы, так как указатель (ссылка) мог бы указывать на уже несуществующую или перемещенную область памяти.

🚩Демонстрация проблемы

Здесь попытка взять ссылку на значение из карты могла бы привести к проблемам
package main

import "fmt"

func main() {
m := map[string]int{"a": 1, "b": 2}

// Нельзя делать так:
// p := &m["a"]

// Вместо этого можно работать с копией значения
value := m["a"]
p := &value

fmt.Println("Value:", *p) // 1

// Изменение карты
m["c"] = 3

// Ссылка на значение в карте могла бы стать недействительной
// fmt.Println("Value after map change:", *p)
}


🚩Правильные способы работы со значениями карты

Для работы с ними лучше использовать копии значений. Вот несколько способов, как это можно сделать:

🟠Работа с копией значения
Получить значение из карты и сохранить его в переменную.
value := m["a"]   


🟠Изменение значения в карте
Если необходимо изменить значение в карте, его нужно сначала извлечь, изменить, а затем снова записать в карту.
value := m["a"]
value = value + 10
m["a"] = value


🟠Использование указателей в качестве значений
В некоторых случаях можно использовать указатели в качестве значений карты, чтобы можно было изменять значения через указатели.
   package main

import "fmt"

func main() {
m := map[string]*int{"a": new(int), "b": new(int)}
*m["a"] = 1
*m["b"] = 2

// Теперь можно брать указатели на значения
p := m["a"]
fmt.Println("Value:", *p) // 1

// Изменение значения через указатель
*p = 42
fmt.Println("Updated Value:", *m["a"]) // 42
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Что происходит при склеивании строк?

Склеивание (конкатенация) строк создаёт новую строку, содержащую все части. В большинстве языков строки — неизменяемы, и потому при склеивании каждая операция создаёт новый объект в памяти. Например:
Под капотом может происходить множественное выделение памяти, особенно при частых склеиваниях.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2