Это целочисленные значения, которые используются для доступа к элементам упорядоченных структур данных. В контексте 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
1. Чтение возможно – если в канале остались данные, они будут возвращены.
2. Получение zero-value – если канал пуст, возвращается нулевое значение типа (0 для int, "" для string и nil для указателей).
3. ok == false – можно проверить факт закрытия с помощью второго параметра при чтении (val, ok := <-chan).
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1🔥1
Таймауты (
timeout) в HTTP-запросах предотвращают зависание приложения при медленных или недоступных серверах. Они позволяют ограничить время ожидания ответа, чтобы избежать бесконечного ожидания и высвободить ресурсы.В Golang таймауты можно устанавливать на разных уровнях:
Timeout для всего запроса (включает подключение, отправку и получение данных)
client := &http.Client{
Timeout: 5 * time.Second, // Запрос не может длиться дольше 5 секунд
}Таймаут на установку соединения
transport := &http.Transport{
DialContext: (&net.Dialer{
Timeout: 2 * time.Second, // 2 секунды на подключение
}).DialContext,
}
client := &http.Client{Transport: transport}Таймаут на чтение и запись
transport := &http.Transport{
ResponseHeaderTimeout: 3 * time.Second, // 3 секунды на заголовки
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Нормализация — это процесс организации структуры таблиц в базе данных для устранения дублирования данных и обеспечения логической целостности.
Цель — разбиение данных на связанные таблицы, чтобы минимизировать избыточность и упростить обновления.
Существуют уровни нормализации (первая нормальная форма, вторая и т.д.), каждая из которых добавляет ограничения к структуре данных.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2👍1
Слайсы представляют собой мощный и гибкий инструмент для работы с последовательностями элементов. Основаны на массивах, но они предоставляют более удобный и динамичный способ работы с данными.
Указатель на первый элемент массива, на который ссылается слайс.
Количество элементов, доступных в слайсе.
Максимальное количество элементов, которое может содержать слайс без перераспределения памяти.
Можно представить в виде структуры
type slice struct {
ptr *ElementType // Указатель на базовый массив
len int // Длина
cap int // Емкость
}Можно создать из массива, указав диапазон элементов:
package main
import "fmt"
func main() {
arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4] // Слайс содержит элементы с индексами 1, 2, 3
fmt.Println(slice) // [2 3 4]
}
Позволяет создать слайс определенной длины и емкости:
package main
import "fmt"
func main() {
slice := make([]int, 3, 5) // Слайс длиной 3 и емкостью 5
fmt.Println(slice) // [0 0 0]
}
Осуществляется так же, как и к элементам массива:
package main
import "fmt"
func main() {
slice := []int{1, 2, 3, 4, 5}
fmt.Println(slice[0]) // 1
slice[1] = 10
fmt.Println(slice) // [1 10 3 4 5]
}
Для этого используется функция
appendpackage main
import "fmt"
func main() {
slice := []int{1, 2, 3}
slice = append(slice, 4, 5) // Добавляем элементы 4 и 5 в конец слайса
fmt.Println(slice) // [1 2 3 4 5]
}
Можно создавать новые слайсы на основе существующих
package main
import "fmt"
func main() {
slice := []int{1, 2, 3, 4, 5}
subSlice := slice[1:4] // Слайс содержит элементы с индексами 1, 2, 3
fmt.Println(subSlice) // [2 3 4]
}
Для этого используется функция
copypackage main
import "fmt"
func main() {
src := []int{1, 2, 3}
dst := make([]int, len(src))
copy(dst, src)
fmt.Println(dst) // [1 2 3]
}
Могут автоматически изменять свою емкость при добавлении новых элементов с помощью
append. Когда емкость текущего массива недостаточна для добавления новых элементов, создается новый массив с большей емкостью, в который копируются существующие элементы.package main
import "fmt"
func main() {
slice := make([]int, 3, 5)
fmt.Println("Before append:", slice, "Len:", len(slice), "Cap:", cap(slice))
slice = append(slice, 1, 2, 3)
fmt.Println("After append:", slice, "Len:", len(slice), "Cap:", cap(slice))
slice = append(slice, 4)
fmt.Println("After another append:", slice, "Len:", len(slice), "Cap:", cap(slice))
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
Существуют встроенные функции для преобразования типов, включая преобразование из строки в целое число и наоборот. Для этих целей используются функции из стандартной библиотеки
strconv. Рассмотрим, как это делается.Для этого используется функция
strconv.Atoi. Она возвращает два значения: само число и ошибку, если преобразование не удалось.package main
import (
"fmt"
"strconv"
)
func main() {
str := "123"
num, err := strconv.Atoi(str)
if err != nil {
fmt.Println("Error converting string to int:", err)
} else {
fmt.Println("Converted number:", num)
}
}
Для этого используется функция
strconv.Itoa.package main
import (
"fmt"
"strconv"
)
func main() {
num := 123
str := strconv.Itoa(num)
fmt.Println("Converted string:", str)
}
Важно обрабатывать ошибки при преобразовании типов, особенно при преобразовании строки в целое число, чтобы избежать неожиданных сбоев в программе.
package main
import (
"fmt"
"strconv"
)
func main() {
str := "abc"
num, err := strconv.Atoi(str)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Converted number:", num)
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1