Самый эффективный способ — избегать многократной конкатенации, особенно в цикле, потому что каждая операция создаёт новую строку. Вместо этого используют буферы или строчные сборщики, которые работают с выделенной памятью и собирают результат за один проход. Это снижает количество аллокаций и копирований.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Это процесс копирования и поддержания данных в синхронном или асинхронном порядке между несколькими базами данных или серверами. Это позволяет данным быть доступными на различных серверах, что увеличивает доступность, устойчивость к сбоям и может улучшить производительность приложений за счёт распределения нагрузки на разные серверы или узлы.
Позволяет системе продолжать работать даже при сбое одного или нескольких серверов. При наличии нескольких копий данных, система может переключиться на использование актуальной копии данных на другом сервере.
Может помочь распределить запросы чтения между несколькими узлами, тем самым уменьшая нагрузку на один сервер и улучшая время отклика в приложениях.
Реплик в разных географических локациях может улучшить время доступа к данным для пользователей, которые находятся ближе к одной из реплик.
Данных на разные физические места уменьшает риски, связанные с потерей данных в случае катастроф.
Данные одновременно записываются в основную и реплицированную базы данных. Транзакция считается завершенной только после успешной записи на всех репликах. Это обеспечивает высокую степень согласованности данных, но может снижать производительность из-за задержек, связанных с ожиданием подтверждения от всех реплик.
Изменения данных первоначально записываются на основной сервер, и только после этого асинхронно передаются на репликационные серверы. Это метод быстрее, поскольку основная система не ждёт подтверждения от реплик перед завершением транзакции. Однако это также увеличивает риск несогласованности данных между репликами.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
2. Ёмкость (cap): максимальное количество элементов, которые слайс может вместить без перевыделения памяти.
3. При добавлении элементов с помощью append длина увеличивается, а если она превышает ёмкость, выделяется новый блок памяти с удвоенной ёмкостью.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Концепции объектно-ориентированного программирования (ООП) реализованы несколько иначе, чем в традиционных ООП-языках, таких как Java или C++. Не использует классы, наследование и полиморфизм на основе классов в привычном понимании. Вместо этого он применяет интерфейсы, структуры и встраивание для достижения гибкости и мощи ООП.
Основной способ организации и капсуляции данных — это структуры (
structs
). Структуры объединяют данные в одну сущность, но в отличие от классов, они не включают определение методов внутри себя. Вместо этого методы определяются отдельно и ассоциируются со структурой через определение получателя метода.type Person struct {
Name string
Age int
}
func (p Person) Greet() string {
return "Hello, my name is " + p.Name
}
Реализуется через интерфейсы. Интерфейс — это набор сигнатур методов. Тип считается реализующим интерфейс, если он имеет все методы, указанные в интерфейсе. Важной особенностью интерфейсов в Go является то, что типы могут удовлетворять интерфейсам неявно, без специального объявления.
type Greeter interface {
Greet() string
}
func GreetSomeone(g Greeter) {
fmt.Println(g.Greet())
}
Один из способов реализации композиции — встраивание структур. Можно встроить одну структуру в другую, что позволяет делегировать часть работы встроенной структуре.
type Employee struct {
Person
Position string
}
func (e Employee) Work() string {
return e.Name + " is working as a " + e.Position
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3💊1
В Go строки хранятся в UTF-8, что позволяет корректно работать с символами любых языков (включая иероглифы, кириллицу и т.д.).
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
В Go для проверки работы горутины часто используют каналы. Они позволяют передавать данные между горутинами и могут использоваться для сигнализации о состоянии выполнения горутины.
Горутина может отправлять сигнал (например,
true
) в канал, чтобы уведомить о своем завершении.func worker(done chan bool) {
fmt.Println("Работа началась...")
time.Sleep(2 * time.Second) // Имитация работы
fmt.Println("Работа завершена!")
done <- true // Отправляем сигнал в канал
}
func main() {
done := make(chan bool)
go worker(done) // Запускаем горутину
// Ожидаем сигнал завершения
<-done
fmt.Println("Основной поток: горутина завершена")
}
Горутина может отправлять данные в канал, чтобы сигнализировать о прогрессе выполнения.
func worker(progress chan int) {
for i := 1; i <= 5; i++ {
fmt.Printf("Шаг %d выполнен\n", i)
progress <- i // Отправляем номер шага в канал
time.Sleep(500 * time.Millisecond) // Имитация работы
}
close(progress) // Закрываем канал после завершения работы
}
func main() {
progress := make(chan int)
go worker(progress) // Запускаем горутину
// Считываем данные из канала
for step := range progress {
fmt.Printf("Получен сигнал: шаг %d завершен\n", step)
}
fmt.Println("Все шаги выполнены!")
}
Если важно знать, работает ли горутина, но при этом нужно ограничить ожидание, используется оператор
select
с тайм-аутом.func worker(status chan string) {
time.Sleep(2 * time.Second) // Имитация работы
status <- "Горутина завершена"
}
func main() {
status := make(chan string)
go worker(status)
select {
case msg := <-status:
fmt.Println(msg)
case <-time.After(1 * time.Second): // Тайм-аут 1 секунда
fmt.Println("Горутина работает слишком долго")
}
}
Закрытие канала может служить сигналом того, что горутина завершила свою работу.
func worker(done chan struct{}) {
fmt.Println("Горутина работает...")
time.Sleep(2 * time.Second)
fmt.Println("Горутина завершена!")
close(done) // Закрываем канал
}
func main() {
done := make(chan struct{})
go worker(done)
// Проверяем, когда канал закроется
<-done
fmt.Println("Основной поток: горутина завершила работу")
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍4
Удаление из начала слайса требует сдвига всех оставшихся элементов на один шаг влево. Это операция линейной сложности — O(n), где n — количество элементов после удалённого.
В отличие от удаления с конца, которое обычно O(1), начало требует перераспределения памяти или сдвига указателей.
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔4
Нет, карты (maps) в Go не являются потокобезопасными по умолчанию. Использование карты в нескольких горутинах без должной синхронизации может привести к состояниям гонки, некорректным данным и паникам. Давайте рассмотрим более детально, почему это так и как можно обеспечить потокобезопасность при работе с картами в многопоточной среде.
Карты в Go разработаны для обеспечения высокой производительности при использовании в однопоточных сценариях. Если несколько горутин одновременно пытаются читать и изменять карту, возникает конкуренция за доступ к данным, что может привести к следующим проблемам:
Одновременный доступ к карте из нескольких горутин может привести к состояниям гонки, когда порядок выполнения операций непредсказуем.
Одновременное изменение структуры карты, например добавление или удаление элементов, может вызвать панику в программе.
Самый распространенный способ синхронизации доступа к карте — использование
sync.Mutex
. Мьютексы позволяют заблокировать доступ к карте на время чтения или записи.package main
import (
"fmt"
"sync"
)
func main() {
var mu sync.Mutex
m := make(map[string]int)
var wg sync.WaitGroup
write := func(key string, value int) {
mu.Lock()
m[key] = value
mu.Unlock()
}
read := func(key string) int {
mu.Lock()
defer mu.Unlock()
return m[key]
}
wg.Add(2)
go func() {
defer wg.Done()
write("key1", 42)
}()
go func() {
defer wg.Done()
fmt.Println(read("key1"))
}()
wg.Wait()
}
Если в вашей программе чаще происходят операции чтения, чем записи, можно использовать
sync.RWMutex
, который позволяет нескольким горутинам читать данные одновременно, но обеспечивает эксклюзивный доступ для записи.package main
import (
"fmt"
"sync"
)
func main() {
var mu sync.RWMutex
m := make(map[string]int)
var wg sync.WaitGroup
write := func(key string, value int) {
mu.Lock()
m[key] = value
mu.Unlock()
}
read := func(key string) int {
mu.RLock()
defer mu.RUnlock()
return m[key]
}
wg.Add(2)
go func() {
defer wg.Done()
write("key1", 42)
}()
go func() {
defer wg.Done()
fmt.Println(read("key1"))
}()
wg.Wait()
}
Go предоставляет специальную структуру
sync.Map
, которая изначально создана для безопасного использования в многопоточной среде. Она автоматически обеспечивает синхронизацию операций.package main
import (
"fmt"
"sync"
)
func main() {
var m sync.Map
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
m.Store("key1", 42)
}()
go func() {
defer wg.Done()
value, _ := m.Load("key1")
fmt.Println(value)
}()
wg.Wait()
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Функция append используется для добавления элементов к слайсу. Эта функция может увеличивать длину слайса и, при необходимости, выделять новый подлежащий массив, если текущий массив не имеет достаточной емкости для хранения новых элементов.
Если текущий подлежащий массив слайса имеет достаточно места (емкости) для добавления новых элементов,
append
просто добавляет элементы к существующему массиву.Если емкость текущего массива недостаточна для размещения новых элементов,
append
выделяет новый массив, копирует в него существующие элементы и добавляет новые элементы. Новый массив будет иметь увеличенную емкость (как правило, в два раза больше, чем предыдущая).Синтаксис
slice = append(slice, elem1, elem2, ...)
Пример использования
package main
import "fmt"
func main() {
// Создаем слайс из 3 целых чисел
slice := []int{1, 2, 3}
// Добавляем один элемент
slice = append(slice, 4)
// Добавляем несколько элементов
slice = append(slice, 5, 6, 7)
// Выводим слайс
fmt.Println(slice) // Выводит: [1 2 3 4 5 6 7]
}
Создаем слайс с тремя элементами
[1, 2, 3]
.slice := []int{1, 2, 3}
Добавляем элемент
4
к слайсу. Теперь слайс содержит [1, 2, 3, 4]
.slice = append(slice, 4)
Добавляем элементы
5
, 6
, и 7
. Теперь слайс содержит [1, 2, 3, 4, 5, 6, 7]
.slice = append(slice, 5, 6, 7)
Выводим слайс, который содержит
[1, 2, 3, 4, 5, 6, 7]
.fmt.Println(slice)
Работа с емкостью и длиной
package main
import "fmt"
func main() {
slice := []int{1, 2, 3}
fmt.Printf("Before append: len=%d cap=%d %v\n", len(slice), cap(slice), slice)
slice = append(slice, 4)
fmt.Printf("After append: len=%d cap=%d %v\n", len(slice), cap(slice), slice)
slice = append(slice, 5, 6, 7)
fmt.Printf("After multiple appends: len=%d cap=%d %v\n", len(slice), cap(slice), slice)
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
💊7👍1
Триггер — это специальная процедура в СУБД, которая автоматически выполняется при определённом событии (например,
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