Составной индекс (Composite Index) — это индекс, который создаётся сразу на нескольких колонках таблицы базы данных. Он помогает ускорить поиск и сортировку данных, особенно если запросы используют условия по нескольким полям.
Если часто выполняются запросы с фильтрацией по нескольким колонкам (
WHERE col1 = X AND col2 = Y
), составной индекс ускоряет их выполнение.Если запросы используют
ORDER BY col1, col2
, индекс помогает избежать дополнительной сортировки.Без индекса СУБД вынуждена выполнять полный перебор (Full Table Scan), что занимает больше времени.
Он строится по нескольким колонкам и фактически представляет собой отсортированную структуру данных. Например, в PostgreSQL или MySQL создаётся так:
CREATE INDEX idx_name ON users (last_name, first_name);
Теперь база данных будет использовать индекс для запросов:
SELECT * FROM users WHERE last_name = 'Иванов' AND first_name = 'Петр';
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
В Go мапа (map[K]V) — это указатель на внутреннюю хеш-структуру. Её "вес" при передаче — 8 байт (64-битная архитектура), так как это просто ссылка.
Но фактический размер в памяти зависит от количества элементов и структуры хеш-таблицы, которая может занимать значительно больше.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Строки являются основным типом данных и представляют собой неизменяемые последовательности байт. Понимание того, как строки устроены внутри, поможет лучше управлять производительностью и памятью при работе со строками. Вот основные аспекты внутреннего устройства строк:
Строки неизменяемы, что означает, что их содержимое не может быть изменено после создания. Это важное свойство обеспечивает несколько преимуществ, включая безопасность при передаче строк между функциями и горутинами без необходимости блокировок или других форм синхронизации.
Внутренне строка представлена структурой, которая содержит два поля:
Указатель на массив байтов: Это указатель на первый элемент массива байт, который фактически хранит символы строки в кодировке UTF-8.
Длина: Количество байт в строке, а не количество рун или символов. Это важное различие, поскольку в UTF-8 один символ может занимать от 1 до 4 байт.
Go использует UTF-8 как стандартную кодировку для строк. Это позволяет эффективно работать с международным текстом, поддерживая широкий спектр символов без сложностей, связанных с другими кодировками. Однако это также означает, что операции, такие как получение длины строки в рунах (символах) или доступ к отдельному символу, могут потребовать дополнительных вычислений для обработки многобайтовых символов.
Поскольку строки неизменяемы, любая операция, которая кажется "изменяющей" строку, на самом деле создает новую строку. Операции среза строк в Go особенно эффективны, потому что новые строки создаются путем указания на тот же массив байтов, что и исходная строка, с изменением только начальной позиции и длины. Это делает срезы строк очень быстрыми и экономичными с точки зрения использования памяти.
Благодаря неизменяемости и способу хранения строк в виде срезов байтов, Go обеспечивает эффективное управление памятью и производительность при работе со строками. Однако необходимо быть осторожным с операциями, которые могут казаться невинными, но приводят к частому созданию новых строк, так как это может повлечь за собой издержки на выделение памяти и сборку мусора.
s := "Hello, world" // Создание строки
t := s[7:] // Срез строки, создает новую строку "world"
fmt.Println(s) // Выводит: Hello, world
fmt.Println(t) // Выводит: world
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Это потокобезопасные операции с примитивными данными без мьютексов. Они включают операции чтения, записи, инкремента, сравнения и замены. Используются для повышения производительности при работе с простыми объектами, такими как счетчики или флаги.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
В Go
sync.WaitGroup
используется для синхронизации выполнения горутин. Она позволяет основной горутине (или любой другой горутине) ждать завершения группы горутин перед продолжением работы. Это особенно полезно, когда нужно убедиться, что все фоновые задачи завершены до выполнения дальнейших действий.Увеличивает (или уменьшает) счетчик горутин на заданное значение
delta
.Обычно вызывается до запуска горутин, чтобы установить количество горутин, которые нужно дождаться.
Уменьшает счетчик горутин на 1.
Вызывается горутиной, когда она завершает свою работу.
Блокирует выполнение до тех пор, пока счетчик горутин не станет равен нулю.
Обычно вызывается основной горутиной для ожидания завершения всех горутин.
Мы используем
WaitGroup
для ожидания завершения нескольких горутин.package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // Уменьшает счетчик на 1 при завершении работы горутины
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1) // Увеличивает счетчик горутин на 1
go worker(i, &wg)
}
wg.Wait() // Ожидает завершения всех горутин
fmt.Println("All workers done")
}
worker
.WaitGroup
увеличивается на 1 перед запуском каждой горутины с помощью wg.Add(1)
.wg.Done()
при завершении, уменьшая счетчик на 1.wg.Wait()
, блокируясь до тех пор, пока все горутины не завершат свою работу.Позволяет основной горутине дождаться завершения всех запущенных горутин, что особенно важно для корректного завершения программы или выполнения зависимых задач.
Гарантирует, что основная горутина не завершит выполнение программы до того, как завершатся все горутины, предотвращая возможные дедлоки или незавершенные операции.
Позволяет легко управлять множеством горутин, не требуя сложной логики для отслеживания их завершения.
Без использования
WaitGroup
основной поток может завершиться до завершения всех горутин, что приведет к неполной обработке данных. В этом примере использование time.Sleep
для ожидания является ненадежным и не гарантирует завершение всех горутин. Вместо этого правильное использование WaitGroup
обеспечивает корректное завершение всех задач.package main
import (
"fmt"
"time"
)
func worker(id int) {
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}
func main() {
for i := 1; i <= 5; i++ {
go worker(i)
}
time.Sleep(2 * time.Second) // Это не гарантирует завершение всех горутин
fmt.Println("All workers done")
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Пустой интерфейс
interface{}
является универсальным контейнером, который может содержать значение любого типа. Это связано с тем, что в Go любой тип автоматически реализует пустой интерфейс, поскольку в нем нет методов, которые нужно реализовать. числа, строки, булевы значения и т.д.
массивы, срезы, карты, структуры.
функции различных типов.
значения, которые реализуют другие интерфейсы.
Примитивные типы
package main
import "fmt"
func main() {
var i interface{}
i = 42
fmt.Println(i) // Output: 42
i = "hello"
fmt.Println(i) // Output: hello
i = true
fmt.Println(i) // Output: true
}
Композитные типы
package main
import "fmt"
func main() {
var i interface{}
i = []int{1, 2, 3}
fmt.Println(i) // Output: [1 2 3]
i = map[string]int{"one": 1, "two": 2}
fmt.Println(i) // Output: map[one:1 two:2]
type Person struct {
Name string
Age int
}
i = Person{Name: "Alice", Age: 30}
fmt.Println(i) // Output: {Alice 30}
}
Функции
package main
import "fmt"
func main() {
var i interface{}
i = func() {
fmt.Println("Hello from function")
}
if f, ok := i.(func()); ok {
f() // Output: Hello from function
}
}
Другие интерфейсы
package main
import "fmt"
type Stringer interface {
String() string
}
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%s (%d years old)", p.Name, p.Age)
}
func main() {
var i interface{}
i = Person{Name: "Alice", Age: 30}
if str, ok := i.(Stringer); ok {
fmt.Println(str.String()) // Output: Alice (30 years old)
}
}
Утверждение типа
package main
import "fmt"
func main() {
var i interface{} = 42
if v, ok := i.(int); ok {
fmt.Println("Integer:", v) // Output: Integer: 42
} else {
fmt.Println("Not an integer")
}
}
Использование
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") // Output: String: hello
printType(42) // Output: Integer: 42
printType(true) // Output: Boolean: true
printType(3.14) // Output: Unknown type: float64
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Для защиты можно использовать мьютексы, sync.Map или обрабатывать все операции с картой в отдельной горутине через каналы. Это исключает возможность одновременного доступа.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Объектно-ориентированная модель отличается от традиционных ООП-языков, таких как C# или Java. Нет классов и наследования в привычном понимании. Вместо этого используются структуры (structs) и интерфейсы для реализации основных принципов ООП: инкапсуляции, композиции и полиморфизма.
Служат аналогом классов. Они позволяют объединять данные в логически связанные группы.
type Person struct {
Name string
Age int
}
Могут быть определены для структур, что позволяет связывать функции с типами.
func (p Person) Greet() {
fmt.Printf("Hello, my name is %s\n", p.Name)
}
Достигается через модификаторы доступа на уровне пакета. Поля и методы, начинающиеся с заглавной буквы, экспортируемые (public), остальные — нет (private).
type Person struct {
name string // неэкспортируемое поле
Age int // экспортируемое поле
}
Go не поддерживает классическое наследование. Вместо этого используется композиция для включения функциональности одного типа в другой.
type Employee struct {
Person
Position string
}
Используются для определения поведения. Типы могут реализовывать интерфейсы неявно, просто предоставляя методы, указанные в интерфейсе.
type Greeter interface {
Greet()
}
func SayHello(g Greeter) {
g.Greet()
}
type Person struct {
Name string
}
func (p Person) Greet() {
fmt.Printf("Hello, my name is %s\n", p.Name)
}
func main() {
p := Person{Name: "John"}
SayHello(p)
}
Достигается через интерфейсы. Любой тип, который реализует интерфейс, может быть использован вместо него.
type Greeter interface {
Greet()
}
func SayHello(g Greeter) {
g.Greet()
}
type Robot struct {
ID string
}
func (r Robot) Greet() {
fmt.Printf("Greetings, I am robot %s\n", r.ID)
}
func main() {
p := Person{Name: "John"}
r := Robot{ID: "XJ-9"}
SayHello(p)
SayHello(r)
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Объектно-ориентированное программирование (ООП) в Go и C# реализовано с использованием различных подходов и парадигм, отражающих философию и дизайн каждого языка.
Используются структуры (
struct
).type Person struct {
Name string
Age int
}
func (p *Person) Greet() {
fmt.Printf("Hello, my name is %s\n", p.Name)
}
Используется композиция вместо наследования.
type Employee struct {
Person
Position string
}
Модификаторы доступа на уровне пакета (экспортируемые и неэкспортируемые поля).
type Person struct {
name string // неэкспортируемое поле
Age int // экспортируемое поле
}
Реализуется через интерфейсы.
type Greeter interface {
Greet()
}
type Person struct {
Name string
}
func (p *Person) Greet() {
fmt.Printf("Hello, my name is %s\n", p.Name)
}
Используются классы.
public class Person {
public string Name { get; set; }
public int Age { get; set; }
public void Greet() {
Console.WriteLine($"Hello, my name is {Name}");
}
}
Поддерживается классическое наследование.
public class Employee : Person {
public string Position { get; set; }
}
Модификаторы доступа (
public
, private
, protected
).public class Person {
private string name;
public int Age { get; set; }
}
Через виртуальные методы и интерфейсы.
public class Person {
public virtual void Greet() {
Console.WriteLine("Hello!");
}
}
public class Employee : Person {
public override void Greet() {
Console.WriteLine("Hello, I am an employee!");
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Стандартные уровни:
1. Read Uncommitted — минимальная изоляция, возможны "грязные" чтения.
2. Read Committed — читаются только зафиксированные данные.
3. Repeatable Read — одна и та же строка не изменяется другими транзакциями.
4. Serializable — максимальная изоляция, предотвращает все типы конфликтов, но снижает производительность.
Чем выше уровень — тем безопаснее, но медленнее.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Да, в SQL возможен
JOIN
с вложенными запросами. Это позволяет объединять результаты подзапросов с другими таблицами, что полезно, когда нужно предварительно отфильтровать или агрегировать данные перед объединением.JOIN можно использовать не только с реальными таблицами, но и с результатами подзапросов, если они представляют собой временные таблицы.
Допустим, у нас есть две таблицы:
-
orders (id, user_id, total)
-
users (id, name)
Мы хотим получить всех пользователей и их заказы, но только те заказы, где сумма больше 100
SELECT u.name, sub.total
FROM users u
JOIN (
SELECT user_id, total
FROM orders
WHERE total > 100
) AS sub ON u.id = sub.user_id;
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Это фундаментальные концепции, используемые в операционных системах для управления выполнением программ. Хотя они тесно связаны, между ними есть ключевые различия:
Это экземпляр выполняющейся программы. Процесс имеет собственное изолированное адресное пространство памяти, что означает, что память, которую использует один процесс, не может быть напрямую доступна другому процессу. Каждый процесс предоставляет ресурсы, необходимые для выполнения программы, включая память, дескрипторы файлов, и переменные среды. Процессы изолированы друг от друга, что повышает безопасность и устойчивость системы в целом, поскольку сбой в одном процессе обычно не влияет на другие процессы. Однако эта изоляция требует больших затрат ресурсов при переключении между процессами и при их создании.
Это более легковесная единица выполнения, которая существует внутри процесса. Все потоки внутри одного процесса делят одно и то же адресное пространство памяти и системные ресурсы, такие как файловые дескрипторы. Это позволяет потокам более эффективно общаться друг с другом, поскольку они могут обмениваться данными без использования специальных механизмов межпроцессного взаимодействия. Потоки идеально подходят для выполнения задач, которые могут быть эффективно распараллелены, поскольку они позволяют программе выполнять множество операций одновременно. Однако, поскольку потоки делят память, разработчику необходимо тщательно управлять доступом к общим данным, чтобы избежать условий гонки и других проблем синхронизации.
Процессы изолированы друг от друга, в то время как потоки делят состояние и ресурсы внутри одного процесса.
Каждый процесс имеет собственное адресное пространство, в то время как все потоки внутри процесса делят его адресное пространство.
Создание нового процесса более ресурсоемко, чем создание потока внутри существующего процесса.
Взаимодействие между процессами требует использования межпроцессного взаимодействия (IPC), такого как сокеты, разделяемая память, очереди сообщений и т. д. Потоки внутри процесса могут общаться друг с другом напрямую через общую память.
Ошибка в одном процессе обычно не влияет на другие процессы, но ошибка в одном потоке может привести к сбою всего процесса.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Нельзя напрямую взять ссылку на значение, хранящееся по ключу в карте (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
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
В Go существует несколько основных типов, которые можно разделить на следующие категории: базовые типы, агрегированные типы, ссылочные типы и интерфейсы. Рассмотрим каждую категорию более подробно.
Целые числа:
int (размер зависит от платформы):int8, int16, int32, int64 ; uint (беззнаковый, размер зависит от платформы) ; uint8 (также известен как byte), uint16, uint32, uint64
Числа с плавающей запятой:float32, float64
Комплексные числа:complex64, complex128
bool
(значения true
и false
)byte
(аналог uint8
)rune
(аналог int32
, используется для представления символов Unicode)✅
string
(последовательность байт, используемая для хранения текста)Фиксированный размер:
[n]T
(например, [5]int
для массива из пяти целых чисел)Переменный размер:
[]T
(например, []int
для среза целых чисел)Набор полей:
struct
(например, type Person struct { Name string; Age int }
)Хранит адрес переменной:
*T
(например, *int
для указателя на целое число)Хранит пары ключ-значение:
map[K]V
(например, map[string]int
для карты, где ключи - строки, а значения - целые числа)Определяет тип функции:
func(параметры) возвращаемые_типы
(например, func(int) int
для функции, принимающей целое число и возвращающей целое число)Определяет методы, которые должны быть реализованы:
interface{}
(например, type Stringer interface { String() string }
)package main
import "fmt"
// Определение структуры
type Person struct {
Name string
Age int
}
// Функция, использующая интерфейс
func (p Person) String() string {
return fmt.Sprintf("%s (%d years old)", p.Name, p.Age)
}
func main() {
// Пример использования базовых типов
var age int = 30
var name string = "Alice"
var isStudent bool = false
// Пример использования агрегированных типов
var scores [3]int = [3]int{90, 85, 88}
var people []Person = []Person{
{Name: "Bob", Age: 25},
{Name: "Charlie", Age: 35},
}
// Пример использования ссылочных типов
var p *Person = &Person{Name: "Dave", Age: 40}
var ageMap map[string]int = map[string]int{"Alice": 30, "Bob": 25}
// Вывод данных
fmt.Println("Name:", name)
fmt.Println("Age:", age)
fmt.Println("Is student:", isStudent)
fmt.Println("Scores:", scores)
fmt.Println("People:", people)
fmt.Println("Pointer to Person:", p)
fmt.Println("Age map:", ageMap)
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
- UInt — беззнаковое целое число, только 0 и положительные значения.
UInt полезен, если ты точно знаешь, что число не может быть отрицательным (например, размер массива). Однако арифметические операции между Int и UInt требуют явного преобразования.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
Go (или Golang) обладает рядом преимуществ, которые делают его популярным.
Был разработан с упором на простоту синтаксиса и лаконичность. Это делает язык легким для изучения и чтения кода. Минимализм языка позволяет разработчикам сосредоточиться на решении задач, а не на сложностях синтаксиса.
Компилируется в машинный код, что обеспечивает высокую производительность выполнения программ. Производительность Go сопоставима с производительностью программ, написанных на C или C++, благодаря низкоуровневой оптимизации компилятора.
Одним из ключевых преимуществ является встроенная поддержка параллелизма и конкурентности. С помощью горутин и каналов разработчики могут легко создавать многопоточные приложения.
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 3; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world")
say("hello")
}
Имеет строгую систему типов, которая помогает предотвращать ошибки на этапе компиляции. В языке отсутствуют неявные преобразования типов, что снижает вероятность ошибок. Также Go управляет памятью с помощью встроенного сборщика мусора (garbage collector), что предотвращает утечки памяти.
Стандартных инструментов
Поставляется с богатым набором встроенных инструментов для разработки, таких как:
go fmt
для автоматического форматирования кода.go test
для запуска тестов.go build
и go run
для сборки и выполнения программ.go doc
для генерации документации.Поддерживает компиляцию кода для различных платформ и операционных систем. Это делает его удобным для разработки кроссплатформенных приложений.
Обширна и покрывает многие аспекты разработки, такие как работа с сетью, работа с файлами, веб-разработка и многое другое. Это позволяет разработчикам быстро начинать работу, не тратя время на поиск и интеграцию сторонних библиотек.
Имеет большое и активное сообщество, а также поддерживается компанией Google. Это гарантирует наличие множества ресурсов для обучения и решения возникающих вопросов, а также активное развитие и улучшение языка.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM