Триггер — это специальная процедура в СУБД, которая автоматически выполняется при определённом событии (например,
INSERT, UPDATE, DELETE) в таблице.Когда выполняется определённое действие с таблицей, триггер срабатывает автоматически и выполняет заданную логику. Это полезно для:
- Автоматической проверки данных.
- Поддержания целостности информации.
- Логирования изменений.
Допустим, у нас есть таблица
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();
Используется для проверки или модификации данных перед изменением.
Чаще всего используется для логирования или дополнительных действий.
Позволяет заменить выполнение операции (
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
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1
В Go механизм сборки мусора называется Garbage Collector (GC).
Go использует автоматический сборщик мусора с конкурентной, трицветной, инкрементальной стратегией.
выполняется параллельно с работой программы.
объекты помечаются как белые, серые и чёрные.
работает порциями, а не останавливает программу надолго.
мусор, который будет удалён.
те, которые находятся в обработке.
используемые объекты, которые не подлежат удалению.
Чтобы уменьшить нагрузку на GC:
Используйте пул объектов (
sync.Pool). Минимизируйте аллокации в куче (например, используйте
[]byte вместо string, если можно). Ограничивайте долгоживущие объекты, так как они реже сканируются.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
HAVING — это оператор в SQL, который фильтрует группированные (GROUP BY) данные по агрегатным функциям (SUM, COUNT, AVG, MAX, MIN). 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 — это последовательность байтов, закодированных в 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
- Фиксировать транзакции быстрее – минимизировать время блокировки.
- Обращаться к таблицам в одном порядке во всех транзакциях.
- Использовать LOCK TABLES осторожно – избегать чрезмерного блокирования.
- Рассмотреть уровень изоляции – READ COMMITTED или REPEATABLE READ могут уменьшить вероятность дедлоков.
- Анализировать логи и SHOW ENGINE INNODB STATUS для выявления конфликтов.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥2
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 и HAVINGSELECT 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
В 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
В контексте gRPC (Google Remote Procedure Call) unary и stream — это два разных типа взаимодействия между клиентом и сервером.
это стандартный запрос-ответ:
Клиент отправляет одно сообщение → сервер отвечает одним сообщением.
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 передача данных идёт потоком. gRPC поддерживает три вида стримов:
Клиент отправляет один запрос → сервер возвращает поток ответов.
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
}Клиент отправляет поток данных → сервер отвечает одним ответом.
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
}
}Клиент и сервер обмениваются данными в потоке одновременно.
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
Это ситуация, когда две или более транзакции блокируют друг друга, ожидая ресурс, который уже заблокирован другой транзакцией. Это приводит к зависанию операций и невозможности завершить выполнение запросов.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
В Go, проверка типа интерфейса может быть выполнена несколькими способами: с помощью утверждения типа (type assertion) и с помощью конструкции
switch для выбора типа. Утверждение типа позволяет проверить, является ли значение определенного интерфейса конкретным типом. Если да, то оно преобразует интерфейс в этот тип.
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 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
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Обладает несколькими ключевыми отличиями от 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
2. На основе хэш-значения определяется позиция (bucket) для хранения пары ключ-значение.
3. Если bucket пустой, пара помещается в этот bucket.
4. Если bucket занят (коллизия), данные добавляются в структуру разрешения коллизий (например, связанный список или дерево).
5. Если объём данных превышает определённый порог, структура может быть преобразована для улучшения производительности (например, из списка в дерево).
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔3👍2💊1
Нельзя напрямую взять ссылку на значение, хранящееся по ключу в карте (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
Grafana — это инструмент для визуализации и мониторинга данных. Он позволяет строить дашборды (панели) с графиками, метриками и алертами, получая данные из различных источников (Prometheus, InfluxDB, Loki, MySQL и других).
создание графиков, таблиц, гистограмм и других визуальных представлений.
отслеживание метрик серверов, баз данных, контейнеров и микросервисов.
поддерживает Prometheus, Elasticsearch, MySQL, PostgreSQL и десятки других.
оповещения (email, Slack, Telegram) при достижении критических значений.
удобный интерфейс для анализа производительности систем.
Можно отслеживать загрузку CPU, RAM, количество запросов в базе, ошибки и задержки API.
Позволяет наблюдать за работой Kubernetes, Docker, CI/CD-пайплайнов и микросервисов.
Может использоваться для отображения KPI, продаж, конверсий и других данных.
Если сервер падает или нагрузка превышает порог – Grafana уведомит команду.
Устанавливаем Prometheus и Grafana
Добавляем Prometheus как источник данных в Grafana
Создаём дашборд с метриками, например
Количество HTTP-запросов
Время ответа сервера
Количество активных соединений
http_requests_total{job="web_service"}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1