Это состояние, при котором все горутины ожидают события, которое не наступит. Go детектирует deadlock и вызывает panic, если основная горутина заблокирована на ожидании данных из канала, но нет активных писателей.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Mutex (от Mutual Exclusion — взаимное исключение) — это примитив синхронизации, который используется для защиты критических секций ресурса или данных, доступ к которым должен быть ограничен таким образом, чтобы только один поток или горутина могли работать с ним в каждый конкретный момент времени. Mutex гарантирует, что только один поток может входить в защищённую секцию кода, выполняющую операции над общими данными.
Это наиболее распространённый тип мьютексов. При попытке захватить мьютекс, если он уже занят другим потоком, поток блокируется и ожидает, пока мьютекс не будет освобождён.
Вместо блокирования потока, потоки активно проверяют состояние мьютекса в цикле. Это может быть эффективно, если мьютекс захватывается на очень короткое время, так как избегается затрата времени на блокировку и разблокировку потока.
Стандартная библиотека предоставляет мьютекс в пакете
sync
. Вот пример того, как можно использовать мьютекс для синхронизации доступа к общему ресурсу:package main
import (
"fmt"
"sync"
)
var (
balance int
mutex sync.Mutex
)
func deposit(value int, wg *sync.WaitGroup) {
mutex.Lock() // Захват мьютекса перед изменением переменной balance
fmt.Printf("Depositing %d to account with balance: %d\n", value, balance)
balance += value
mutex.Unlock() // Освобождение мьютекса после изменения
wg.Done()
}
func main() {
var wg sync.WaitGroup
wg.Add(2)
go deposit(100, &wg)
go deposit(200, &wg)
wg.Wait()
fmt.Printf("New Balance %d\n", balance)
}
Держите код внутри защищённой секции как можно более коротким, чтобы избежать замедления работы других потоков, ожидающих доступа к ресурсу.
Это может привести к взаимоблокировкам, если мьютексы захватываются в разном порядке.
Помните, что мьютексы защищают только блоки кода, для которых они явно захватываются. Доступ к тем же данным за пределами защищённого блока может привести к условиям гонки.
Если это возможно, используйте каналы или другие средства синхронизации, которые могут быть более идиоматичными.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Горутины управляются Go runtime, а не операционной системой напрямую.
Это делает их лёгкими и дешевыми — можно создавать тысячи горутин без нагрузки на систему.
ОС видит лишь ограниченное количество потоков (M), а Go сам планирует, какие G выполнять на каких M через P.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Слайс (
slice
) — это динамический массив, который ссылается на часть массива в памяти. В отличие от массивов (array
), слайсы могут изменять размер. Слайс в Go — это структура
type SliceHeader struct {
Data uintptr // Указатель на массив в памяти
Len int // Длина слайса (количество элементов)
Cap int // Вместимость (capacity) — сколько элементов может вместить без перевыделения памяти
}
Пример структуры слайса
arr := [5]int{1, 2, 3, 4, 5}
s := arr[1:4] // Берём срез от 2-го до 4-го элемента
fmt.Println(s) // [2 3 4]
Есть несколько способов создать слайс:
Способ 1: Срез массива
arr := [5]int{10, 20, 30, 40, 50}
s := arr[1:4] // [20 30 40]
Способ 2: Использование
make()
s := make([]int, 3, 5) // Длина 3, вместимость 5
fmt.Println(s, len(s), cap(s)) // [0 0 0] 3 5
Способ 3: Литерал (инициализация значениями)
s := []int{1, 2, 3}
fmt.Println(s) // [1 2 3]
Слайсы можно изменять, используя
append()
. s := []int{1, 2, 3}
s = append(s, 4, 5)
fmt.Println(s) // [1 2 3 4 5]
Когда
append()
увеличивает slice
, Go использует оптимизированный алгоритм роста:- Если
cap < 1024
, слайс удваивает размер (cap *= 2
). - Если
cap >= 1024
, рост идёт примерно на 25% (cap += cap / 4
). s := []int{}
for i := 0; i < 10; i++ {
s = append(s, i)
fmt.Printf("Len: %d, Cap: %d\n", len(s), cap(s))
}
Выход (пример)
Len: 1, Cap: 1
Len: 2, Cap: 2
Len: 3, Cap: 4
Len: 5, Cap: 8
Len: 9, Cap: 16
Так как слайсы хранят ссылку на массив, возможны побочные эффекты.
arr := [5]int{1, 2, 3, 4, 5}
s1 := arr[:3] // [1 2 3]
s2 := arr[2:] // [3 4 5]
s2[0] = 100 // Меняем первый элемент s2
fmt.Println(s1) // [1 2 100] ❗️ s1 тоже изменился
Решение: используйте
copy()
для создания нового массива. s1 := []int{1, 2, 3}
s2 := make([]int, len(s1))
copy(s2, s1) // Копируем данные
s2[0] = 100
fmt.Println(s1) // [1 2 3] ✅ Оригинал не изменился
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
В Go пустой интерфейс
interface{}
является особым типом, который может содержать значение любого типа. Это связано с тем, что в Go любой тип реализует пустой интерфейс, поскольку в нем нет методов, которые нужно реализовать. Поскольку пустой интерфейс не требует реализации каких-либо методов, любой тип в Go автоматически реализует этот интерфейс. Это делает пустой интерфейс универсальным контейнером для значений любых типов.
type interface{} interface {}
Типа конкретного значения
Самого значения
Когда значение присваивается переменной типа интерфейс, Go сохраняет информацию о типе и значении этого значения. Для пустого интерфейса эта информация может быть любого типа.
Когда значение из пустого интерфейса приводится к конкретному типу, происходит проверка типа во время выполнения. Если значение внутри интерфейса действительно является указанным типом, приведение успешно. В противном случае приведение не удается, и возвращается значение
nil
или происходит паника, если приведение выполнено без проверки.Присваивание значений пустому интерфейсу
package main
import "fmt"
func main() {
var i interface{}
i = 42
fmt.Println(i) // 42
i = "hello"
fmt.Println(i) // hello
}
Утверждение типа (Type Assertion)
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")
}
// Утверждение типа без проверки
// Это вызовет панику, если тип не соответствует
s = i.(string)
fmt.Println("String:", s)
}
Использование switch для проверки типа
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)
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Плюсы: простота разработки, тестирования и развертывания, отсутствие сложностей с сетевыми взаимодействиями.
Минусы: низкая масштабируемость, сложность вносить изменения в крупное приложение.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Это предопределенное идентификатор, используемое для создания последовательностей целочисленных констант. Он применяется в контексте объявления констант и автоматически инкрементируется на единицу с каждым новым значением. Обычно используется для определения множества связанных констант без необходимости вручную назначать каждому элементу значение.
Начинает счет с 0 в каждой новой группе констант.
Каждое последующее использование
iota
в одной группе констант увеличивает его значение на 1.При каждом новом объявлении константного блока
iota
сбрасывается до 0.package main
import "fmt"
const (
A = iota // 0
B // 1
C // 2
)
func main() {
fmt.Println(A) // Вывод: 0
fmt.Println(B) // Вывод: 1
fmt.Println(C) // Вывод: 2
}
Использование его для создания битовых флагов
package main
import "fmt"
const (
Flag1 = 1 << iota // 1 << 0 = 1
Flag2 // 1 << 1 = 2
Flag3 // 1 << 2 = 4
Flag4 // 1 << 3 = 8
)
func main() {
fmt.Println(Flag1) // Вывод: 1
fmt.Println(Flag2) // Вывод: 2
fmt.Println(Flag3) // Вывод: 4
fmt.Println(Flag4) // Вывод: 8
}
Сброс его в новом блоке
package main
import "fmt"
const (
X = iota // 0
Y // 1
)
const (
Z = iota // 0 (новый блок констант, iota сбрасывается)
W // 1
)
func main() {
fmt.Println(X) // Вывод: 0
fmt.Println(Y) // Вывод: 1
fmt.Println(Z) // Вывод: 0
fmt.Println(W) // Вывод: 1
}
Можно использовать в выражениях и совместно с другими константами для создания более сложных последовательностей.
package main
import "fmt"
const (
_ = iota // пропускаем 0
KB = 1 << (10 * iota) // 1 << 10 = 1024
MB // 1 << 20 = 1048576
GB // 1 << 30 = 1073741824
)
func main() {
fmt.Println("KB:", KB) // Вывод: KB: 1024
fmt.Println("MB:", MB) // Вывод: MB: 1048576
fmt.Println("GB:", GB) // Вывод: GB: 1073741824
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
EXPLAIN
— это команда в SQL, которая показывает, как база данных выполняет SQL-запрос. Она помогает оптимизировать запросы, анализируя их план выполнения. Когда вы пишете
EXPLAIN
перед SQL-запросом, база данных не выполняет его, а показывает пошаговый процесс выполнения. Это помогает понять: Какие индексы используются
Какие таблицы сканируются
Какие соединения (
JOIN
) выполняются Оценить производительность запроса
EXPLAIN SELECT * FROM users WHERE age > 30;
Выход (пример)
Seq Scan on users (cost=0.00..25.00 rows=10 width=100)
Добавляет реальное время выполнения запроса
EXPLAIN ANALYZE SELECT * FROM users WHERE age > 30;
Использование индексов
CREATE INDEX idx_users_age ON users(age);
Правильный порядок
JOIN
EXPLAIN SELECT * FROM orders JOIN users ON orders.user_id = users.id;
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
ACID – это принципы целостности транзакций в базах данных:
- A (Atomicity) – транзакция либо выполняется полностью, либо не выполняется вовсе.
- C (Consistency) – после транзакции БД остается в согласованном состоянии.
- I (Isolation) – параллельные транзакции не влияют друг на друга.
- D (Durability) – данные сохраняются даже в случае сбоя системы.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Горутины в Go являются легковесными потоками управления, которые позволяют выполнять параллельные задачи более эффективно по сравнению с потоками операционной системы. Их ключевые преимущества включают:
Горутины потребляют значительно меньше памяти и ресурсов по сравнению с потоками ОС. Каждая горутина стартует с размером стека порядка 2 КБ, тогда как поток ОС требует гораздо большего размера стека (обычно несколько мегабайт).
Горутины управляются встроенным планировщиком Go, а не планировщиком ОС. Планировщик Go распределяет выполнение горутин по доступным потокам ОС, используя концепцию "M:N" (много горутин на несколько потоков).
Создание, синхронизация и управление горутинами намного проще благодаря встроенным средствам Go, таким как каналы (
channels
) и sync
-пакет. В то время как работа с потоками ОС требует дополнительных усилий для синхронизации (мьютексы, условные переменные и т.д.).Стек горутины автоматически увеличивается или уменьшается в зависимости от потребностей, что позволяет эффективно использовать память.
Синтаксис и использование горутин интуитивно понятны. Для запуска достаточно добавить
go
перед вызовом функции:package main
import (
"fmt"
"time"
)
func sayHello() {
for i := 0; i < 5; i++ {
fmt.Println("Hello")
time.Sleep(100 * time.Millisecond)
}
}
func main() {
go sayHello() // Запускаем горутину
fmt.Println("World")
time.Sleep(1 * time.Second) // Даем горутине время завершиться
}
Горутины позволяют эффективно использовать современные многоядерные процессоры благодаря встроенной конкурентной модели и поддержке параллелизма.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Да, между SSL (Secure Sockets Layer) и TLS (Transport Layer Security) есть отличия. TLS является улучшенной и более безопасной версией SSL.
SSL 1.0: Никогда не был выпущен публично из-за серьезных уязвимостей.
SSL 2.0: Выпущен в 1995 году, но вскоре был признан небезопасным из-за множества уязвимостей.
SSL 3.0: Выпущен в 1996 году, значительно улучшил безопасность, но со временем также был признан устаревшим из-за уязвимостей (например, POODLE-атака).
TLS 1.0: Выпущен в 1999 году как обновление SSL 3.0. Включает исправления безопасности и улучшения.
TLS 1.1: Выпущен в 2006 году с дополнительными защитами от некоторых атак.
TLS 1.2: Выпущен в 2008 году, поддерживает современные алгоритмы шифрования и хеширования.
TLS 1.3: Выпущен в 2018 году, значительно улучшена безопасность и производительность, упрощен процесс установки соединения.
SSL: Поддерживает более старые и менее безопасные алгоритмы шифрования.
TLS: Поддерживает более современные и безопасные алгоритмы шифрования. TLS 1.3 исключает поддержку устаревших алгоритмов и предлагает только современные безопасные алгоритмы.
SSL: Более сложный процесс рукопожатия, включающий несколько шагов, что делает его уязвимым для некоторых атак.
TLS: Улучшенный процесс рукопожатия, включая использование HMAC (Hash-based Message Authentication Code) для обеспечения целостности сообщения. TLS 1.3 значительно упрощает и ускоряет процесс рукопожатия.
SSL: Использует комбинацию MD5 и SHA-1 для целостности данных, что не так безопасно по современным стандартам.
TLS: Использует HMAC с SHA-256 и другими современными алгоритмами для обеспечения целостности данных.
SSL: Меньше возможностей для управления сеансами.
TLS: Включает улучшенные механизмы для управления сеансами, такие как возобновление сеансов, что позволяет экономить время и ресурсы при повторных подключениях.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Пустая строка ("") в Go — это структура string, которая содержит:
- указатель на данные (0 байт),
- длину строки (int — 8 байт на 64-битной архитектуре).
Таким образом, даже пустая строка весит 16 байт (указатель + длина), без учёта хранимых данных.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Слайсы являются ссылочными типами, поэтому простое присваивание одного слайса другому создаст новую ссылку на тот же подлежащий массив. Если вы хотите создать копию слайса с независимым подлежащим массивом, можно использовать встроенную функцию
copy
или методы, такие как использование append
.Создает побайтовую копию элементов из одного слайса в другой.
package main
import "fmt"
func main() {
original := []int{1, 2, 3, 4, 5}
// Создаем новый слайс той же длины, что и оригинал
copySlice := make([]int, len(original))
// Копируем элементы из оригинального слайса в новый
copy(copySlice, original)
// Изменяем элемент в копии
copySlice[0] = 100
fmt.Println("Оригинал:", original) // Выводит: Оригинал: [1 2 3 4 5]
fmt.Println("Копия:", copySlice) // Выводит: Копия: [100 2 3 4 5]
}
Использование функции
inal)
Чтобы создать новый слайс с копированными элементами.
package main
import "fmt"
func main() {
original := []int{1, 2, 3, 4, 5}
// Копируем элементы из оригинального слайса в новый слайс
copySlice := append([]int(nil), original...)
// Изменяем элемент в копии
copySlice[0] = 100
fmt.Println("Оригинал:", original) // Выводит: Оригинал: [1 2 3 4 5]
fmt.Println("Копия:", copySlice) // Выводит: Копия: [100 2 3 4 5]
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
- Лёгкие по памяти (обычно килобайты, а не мегабайты).
- Масштабируемы — можно запускать тысячи горутин.
- Управляются внутренним планировщиком Go, а не ОС.
- Быстро переключаются между задачами.
- Просты в использовании (каналы, select, go).
Это делает горутины особенно эффективными для серверной разработки и высоконагруженных систем.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Замыкание (closure) — это функция, которая запоминает и использует переменные из своей внешней области видимости, даже если эта область видимости уже закончила своё выполнение.
Когда внутренняя функция обращается к переменным внешней функции, она "замыкает" эти переменные на себе. Они продолжают существовать, пока жива сама функция-замыкание.
package main
import "fmt"
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
func main() {
inc := counter() // создаём замыкание
fmt.Println(inc()) // 1
fmt.Println(inc()) // 2
fmt.Println(inc()) // 3
newInc := counter() // новое замыкание с новой переменной count
fmt.Println(newInc()) // 1
}
Счётчики (как в примере выше). Фабричные функции, создающие специфичные обработчики. Ограничение доступа к данным (инкапсуляция).
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Foreign key (внешний ключ) — это ограничение в базе данных, обеспечивающее связь между таблицами и целостность данных.
Он:
- Предотвращает вставку «висячих» записей (без связанных данных).
- Позволяет БД контролировать каскадное удаление/обновление.
- Делает структуру БД более безопасной и логичной.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
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
- G (goroutine) — лёгкая задача.
- M (machine) — системный поток.
- P (processor) — логическая единица исполнения, выполняющая G на M.
Планировщик:
- Назначает горутины на доступные процессоры.
- Переключает задачи при блокировке.
- Использует стратегию work-stealing, чтобы равномерно загружать потоки.
- Работает независимо от ОС.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM