├── .github └── workflows │ └── update-content.yaml ├── LICENSE ├── README.md └── content ├── 1.intro.md ├── 2.hr-softskills ├── 1.index.md ├── easter-egg.md ├── formal.md ├── future.md ├── people.md ├── planning.md └── work.md ├── 3.golang ├── 1.array-slice.md ├── 2.strings.md ├── 3.hashmap.md ├── 4.interface.md ├── 5.links.md ├── 6.goroutines.md ├── 7.context.md ├── defer.md ├── errors-panics.md ├── garbage-collector.md ├── generics.md ├── overall.md ├── package.md ├── race-condition.md ├── sheduler.md ├── structs.md └── sync-primitives.md ├── 4.infrastructure ├── _category_.json ├── db-relation.md ├── docker.md ├── git.md ├── kubernetes.md ├── redis.md └── Брокеры сообщений │ ├── _category_.json │ ├── kafka.md │ └── overall.md ├── 5.systemdesign ├── _category_.json ├── data-management.md ├── load-balancing.md └── microservices.md ├── 6.patterns ├── _category_.json ├── gangoffour.md └── solid.md └── 7.systems ├── _category_.json └── linux ├── _category_.json ├── filesystem.md ├── overall.md └── signals.md /.github/workflows/update-content.yaml: -------------------------------------------------------------------------------- 1 | name: 'update-content' 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | 17 | - name: Pushes to another repository 18 | uses: cpina/github-action-push-to-another-repository@main 19 | env: 20 | API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }} 21 | with: 22 | source-directory: 'content' 23 | target-directory: 'content' 24 | destination-github-username: 'backends-wiki' 25 | destination-repository-name: 'website' 26 | user-email: auto-update@backends.wiki 27 | target-branch: main -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Alexander 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # backends.wiki - база знаний для подготовки к собеседованиям. 2 | 3 | Данный репозиторий содержит весь контент с сайта Backends.wiki. 4 | 5 | [Ссылка на сайт с общей базой данных](https://backends.wiki) 6 | 7 | ## Помочь с проектом 8 | 9 | Вы всегда можете помочь проекту, добавив новые вопросы или исправив ошибки в существующих. 10 | 11 | Для этого нужно сделать следующее: 12 | 13 | - Сделать форк репозитория 14 | - Внести изменения 15 | - Создать пулл реквест 16 | 17 | Либо создать [issue](https://github.com/backends-wiki/content/issues) с описанием ошибки или нового вопроса, желательно с ответом и/или примерами кода. 18 | -------------------------------------------------------------------------------- /content/1.intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'О проекте' 3 | description: '' 4 | --- 5 | 6 | В один момент я начал готовиться и ходить по собеседованиям на позицию Golang разработчика. 7 | В этом репозитории я собираю вопросы, которые мне задавали на собеседованиях, а также вопросы, которые я нашел в интернете и которые мне показались интересными. 8 | 9 | Помните, подготовка к собеседованию - это не только знание ответов на вопросы, но и умение решать задачи. 10 | 11 | # Чатик в телеграме 12 | [Кликни на меня](https://t.me/backendswiki_chat) 13 | 14 | # Помочь с проектом 15 | Вы всегда можете помочь проекту, добавив новые вопросы или исправив ошибки в существующих. 16 | 17 | Для этого нужно сделать следующее: 18 | - Сделать форк репозитория 19 | - Внести изменения 20 | - Создать пулл реквест 21 | 22 | Либо создать [issue](https://github.com/backends-wiki/conten/issues) с описанием ошибки или нового вопроса, желательно с ответом или примерами кода. 23 | 24 | Так же не забываем, о моральной помощи в виде звездочки у [репозитория](https://github.com/backends-wiki/conten) ⭐️ 25 | -------------------------------------------------------------------------------- /content/2.hr-softskills/1.index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'HR Soft Skills' 3 | description: '' 4 | navigation: false 5 | --- 6 | 7 | Раздел посвящен вопросам, которые могут быть заданы на собеседовании на позицию Backend разработчика, но не относятся к технической части. 8 | -------------------------------------------------------------------------------- /content/2.hr-softskills/easter-egg.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Вопросы с подвохом 3 | sidebar_position: 5 4 | --- 5 | 6 | ## Чем вы конкретно занимались 7 | 8 | ### Зачем спрашивают? 9 | Если вы ответили фигово на вопрос: "Что вы делали на прошлом месте" , то собеседующим не понятно, чем именно занимались вы. Есть сомнения, что вы делали ценную для этой вакансии работу.. 10 | 11 | ### Как ответить? 12 | Делайте акценты на задачах, которые релевантны текущей вакансии. 13 | Надежный способ успокоить интервьюера - сказать "70% времени разрабатывал новый функционал, 20% - багфиксы и продумывание архитектуры, 10% митинги и бюрократия" 14 | 15 | ### Как не стоит отвечать 16 | "Половину времени настраивал среду для работу, доступы, участвовал в митингах" 17 | 18 | ## Расскажите о самых сложных проблемах, над которыми вы работали. Расскажите в подробностях, как вы их решали 19 | 20 | ### Зачем спрашивают? 21 | 1. Умение работать в нестандартных условиях или в сжатые сроки. 22 | 2. Понять реальный уровень компетенций. Без опыта невозможно подробно рассказать о проблемах и нюансах. 23 | 3. Понять насколько сложные перед вами стояли задачи, как вы их решали 24 | 25 | ### Как ответить? 26 | Здесь хотят услышать подход к решению проблем. 27 | 1. Вспоминайте какую-то срочную, сложную задачу. 28 | 2. Объясняете, почему она важна и почему сложна. 29 | 3. Рассказываете, как разбирались в проблеме 30 | 4. Как в итоге решили 31 | 32 | ### Как не стоит отвечать 33 | Любой отказ рассказывать будет воспринят негативно. 34 | Случай, который в решили в течение дня - недостаточно круто прозвучит. 35 | 36 | ## У вас есть предложения от других компаний? 37 | 38 | ### Зачем спрашивают? 39 | Рекрутеры задают этот вопрос чтобы узнать сколько у них времени на обратную связь. Также пытаются понять насколько вы востребованы и есть ли у работодателя конкуренты. 40 | 41 | ### Как ответить? 42 | “Да, есть другие интересные предложения, но хочу рассмотреть ваш вариант, потому что..." Подумайте и запишите ответ, например: у вас крутой проект, сильная команда, правильная культура. 43 | 44 | Работодатель должен чувствовать, что конкурирует с другими предложениями. И что у него мало времени на хантинг. Как в маркетинге и продажах это подталкивает человека захотеть “купить” здесь и сейчас. 45 | 46 | Если есть офферы - говорите, что компании ждут ответа до определенной даты. Если нет - говорите, что есть. Никто не имеет права требовать назвать имя компании, расскажите в общем - этого достаточно. 47 | 48 | ### Как не стоит отвечать 49 | 1. Не говорите что получили уже 15 предложений и будете выбирать. Во-первых, непонятно сколько времени уйдет на выбор. Во-вторых, вы говорите работодателю чтобы он не рассчитывал на вас, так как конкурентов много. 50 | 2. Не критикуйте другие предложения, это понизит вашу ценность. Работодатель перестает чувствовать конкуренцию и понимает что можно прогибать, например по деньгам. 51 | 3. Не говорите, что постоянно в поиске и что регулярно зовут на собеседования. Работодатель подумает, что вы так же легко уйдете из его компании. 52 | 53 | ## Какая ЗП у вас на текущей работе? 54 | 55 | ### Зачем спрашивают? 56 | 1. HR или будущий руководитель ищет повод чтобы поторговаться и скинуть оклад, прежде чем сделать оффер. 57 | 2. Если текущая ЗП больше того, что они готовы предложить -закончить собеседование и сэкономить всем время. 58 | 59 | ### Как ответить? 60 | Называйте сумму в 85-90% от той, которую вы хотите получить в оффере. Например, сейчас получаете 50 т.р., верите, что эта компания согласиться на 120 т.р. - отвечайте “сейчас получаю 100 т.р.”. 61 | 62 | ### Как не стоит отвечать 63 | - "Это коммерческая тайна" 64 | - "А какая вам разница?" 65 | -------------------------------------------------------------------------------- /content/2.hr-softskills/formal.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Формальность 3 | sidebar_position: 3 4 | --- 5 | 6 | ## Как вы относитесь к командировкам? 7 | 8 | ### Зачем спрашивают? 9 | Подвоха тут нет. 10 | Хотят понять можно ли отправлять вас по командировкам и как часто 11 | 12 | ### Как ответить? 13 | Отвечайте как есть. Отличный повод узнать, часто ли нужно будет ездить. 14 | Если вам не подходит - лучше не идти к ним. 15 | 16 | ### Как не стоит отвечать 17 | Не надо врать или приукрашивать 18 | "Если X2 к ЗП + командировочные + доп отпуск , то подумаю" - осторожно узнайте про условия, но не вставайте в аггрессивную позу сразу 19 | 20 | ## Когда вы готовы выйти на работу? 21 | 22 | ### Зачем спрашивают? 23 | Работодатель проверяет насколько активно вы ищете работу. Говорите как есть. 24 | Обычно, компаниям не интересно ждать 3 месяца, пока вы закончите проект и передадите дела. К тому же, на практике таких кандидатов можно не дождаться. 25 | 26 | ### Как ответить? 27 | 1) Если Вы решили выйти на работу как можно быстрее, так и говорите: 28 | Я готов выйти на работу уже с завтрашнего числа. Когда вы хотите, чтобы я начал? 29 | 30 | 2) Если же Вам надо отрабатывать 2 недели на старой работе, говорите точную дату, с которой сможете начать работать. 31 | 32 | ### Как не стоит отвечать 33 | "Я не знаю, я ещё не говорил об этом со своим руководством" - нужна уверенность, иначе сделают пометку "сам не знает когда выйдет, будет мозг иметь" -------------------------------------------------------------------------------- /content/2.hr-softskills/future.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Ваши планы на будущее 3 | sidebar_position: 2 4 | --- 5 | 6 | ## Что вы ищите сейчас? Опишите свое идеальное место работы. Что вы надеетесь получить от этой работы? 7 | 8 | ### Зачем спрашивают? 9 | 1. Узнать ожидания кандидата 10 | 2. На что кандидат обращает внимание при выборе компании 11 | 3. Насколько текущие условия подойдут кандидату 12 | 13 | ### Как ответить? 14 | Ключевые пункты: 15 | 1. Продвинутые инструменты и технологии 16 | 2. Развитые процессы в компании/команде, крутая команда команды 17 | 3. Полезность и амбициозность проекта/продукта 18 | 4. Обучение сотрудников 19 | 20 | По ходу рассказа связывайте эти пожелания с компанией, куда собеседуетесь. 21 | Нужно как в продажах - говорить больше о клиенте и меньше о себе. Убеждайте, что вам нравятся культура, проекты, люди, которым приносят пользу эти проекты, подход компании к работе и сотрудникам. 22 | Ищите зацепки в описании вакансии или спрашивайте у HR при первом созвоне. 23 | 24 | Пример ответа: 25 | “Хочу работать с современным стеком технологий, видел что у вас React, Redux, Saga, Typescript и Git - это радует. 26 | Мне важно знать, что я приношу пользу людям. На вашем проекте я буду в этом уверен. 27 | Также не хотелось бы тратить время на компанию с неразвитими организационными процессами" 28 | 29 | ### Как не стоит отвечать 30 | Самое нелепое, что может быть, это если из вашего ответа будет следовать, что вам интересна совсем другая работа, чем вакансия, по поводу которой вы сейчас пришли на собеседование. 31 | 32 | "Оплачиваемое рабочее место, с ДМС и отпуском" - иными словами, "да мне пофиг", такими людьми только дырки затыкают, но всерьез не воспринимают. 33 | 34 | ## Какой вы видите свою будущую команду 35 | 36 | ### Зачем спрашивают? 37 | 38 | 1. В какой команде вы привыкли работать 39 | 2. Что для вас идеальная команда 40 | 3. Какую роль в команде вы хотите выполнять 41 | 42 | ### Как ответить? 43 | 44 | Здесь нужно попасть в "ожидания" того кто спрашивает. 45 | 46 | "Хочу команду: 47 | 1. В которой мы вместе сможем развиваться 48 | 2. С которой мы вместе будем делать ваш классный мега продукт 49 | 3. С которой мы вместе выстроим самые эффективные процессы 50 | 51 | ### Как не стоит отвечать 52 | 53 | 1. "Ну хочу чтобы там были все кто нужен, и чтобы мне нужно было только писать код" - показывает что вы не хотите развиваться 54 | 2. "Хочу чтобы были сильные senior, у которых я буду спрашивать как сделать" - нахер вы вообще нужны? 55 | 3. "Хочу чтобы слушали меня всегда, и не мешали работать" 56 | 57 | ## Каким вы видите своего будущего начальника 58 | 59 | ### Зачем спрашивают? 60 | 61 | На какие качества вы больше всего обращаете внимание, узнать, что вам важно. 62 | Какой формат работы вы считаете правильным. 63 | 64 | ### Как ответить? 65 | 66 | Хочется работать на человека с амбициями, который хочет развиваться и делать классные проекты. 67 | В работе с своей команды жду взаимоуважения, желания доверять мне сложные задачи, честности и поддержки. 68 | 69 | Каждый пункт руководитель должен мысленно отмечать в голове "да, я такой, я молодец)))". Если, наоборот, босс - унылое говно, может и не надо к нему идти? 70 | 71 | ### Как не стоит отвечать 72 | 73 | "Руководитель дает мне задачу и не вмешивается, отчетность я сдам в конце месяца по результатам работы." 74 | "Чтобы не дергал лишний раз" 75 | "Дает только точные продуманные задачи" 76 | 77 | ## Чем вас заинтересовала наша вакансия? 78 | 79 | ### Зачем спрашивают? 80 | 81 | 1. Хотите ли вы у них работать. 82 | 2. Что именно вас привлекло в вакансии. 83 | 3. Понимаете ли вы, чем нужно будет заниматься. 84 | 85 | ### Как ответить? 86 | 87 | Уже обсуждалось. 88 | Основные моменты, которые можно упомянуть: 89 | 1. Инструменты и технологии 90 | 2. Процессы в компании/команде, состав команды 91 | 3. Цели проекта/продукта 92 | 4. Обучение сотрудников 93 | 5. Чем предстоит заниматься 94 | 6. Каким видят идеального кандидата. Например, “многозадачен” 95 | 7. Какие задачи стоят в ближайшее время: “оптимизация системы” 96 | 97 | Пример ответа: 98 | “У вас используются React, Redux, Saga, Typescript и Git - со всем этим работал и поэтому подхожу на эту роль. Вы работаете удаленно, а я уже давно работаю на удаленке и поэтому продуктивно включусь в процесс. У вас небольшая продуктовая команда, поэтому нужен человек, который сможет настроить CI, среду, что-то сверстать, я как раз могу этим заняться.” 99 | 100 | ### Как не стоит отвечать 101 | 102 | "Руководитель дает мне задачу и не вмешивается, отчетность я сдам в конце месяца по результатам работы." 103 | "Чтобы не дергал лишний раз" 104 | "Дает только точные продуманные задачи" 105 | 106 | 107 | ## Почему нам стоит нанять именно Вас? Почему вы хотите работать именно у нас? Как вы видите развитие своей карьеры и какие навыки вы хотели бы освоить, являясь нашим сотрудником 108 | 109 | ### Зачем спрашивают? 110 | 1. Услышать что-то новое и ключевое о кандидате, что повлияет на решение. 111 | 2. Оценить способность аргументировать и убеждать. 112 | 3. Понять, а сильно ли кандидат заинтересован в работе. 113 | 114 | ### Как ответить? 115 | Нужно как в продажах - говорить больше о клиенте и меньше о себе. Убеждайте, что вам нравятся культура, проекты, люди, которым приносят пользу эти проекты, подход компании к работе и сотрудникам. 116 | 117 | Выпишите себе следующие пункты и найдите на них ответы. Ищите в описании вакансии или спрашивайте у HR при первом созвоне. Много инфы можно накопать почитав о компании: 118 | 1. Их инструменты и технологии 119 | 2. Как устроены процессы в компании/команде, состав команды 120 | 3. Цели проекта/продукта 121 | 4. Обучение сотрудников 122 | 5. Чем предстоит заниматься 123 | 6. Каким видят идеального кандидата.Например, “многозадачен” 124 | 7. Какие задачи стоят в ближайшее время: “оптимизация системы” 125 | 126 | Пример ответа: 127 | “У вас используются React, Redux, Saga, Typescript и Git - со всем этим работал и поэтому подхожу на эту роль. Вы работаете удаленно, а я уже давно работаю на удаленке и поэтому продуктивно включусь в процесс. У вас небольшая продуктовая команда, поэтому нужен человек, который сможет настроить CI, среду, что-то сверстать, я как раз могу этим заняться.” 128 | 129 | ### Как не стоит отвечать 130 | 1. “Хочу как можно быстрее вырасти в лида, поэтому буду очень стараться” - не факт что работодателю это нужно. Будьте аккуратны рассказывая о амбициях. 131 | 2. ”Сами скажите”, ”А я пока не знаю”, “Это вы меня позвали” - никто не любит неподготовленных выпендрежников. 132 | 3. “Я педантичный, дотошный, мотивированный, ответственный, коммуникабельный и бла бла бла…” - не интересно, не информативно. 133 | 4. "Буду работу работать" 134 | -------------------------------------------------------------------------------- /content/2.hr-softskills/people.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Работа с людьми 3 | sidebar_position: 1 4 | --- 5 | 6 | ## Как вы действуете в конфликтной ситуации на работе? 7 | 8 | ### Зачем спрашивают? 9 | 1. Отсеять опасных неадекватов. Да, в IT тоже иногда бьют друг другу лица. 10 | 2. Отсеять проблемных сотрудников и исключить ходячую головную боль. 11 | 3. Действительно хотят понять как вы будете действовать в конфликтной ситуации, есть ли у вас навык выхода из них. 12 | 4. Прощупывают ваши способности строить отношения в принципе, что кратно влияет на эффективность и раскрывается в вашем ответе на вопрос. 13 | 14 | ### Как ответить? 15 | 16 | 1. Говорить, что вы развивали конфликт не стоит. 17 | 2. Показывайте, что вы за мирное решение и что с вашим приходом конфликтов станет меньше. 18 | 3. Расскажите, что попав в конфликт вы: 19 | - Ставите себя на место другой стороны. 20 | - “Открыты к разговору” 21 | - Готовы признавать ошибки. 22 | - Находите компромисс в диалоге. 23 | - Просите совета у руководителя и других коллег, если нужно. 24 | - Заранее строите доверительные отношения с людьми, чтобы конфликтов было меньше, 25 | - Только если предыдущие пункты не сработали, а проблема как риск для проекта осталась, обращаетесь за помощью. 26 | 4. Интервьюер должен услышать, что победила дружба. 27 | 5. Без заготовленного заранее кейса вам будет сложно на ходу правильно себя показать - запишите пару ситуаций. 28 | 29 | ### Как не стоит отвечать 30 | - “После работы я поговорил бы с ним как мужчина с мужчиной” 31 | - “Выскажу руководителю в лицо все что о нем думаю” 32 | - “Почти всегда я прав, нужно это доказать любыми силами” 33 | 34 | 35 | ## Как бы вы объяснили своей бабушке - кем вы работаете? 36 | 37 | ### Зачем спрашивают? 38 | Навыки коммуникации и взаимодействия 39 | Иногда бывает важно понять - насколько кандидат может излагать свои мысли доступным языком, а не в технических терминах. 40 | 41 | ### Как ответить? 42 | Постарайтесь дать максимально лёгкий и доступный для понимания ответ 43 | 44 | ### Как не стоит отвечать 45 | Начать объяснять теорию так, как написано в умных книжках 46 | 47 | ## Что вы будете делать если руководство в обход вашего менеджера ставит вам срочные задачи 48 | 49 | ### Зачем спрашивают? 50 | 51 | Ответ на вопрос покажет ваше отношение к такому стилю работы. Существуют адекватные способы с этим работать. Но умеете ли вы? 52 | 53 | ### Как ответить? 54 | 55 | "Это нередкая и нормальная ситуация, с которой можно работать. 56 | Мои задачи при этом: 57 | 1) Уметь перестроиться и быстро дать результат, когда это нужно 58 | 2) Показывать прозрачность моей работы, чтобы мой руководитель легко и эффективно принмал решение что важнее 59 | 3) Оповещать всех, кто ждет результата моей работы, об изменениях в приоритетах и сроках 60 | 61 | Если приносят очевидно сверхсрочную задачу - оповещу остальных и руководителя, а затем пойду и сделаю. 62 | Если дают умеренно срочную задачу - обсужду с руководителем, затем сообщу о сроке решения. 63 | Когда нет возможности посоветоваться - расспрошу побольше и сам изменю приоритеты. 64 | В КАЖДОМ таком случае буду напоминать, что продуктивнее ставить задачи через моего руководителя. 65 | 66 | ### Как не стоит отвечать 67 | 68 | - "Буду работать 24 на 7" 69 | - "Уволюсь 70 | 71 | ## В вашей команде каждый день меняются приоритеты, появляются новые срочные задачи. Что вы будете с этим делать? 72 | 73 | ### Зачем спрашивают? 74 | 75 | Такие ситуации действительно бывают. Главное чтобы это не было постоянным режимом работы. 76 | 1. Умеете ли вы договариваться с другими людьми в компании 77 | 2. Как вы приоритезируете свои задачи 78 | 79 | ### Как ответить? 80 | 81 | Обсуждаю с менеджером ситуацию. Подсвечиваю риски по текущим задачм, рассказываю сколько осталось. Если решаем что задача действительно горит - оповещаю остальных что их задача будет сделана позже, объясняю почему. 82 | 83 | ### Как не стоит отвечать 84 | 85 | [Клик](https://www.youtube.com/watch?v=eYBwY6zKSNk) 86 | 87 | ## Если бы мы попросили ваших коллег рассказать про вас, чтобы мы услышали? Как бы описали вас другие разработчики / менеджеры проектов, с которыми вы работали? 88 | 89 | ### Зачем спрашивают? 90 | 91 | 1. Оценивается умение и стремление работать в команде. 92 | 2. Собираете ли вы фидбек по своей работе 93 | 3. Осознаете ли моменты, где вы недостаточно хорошо справляетесь 94 | 95 | ### Как ответить? 96 | 97 | "Я переодически спрашиваю про это своего менеджера, тим лида, ментора. Получаю конструктивный фидбек. Им нравится то что я постоянно развиваюсь; отвественно отношусь к свои задачам и срокам; стараюсь во благо проекта; всегда готов помочь коллегам;" 98 | 99 | ### Как не стоит отвечать 100 | Не говорите про негатив - вообще. 101 | Рассказывать, почему вам никогда не давали фидбек - не надо. -------------------------------------------------------------------------------- /content/2.hr-softskills/planning.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Планирование 3 | sidebar_position: 4 4 | --- 5 | 6 | ## Какими будут ваши первые шаги на новом месте работы? 7 | 8 | ### Зачем спрашивают? 9 | Очень сложный вопрос. Задают кандидатам на позиции senior и выше. 10 | Ожидают, что вы покажете полное понимание ситуации (хоть и без деталей). 11 | Критично попасть в их ожидания. 12 | 13 | ### Как ответить? 14 | К такому вопросу нужно готовиться. Выянсите потребности работодателя: 15 | 1. Изучите описание вакансии 16 | 2. Распросите рекрутера при первом созвоне 17 | 3. Задавайте вопросы на собеседовании 18 | 19 | Основные направления, над которыми нужно работать в любой компании: 20 | 1. Техника - аудит системы 21 | 2. Понять что по команде. Кого обучать, кого нанимать 22 | 3. Выстроить базовые внутренние и внешние отношения 23 | 4. Инфраструктура, DevOps 24 | 5. План разработки - сроки, стоимость, состав команды 25 | 26 | Костяк, шаблон ответа можно заготовить заранее. 75% повторяется, надо потренироваться. 27 | 28 | ### Как не стоит отвечать 29 | - "Не знаю" 30 | - "Буду делать что скажут." 31 | - "Все нахрен переделаю, вы говно, я знаю как надо - научу" 32 | 33 | ## Как вы оцениваете сроки выполнения задачи? 34 | 35 | ### Зачем спрашивают? 36 | 1. Кандидат способен сам оценить задачу? 37 | 2. Как кандидат коммуницирует с командой при оценке 38 | 3. Как часто кандидат попадает в оценку 39 | 40 | ### Как ответить? 41 | Пишу план, выделяю непонятные вещи, выясняю что является целью, накладываю риски -> получается оценка 42 | 43 | ### Как не стоит отвечать 44 | - "я не оценивал, проджект приносил задачу - я ее делал по факту." 45 | - "За сроки я не отвечал" 46 | 47 | ## Расскажите о ситуации, когда вы не укладывались в сроки. 48 | 49 | ### Зачем спрашивают? 50 | 51 | В разговоре о сорванных сроках выясняется много интересного о кандидате. Проверяем уже конкретный опыт, знания и поведение в конкретных рабочих задачах. 52 | 1. Серьезно ли относитесь к обязательствам перед другими. 53 | 2. Как понимаете, что не укладываетесь в сроки. 54 | 3. Не мешает ли гордость признать про*б, а главное, сделать выводы. 55 | 4. Даете ли свою оценку, если чужая неверна. 56 | 5. Умеете ли планировать и оценивать сроки. 57 | 6. Что будете делать, чтобы исправить ситуацию. 58 | 7. Чем готовы пожертвовать, чтобы успеть. 59 | 60 | ### Как ответить? 61 | Ключевые шаги, при сорванных сроках: 62 | 1. Оценить новый срок и учесть ошибки 63 | 2. Предупредить руководителя 64 | 3. Предложить конкретные действия: 65 | а. Привлечь помощь 66 | б. Работать сверхурочно 67 | в. Договориться и сдвинуть срок 68 | г. Изменить постановку задачи, состав работ 69 | д. Отказаться от менее приоритетных задач 70 | 4. Предупредить людей, чья работа зависит от завершения вашей работы 71 | 5. Первым делом брать в работу ту часть задачи, которая самая рискованная и непроработанная. 72 | 73 | Пример хорошего ответа: 74 | “Считаю что такие ситуации нормальны в нашей работе. Часто приходиться работать с задачами высокой неопределенности и тут главное держать в курсе команду и заинтересованные стороны. Когда подобные ситуации происходят, я рассказываю о проблеме команде и руководителю, дальше решаем, нужно ли привлечь помощь, изменить сроки или упростить задачу. Например, перенести часть функционала в следующий релиз.” 75 | 76 | ### Как не стоит отвечать 77 | 1. “Во всем виновато НАТО/кризис/отдел тестирования, аналитики и т.д.” - винить окружающих это неконструктивно и бесполезно. Покажите умение решать проблемы, а не создавать новые. 78 | 2. “Мне спустили срок сверху” - даже в таком случае надо уметь доказать свою оценку и риски через аргументы. 79 | 3. “Да, но это не моя проблема. Сделаю, когда сделаю.” - комментарии не требуются) 80 | 81 | ## С какими гибкими методологиями вы знакомы? 82 | 83 | ### Зачем спрашивают? 84 | Просто хотят понять какой у вас опыт. К гибким подходам положительно относятся во многих компаниях. 85 | Пока вы будете обсуждать этот вопрос, работодатель поймет ваше отношение к такому стилю работы. 86 | 87 | ### Как ответить? 88 | Почитайте про Kanban, SCRUM, XP. Выпишите ключевые моменты. Запомните хотя бы 1/3 из материала - уже будете круче чем большинство кандидатов 89 | 90 | ### Как не стоит отвечать 91 | Не работал, не знаю ничего 92 | - "Работал по скрам, но у нас было говеный скрам. Щас я расскажу как надо на самом деле" -------------------------------------------------------------------------------- /content/2.hr-softskills/work.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Работа над собой 3 | sidebar_position: 6 4 | --- 5 | 6 | ## Что в вашей работе для вас самое сложное? 7 | 8 | ### Зачем спрашивают? 9 | 1. Какие слабые стороны вы видите в своих навыках. 10 | 2. Что вы делаете чтобы исправить это. 11 | 12 | 13 | ### Как ответить? 14 | Проведите параллель с вашим планом развития, чтобы показать, что решаете эти сложности. 15 | Например: "Пару месяцев назад понял что плаваю в работе с базами данных. Занёс это в свой план развития и сейчас как раз прохожу курс на эту тему" 16 | 17 | Основные направления, над которыми нужно работать в любой компании: 18 | 1. Техника - аудит системы 19 | 2. Понять что по команде. Кого обучать, кого нанимать 20 | 3. Выстроить базовые внутренние и внешние отношения 21 | 4. Инфраструктура, DevOps 22 | 5. План разработки - сроки, стоимость, состав команды 23 | 24 | Костяк, шаблон ответа можно заготовить заранее. 75% повторяется, надо потренироваться. 25 | 26 | ### Как не стоит отвечать 27 | "Работать с людьми" - хоть и честно, это существенный минус 28 | "Работать весь день" 29 | "Делать задачи, которые я не хочу делать" - если это упомянули, значит не редкость, значит будет проблемой 30 | 31 | ## Что вы изучаете в последнее время? Как следите за последними тенденциями в разработке? Что полезного и нового вы узнали по своей профессии за последний год? 32 | 33 | ### Зачем спрашивают? 34 | 1. Как вы улучшаете свои навыки и знания прямо сейчас 35 | 2. Насколько ваши интересы в плане развития совпадают 36 | 37 | ### Как ответить? 38 | 1. Подготовьте план развития, со сроками, целями, темами, скилами и т.д. Интервьюеры ссутся от радости когда слышат о таком. Да и само упражнение полезное. 39 | 2. Рассказывайте о вещах которые пригодятся работодателю: переписывают legacy? Скажите что как раз разбираетесь и тестируете тонкости новой версии. 40 | 3. Расскажите о книгах, статьях, онлайн курсах, видео с youtube, паттернах которые изучили и т.д. Ограничений нет. 41 | 42 | Как быть если вы не развиваетесь: 43 | 1. Рассказывайте о том что изучали год, два, три назад. Освежите память, чтобы казалось что вы неделю назад закончили изучать тему. 44 | 2. Посмотрите, что в тренде по специальности: книги, статьи, курсы, либы... Почитайте один вечер - на интервью скажете что уже две недели изучаете. 45 | 46 | ### Как не стоит отвечать 47 | - “Ничего не учу. Все что мне нужно - уже знаю” 48 | - “Последние месяцы/годы нет времени: семья, работа и переезд” - Так говорят те, кто никогда и не прилагал усилий на развитие. Исключения бывают редко. 49 | - “Учу английский, чтобы наконец-то уехать в Америку, как только получится” = “я к вам временно”. 50 | 51 | ## Назовите 3 ваших положительных качества и 3 отрицательных 52 | 53 | ### Зачем спрашивают? 54 | 1. Вас сравнят с другими кандидатами и выберут того, кто больше подходит по культуре, духу и т.п. 55 | 2. Отсеивание неадекватов. 10% кандидатов на этом вопросе бракуются. 56 | 3. Проверяют насколько вы способны в самоанализ. Если нанимают спеца надолго и на вырост, это хорошо поднимает вас в глазах работодателя. 57 | 58 | ### Как ответить? 59 | Плюсы - составьте список важных качеств на подобной позиции или в этой компании. Ищите вдохновение в описании вакансии. Например: 60 | 1. "Постоянно развиваю свои компетенции". 61 | 2. “Беру и делаю - разберусь и найду информацию для решения любой задачи”. 62 | 3. “Всегда добиваюсь результата”. 63 | 64 | Минусы - выбирайте некритичные организационные или технические вещи. И обязательно рассказывайте как планируете их устранять. 65 | 1. “Излишне дотошен, но учусь находить компромисс“. 66 | 2. “Не люблю заниматься рутинной, поэтому тщательно выбираю компанию, в которой буду работать”. 67 | 3. “Пока что у меня мало навыков в работе с БД, как раз прохожу курс”. 68 | 69 | ### Как не стоит отвечать 70 | - “часто опаздываю” 71 | - “шлю на х*й если не могу договориться” 72 | - “не делаю задачу, если она мне не нравится” 73 | - “буду занозой в попе. если что - уволюсь и уведу сотрудников”. 74 | -------------------------------------------------------------------------------- /content/3.golang/1.array-slice.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Массивы и слайсы 3 | --- 4 | 5 | ## Что такое слайс? 6 | 7 | Слайс в golang - это динамический массив, который может хранить элементы одного типа. Слайс имеет нефиксированную длину, которая может изменяться в процессе работы с ним. Слайс также имеет вместимость, которая определяет, сколько элементов может поместиться в слайс без выделения новой памяти. Слайс можно создавать из массивов, с помощью функции make или литералов слайсов. 8 | 9 | ```go 10 | type slice struct { 11 | array unsafe.Pointer 12 | len int 13 | cap int 14 | } 15 | ``` 16 | 17 | Слайс можно считать как ссылку на массив, который содержит элементы слайса. 18 | Слайс состоит из трех полей: указателя на первый элемент массива, длины слайса и вместимости слайса. 19 | Слайс можно передавать в функции по значению, но при этом изменение элементов слайса в функции отразится на исходном слайсе, так как они ссылаются на один и тот же массив. 20 | 21 | Слайсом можно манипулировать с помощью различных операций и функций. Например, можно получать подслайсы с помощью оператора [:], добавлять элементы в конец слайса с помощью функции append, копировать элементы из одного слайса в другой с помощью функции copy, сортировать элементы слайса с помощью функции sort, и так далее. 22 | 23 | ## Чем массив отличается от слайса? 24 | 25 | Массив и слайс в golang - это структуры данных, которые могут хранить элементы одного типа. Однако, между ними есть несколько отличий: 26 | 27 | - Массив имеет фиксированный размер, который определяется при его создании. Слайс имеет переменную длину, которая может изменяться в процессе работы с ним. 28 | - Массив является значением, а слайс - ссылкой. При передаче массива в функцию или присваивании его другой переменной, происходит копирование всех его элементов. При передаче слайса в функцию или присваивании его другой переменной, происходит копирование только его заголовка, а не элементов. 29 | - Массив можно сравнивать с другим массивом того же размера и типа с помощью оператора ==. Слайс нельзя сравнивать с другим слайсом с помощью оператора ==, только с nil. 30 | - Массив можно инициализировать с помощью литерала массива, указав его размер и элементы в фигурных скобках. Слайс можно инициализировать с помощью литерала слайса, не указывая его размер, или с помощью функции make, указав его длину и вместимость 31 | 32 | ## Как работает append? 33 | 34 | Функция append в golang позволяет добавлять элементы в конец слайса, увеличивая его длину и вместимость при необходимости. Функция append принимает слайс и один или несколько элементов того же типа, что и слайс, и возвращает новый слайс, содержащий все элементы исходного слайса и добавленные элементы. 35 | 36 | Например: 37 | ```go 38 | s := []int{1, 2, 3} // создаем слайс из трех элементов 39 | s = append(s, 4, 5) // добавляем два элемента в конец слайса 40 | fmt.Println(s) // выводит [1 2 3 4 5] 41 | ``` 42 | 43 | Функция append может также принимать другой слайс в качестве аргумента, если он заключен в оператор … Это позволяет объединять два слайса в один. 44 | 45 | Например: 46 | ```go 47 | s1 := []int{1, 2, 3} // создаем первый слайс 48 | s2 := []int{4, 5, 6} // создаем второй слайс 49 | s3 := append(s1, s2...) // добавляем второй слайс в конец первого слайса 50 | fmt.Println(s3) // выводит [1 2 3 4 5 6] 51 | ``` 52 | 53 | Функция append работает таким образом, что если вместимость исходного слайса достаточна для добавления новых элементов, то она просто копирует их в свободное пространство массива, на который ссылается слайс. 54 | 55 | Если же вместимость исходного слайса недостаточна, то функция append выделяет новый массив большего размера, копирует в него все элементы исходного слайса и добавляет новые элементы. Затем функция append возвращает новый слайс, который ссылается на новый массив. 56 | 57 | ## До какого размера можно увеличивать слайс? 58 | 59 | Размер слайса в golang ограничен только доступной памятью в системе. Однако, при увеличении размера слайса с помощью функции append, может происходить перевыделение памяти, если вместимость исходного слайса недостаточна для добавления новых элементов. При этом, функция append выделяет новый массив большего размера, копирует в него все элементы исходного слайса и добавляет новые элементы. Затем функция append возвращает новый слайс, который ссылается на новый массив. 60 | 61 | При перевыделении памяти, функция append следует определенной стратегии, чтобы избежать частого копирования и минимизировать оверхед. При текущем размере слайса менее 256 элементов, размер памяти увеличивается вдвое (вне зависимости от запрашиваемой cap). При размере слайса больше 256 элементов, слайс увеличивается на четверть текущего размера. 62 | 63 | Например: 64 | 65 | ```go 66 | s := make([]int, 0, 5) // создаем слайс с длиной 0 и вместимостью 5 67 | fmt.Println(len(s), cap(s)) // выводит 0 5 68 | s = append(s, 1, 2, 3, 4, 5) // добавляем 5 элементов в слайс 69 | fmt.Println(len(s), cap(s)) // выводит 5 5 70 | s = append(s, 6) // добавляем еще один элемент в слайс 71 | fmt.Println(len(s), cap(s)) // выводит 6 10 - вместимость увеличилась вдвое 72 | s = append(s, 7, 8, 9, 10) // добавляем еще 4 элемента в слайс 73 | fmt.Println(len(s), cap(s)) // выводит 10 10 74 | s = append(s, 11) // добавляем еще один элемент в слайс 75 | fmt.Println(len(s), cap(s)) // выводит 11 20 - вместимость увеличилась вдвое 76 | ``` 77 | 78 | ## Берем от слайса слайс, куда будет указывать его указатель? 79 | 80 | Если мы берем от слайса слайс, то его указатель будет указывать на тот же массив, что и указатель исходного слайса, но с другим смещением. Например, если мы имеем слайс s, который ссылается на массив [1, 2, 3, 4, 5], и мы берем от него подслайс s[1:3], то его указатель будет указывать на тот же массив, но с элемента 2. То есть, подслайс будет содержать элементы [2, 3] из исходного массива. 81 | 82 | Это означает, что если мы изменим элементы подслайса, то это отразится на исходном слайсе и наоборот, так как они ссылаются на один и тот же массив. Однако, если мы добавим элементы в подслайс с помощью функции append, то это может привести к перевыделению памяти и созданию нового массива, если вместимость подслайса недостаточна. В этом случае, подслайс будет ссылаться на новый массив, а исходный слайс - на старый. 83 | 84 | 85 | ## В чем разница между слайсом указателей и слайсом значений с точки зрения вызова функции? 86 | 87 | Разница между слайсом указателей и слайсом значений с точки зрения вызова функции заключается в том, как они передаются и изменяются внутри функции. 88 | 89 | Слайс указателей - это слайс, который содержит указатели на элементы другого типа, например: 90 | ```go 91 | var sp []*int // слайс указателей на int 92 | ``` 93 | 94 | Слайс значений - это слайс, который содержит элементы другого типа, например: 95 | ```go 96 | var sv []int // слайс значений int 97 | ``` 98 | 99 | Когда мы передаем слайс указателей в функцию, мы копируем только заголовок слайса, который содержит указатель на первый элемент, длину и вместимость. 100 | 101 | Однако, сами элементы слайса, на которые указывают указатели, не копируются. Это означает, что если мы изменяем элементы слайса в функции, то это отразится на исходном слайсе, так как они ссылаются на одни и те же значения. Например: 102 | ```go 103 | func main() { 104 | x := 1 105 | y := 2 106 | z := 3 107 | sp := []*int{&x, &y, &z} // создаем слайс указателей 108 | fmt.Println(*sp[0], *sp[1], *sp[2]) // выводит 1 2 3 109 | modifySlicePointers(sp) // передаем слайс в функцию 110 | fmt.Println(*sp[0], *sp[1], *sp[2]) // выводит 10 20 30 111 | } 112 | 113 | func modifySlicePointers(sp []*int) { 114 | *sp[0] = 10 // изменяем значение, на которое указывает первый элемент слайса 115 | *sp[1] = 20 // изменяем значение, на которое указывает второй элемент слайса 116 | *sp[2] = 30 // изменяем значение, на которое указывает третий элемент слайса 117 | } 118 | ``` 119 | 120 | Когда мы передаем слайс значений в функцию, мы также копируем только заголовок слайса, который содержит указатель на первый элемент, длину и вместимость. Однако, сами элементы слайса, на которые указывает указатель, также не копируются. Это означает, что если мы изменяем элементы слайса в функции, то это также отразится на исходном слайсе, так как они ссылаются на одни и те же значения. Например: 121 | ```go 122 | func main() { 123 | sv := []int{1, 2, 3} // создаем слайс значений 124 | fmt.Println(sv[0], sv[1], sv[2]) // выводит 1 2 3 125 | modifySliceValues(sv) // передаем слайс в функцию 126 | fmt.Println(sv[0], sv[1], sv[2]) // выводит 10 20 30 127 | } 128 | 129 | func modifySliceValues(sv []int) { 130 | sv[0] = 10 // изменяем значение первого элемента слайса 131 | sv[1] = 20 // изменяем значение второго элемента слайса 132 | sv[2] = 30 // изменяем значение третьего элемента слайса 133 | } 134 | ``` 135 | 136 | Стоит быть внимательным при добавлении элементов в слайс в функции с помощью `append`, так как мы изменяем копию структуры слайса. В этом случае, слайс в функции будет иметь другие значния len, cap и, возможно, ссылки на массив. Это означает, что добавленные элементы не будут видны в исходном слайсе. Например: 137 | 138 | ```go 139 | func main() { 140 | sv := []int{1, 2, 3} // создаем слайс значений 141 | appendSliceValues(sv) // передаем слайс в функцию 142 | fmt.Println(sv) // выводит [1 2 3] 143 | } 144 | 145 | func appendSliceValues(sv []int) { 146 | sv = append(sv, 4) // добавляем элемент в слайс 147 | fmt.Println(sv) // выводит [1 2 3 4] 148 | } 149 | ``` 150 | 151 | Если мы хотим, чтобы добавленные элементы сохранялись в исходном слайсе, мы должны возвращать слайс из функции и присваивать его исходному слайсу. Например: 152 | ```go 153 | func main() { 154 | sv := []int{1, 2, 3} // создаем слайс значений 155 | sv = appendSliceValues(sv) // передаем слайс в функцию и присваиваем его исходному слайсу 156 | fmt.Println(sv) // выводит [1 2 3 4] 157 | } 158 | 159 | func appendSliceValues(sv []int) []int { 160 | sv = append(sv, 4) // добавляем элемент в слайс 161 | return sv // возвращаем слайс из функции 162 | } 163 | ``` 164 | 165 | ## Какие есть ограничения при работе со слайсом? 166 | При работе со слайсом есть некоторые ограничения, которые нужно учитывать: 167 | 168 | - Слайс не может содержать элементы разных типов, только одного. Для хранения элементов разных типов нужно использовать структуры, интерфейсы или пустые интерфейсы (interface{}). 169 | - Слайс не может быть сравнен с другим слайсом с помощью оператора ==, только с nil. Для сравнения двух слайсов нужно использовать цикл или функцию reflect.DeepEqual. 170 | - Слайс не может быть использован в качестве ключа мапы, так как он не является хешируемым типом. Для использования слайса в качестве ключа мапы нужно преобразовать его в строку или другой хешируемый тип. 171 | - Слайс не может быть константой, так как он является ссылочным типом. Для объявления слайса нужно использовать var, := или make. 172 | - Слайс не может быть безопасно передан в функцию или возвращен из функции, так как он ссылается на массив, который может быть изменен в другом месте. Для безопасной передачи или возврата слайса нужно копировать его элементы в новый слайс с помощью функции copy или среза [:]. 173 | 174 | ## С какой скоростью идет поиск в массиве и почему? 175 | 176 | Самый простой алгоритм поиска в массиве - это линейный поиск, который перебирает все элементы массива по порядку, пока не найдет искомый элемент или не дойдет до конца массива. 177 | 178 | Скорость линейного поиска пропорциональна длине массива, то есть чем больше элементов в массиве, тем дольше будет идти поиск. Сложность линейного поиска в худшем случае составляет O(n), где n - это количество элементов в массиве. 179 | 180 | ## Какая есть функции для создания слайса с длиной отличной от нуля? 181 | 182 | Для создания слайса с длиной отличной от нуля в golang, можно использовать одну из следующих функций: 183 | 184 | Функция make, которая принимает тип слайса, длину и вместимость, и возвращает слайс с заданными параметрами. Например: 185 | ```go 186 | s := make([]int, 3, 5) // создает слайс из трех целых чисел с вместимостью пять 187 | ``` 188 | 189 | Функция append, которая принимает слайс и один или несколько элементов того же типа, что и слайс, и возвращает новый слайс, содержащий все элементы исходного слайса и добавленные элементы. Например: 190 | ```go 191 | s := []int{} // создает пустой слайс 192 | s = append(s, 1, 2, 3) // добавляет три элемента в слайс 193 | ``` 194 | 195 | Литерал слайса, который позволяет инициализировать слайс с помощью фигурных скобок и перечисления элементов. Например: 196 | ```go 197 | s := []int{1, 2, 3} // создает слайс из трех целых чисел 198 | ``` 199 | 200 | ## Допустима ли конкуррентная работа со слайсом? 201 | 202 | Такая работа может быть допустима, если соблюдены определенные условия и предосторожности: 203 | - Во-первых, конкуррентное чтение слайса не представляет опасности, если никто не пишет в слайс в то же время. То есть, можно безопасно читать слайс из нескольких горутин, если слайс не изменяется. 204 | - Во-вторых, конкуррентная запись в слайс может привести к гонке данных (data race), если не использовать синхронизацию или атомарные операции. 205 | 206 | Для предотвращения гонки данных, можно использовать один из следующих способов: 207 | 208 | - Использовать мьютекс sync.Mutex или sync.RWMutex для защиты слайса от одновременного доступа. 209 | - Использовать атомарные операции (sync/atomic) для изменения отдельных элементов слайса. 210 | - Использовать каналы для передачи слайса между горутинами. 211 | -------------------------------------------------------------------------------- /content/3.golang/2.strings.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Строки 3 | --- 4 | 5 | ## Что такое string или строки? 6 | 7 | Строка в golang - это последовательность символов, которая представляет собой текстовые данные. 8 | Строки в golang объявляются с типом string и заключаются в двойные кавычки, например: 9 | 10 | ```go 11 | var s string = "hello" 12 | // или сокращенная запись 13 | s := "hey" 14 | ``` 15 | 16 | Строки в golang являются неизменяемыми, то есть их нельзя модифицировать после создания. Однако, можно создавать новые строки из существующих, используя различные операции и функции. 17 | 18 | Например, можно склеивать строки с помощью оператора +, сравнивать строки с помощью оператора ==, или применять функции из пакета `strings` для поиска, замены, преобразования и других манипуляций со строками. 19 | 20 | Строки в golang хранят символы в кодировке UTF-8, которая поддерживает множество языков и специальных символов. 21 | 22 | Каждый символ в строке называется руной (`rune`), которая является псевдонимом для типа `int32`. Руны можно получать из строк с помощью цикла for-range или функции range, а также преобразовывать в строки с помощью функции `string`. 23 | 24 | Источники: 25 | - [Programiz](https://www.programiz.com/golang/string) 26 | - [DigitalOcean](https://www.digitalocean.com/community/tutorials/an-introduction-to-the-strings-package-in-go-ru) 27 | 28 | ## Какие особенности есть у string? 29 | 30 | Строка в golang - это неизменяемая последовательность символов в кодировке UTF-8, которая представляет собой текстовые данные. Строки в golang имеют тип `string` и заключаются в двойные кавычки или обратные апострофы. Строки в golang поддерживают множество языков и специальных символов, включая русские буквы и эмодзи. 31 | 32 | Строки в golang можно объединять, сравнивать, искать, заменять, преобразовывать и выполнять другие манипуляции с помощью различных функций из пакета `strings`. Строки в golang также можно преобразовывать в руны (кодовые значения символов) и байты (единицы хранения данных) для более низкоуровневой работы с текстом 33 | 34 | - [Golangify](https://golangify.com/string) 35 | 36 | ## Как устроена строка внутри, а точнее reflect.String и reflect.StringHeader? 37 | 38 | reflect.String - это псевдоним для типа string, который является неизменяемой последовательностью символов в кодировке UTF-8. 39 | 40 | reflect.StringHeader - это структура, которая содержит два поля: Data и Len. Data - это указатель на первый байт строки, а Len - это длина строки в байтах. Структура reflect.StringHeader может быть получена из строки с помощью функции unsafe.Pointer, которая преобразует любое значение в указатель безопасного типа. Например, можно использовать такой код: 41 | 42 | ```go 43 | s := "hello" 44 | sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) 45 | fmt.Println(sh.Data, sh.Len) // выводит адрес и длину строки 46 | ``` 47 | 48 | Структура reflect.StringHeader может быть также использована для создания новой строки из существующей, изменяя ее указатель или длину. Например, можно использовать такой код: 49 | 50 | ```go 51 | s := "hello, world!" 52 | sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) 53 | sh.Data += 7 // сдвигаем указатель на 7 байтов 54 | sh.Len -= 7 // уменьшаем длину на 7 байтов 55 | fmt.Println(s) // выводит "world!" 56 | ``` 57 | 58 | Однако, такое использование reflect.StringHeader может быть опасным, так как оно нарушает неизменяемость строк и может привести к непредвиденным ошибкам или поведению. Например, если мы изменим длину строки больше, чем ее емкость, то мы можем получить доступ к памяти, которая не принадлежит строке, и вызвать панику или нарушение сегментации. Также, если мы изменим указатель на данные, то мы можем потерять ссылку на оригинальную строку и вызвать утечку памяти или сборку мусора. 59 | 60 | Поэтому, reflect.String и reflect.StringHeader следует использовать с осторожностью и только в тех случаях, когда это необходимо для решения конкретной задачи. В большинстве случаев, можно обойтись стандартными функциями и методами для работы со строками в golang. 61 | -------------------------------------------------------------------------------- /content/3.golang/3.hashmap.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Хеш-мапы 3 | --- 4 | 5 | ## Что такое мапа? 6 | 7 | Мапа в Go - это тип данных, который предназначен для хранения пар "ключ-значение". Это структура данных, также известная как хэш-таблица, словарь или ассоциативный массив. Мапа позволяет получить значение по ключу. Ключами в мапе могут быть любые сравниваемые типы — все сравнимые типы. 8 | 9 | ```go title="main.go" 10 | package main 11 | 12 | import "fmt" 13 | 14 | func main() { 15 | m := make(map[string]int) 16 | m["apple"] = 1 17 | m["banana"] = 2 18 | fmt.Println(m) 19 | } 20 | ``` 21 | 22 | ## Что произойдет при конкурентной записи в мапу? 23 | 24 | Мапы в Go не являются потокобезопасными. Это означает, что если вы попытаетесь записать данные в мапу из нескольких горутин одновременно, это может привести к состоянию гонки. Если вам нужно работать с мапой из нескольких горутин, вы должны использовать механизмы синхронизации, такие как sync.Mutex или sync.RWMutex, чтобы гарантировать, что в любой момент времени только одна горутина может изменять мапу. Вот пример использования sync.Mutex для безопасной записи в мапу из нескольких горутин: 25 | 26 | ```go title="main.go" 27 | package main 28 | 29 | import ( 30 | "fmt" 31 | "sync" 32 | ) 33 | 34 | func main() { 35 | var wg sync.WaitGroup 36 | var mu sync.Mutex 37 | m := make(map[int]int) 38 | 39 | for i := 0; i < 10; i++ { 40 | wg.Add(1) 41 | go func(i int) { 42 | defer wg.Done() 43 | mu.Lock() 44 | m[i] = i * 2 45 | mu.Unlock() 46 | }(i) 47 | } 48 | 49 | wg.Wait() 50 | fmt.Println(m) 51 | } 52 | ``` 53 | 54 | ## Как устроена мапа под капотом? 55 | 56 | Мапа в Go - это структура данных, которая хранит пары "ключ-значение". Внутри мапы ключи и значения хранятся в выделенном участке памяти, последовательно. Для получения адресов ячеек конкретных ключей и значений используется хэширующая функция. 57 | 58 | Вот некоторые детали о том, как устроена мапа в Go: 59 | 60 | - [Мапа разбивается на бакеты для более эффективного поиска](https://www.youtube.com/watch?v=P_SXTUiA-9Y). 61 | - [Хэш-функция используется для равномерного распределения ключей по бакетам](https://www.youtube.com/watch?v=P_SXTUiA-9Y). 62 | - [При переполнении бакета происходит рост мапы](https://www.youtube.com/watch?v=P_SXTUiA-9Y). 63 | - [Эвакуация данных происходит при заполнении мапы](https://www.youtube.com/watch?v=P_SXTUiA-9Y). 64 | - [Порядок обхода мапы является случайным](https://www.youtube.com/watch?v=P_SXTUiA-9Y). 65 | 66 | ## Какие ключи могут быть у мапы? 67 | 68 | Ключами в мапе могут быть любые сравниваемые типы — все простые скалярные типы, массивы. Несравниваемые типы — срезы, мапы, функции. Ключи и значения мапы будут храниться в выделенном участке памяти, последовательно. 69 | 70 | ## Какая сложность работы с мапой? 71 | 72 | Операции вставки, удаления и поиска в мапе в Go обычно имеют сложность O(1), то есть они выполняются за постоянное время. Это достигается за счет использования хэш-таблицы внутри мапы. Однако в худшем случае, когда все ключи попадают в один и тот же бакет, эти операции могут иметь сложность O(n), где n - количество элементов в мапе 73 | 74 | ## Можно ли взять адрес элемента мапы и почему? 75 | 76 | Нет, нельзя взять адрес элемента мапы в Go. Это связано с тем, как устроена мапа внутри. Мапа разбита на бакеты, и при росте мапы элементы могут переходить из одного бакета в другой. Это означает, что адрес элемента в памяти может меняться, и поэтому Go не позволяет взять адрес элемента мапы. Если вы попытаетесь это сделать, компилятор выдаст ошибку. 77 | 78 | ## Как работает эвакуация данных? 79 | 80 | Эвакуация данных в мапе Go происходит при переполнении мапы. Когда количество элементов в мапе достигает определенного порога, мапа “растет” - создается новая, большая мапа, и все элементы из старой мапы копируются в новую. Этот процесс называется "эвакуацией". Важно отметить, что в Go рост мапы происходит асинхронно. Это означает, что во время роста мапы могут возникать ситуации, когда при попытке доступа к данным часть бакетов уже переехала в новую мапу, а часть еще нет. Благодаря этому не происходит просадок во время роста большой мапы. 81 | 82 | ## Как разрешаются коллизии в мапе? 83 | 84 | В мапе Go коллизии разрешаются с помощью бакетов. Каждый бакет может содержать до восьми элементов. Если все ячейки в бакете заняты, то происходит переполнение и мапа “растет” - создается новая, большая мапа, и все элементы из старой мапы копируются в новую. Этот процесс называется "эвакуацией". При этом, благодаря использованию хэш-функции, ключи равномерно распределяются по бакетам. Это позволяет минимизировать количество коллизий и обеспечивает быстрый доступ к данным. 85 | 86 | ## В функции make для мапы мы указываем число. Что оно дает? 87 | 88 | Число, указываемое в функции make для мапы в Go, определяет начальную вместимость мапы. Это число элементов, которые мапа сможет хранить без необходимости расширения. Если вы заранее знаете, сколько элементов будет в мапе, вы можете использовать это число при создании мапы, чтобы уменьшить количество операций реаллокации памяти, что может улучшить производительность. Вот пример создания мапы с начальной вместимостью 10: 89 | 90 | ```go title="main.go" 91 | m := make(map[string]int, 10) 92 | ``` 93 | 94 | ## Стало слишком много коллизий в мапе, как решить проблему? 95 | 96 | Если в мапе Go стало слишком много коллизий, вы можете использовать следующие подходы для решения проблемы: 97 | 98 | Изменить размер мапы: Если мапа слишком мала, она может быстро заполняться, что приводит к большому количеству коллизий. В этом случае вы можете увеличить размер мапы, чтобы уменьшить вероятность коллизий. 99 | 100 | Использовать метод раздельного связывания: Это метод, при котором внутри хеш-таблицы хранится массив фиксированного размера, элементы которого - связанные списки. По хешу ключа определяется элемент массива (bucket - корзину) и потом смотрится в списке, нет ли такого элемента и действуется соответствующее (добавляется/удаляется/модифицируется). 101 | 102 | ## Чем мапа отличается от sync.Map? 103 | 104 | **sync.Map** и обычная мапа в Go имеют различия в использовании и производительности 105 | 106 | 1. **Потокобезопасность**: Обычная мапа в Go не является потокобезопасной, что означает, что одновременная запись в мапу из нескольких горутин может привести к состоянию гонки. В то время как **`sync.Map`** разработана для использования в многопоточных ситуациях и обеспечивает потокобезопасность. 107 | 2. **Производительность** **`sync.Map`** обычно близка по скорости к обычной мапе с **`sync.RWMutex`**, но может быть лучше в случае чтения. Когда происходит чтение и обновление, **`sync.Map`** будет иметь элементы и в чтении, и в “грязном” состоянии. 108 | 3. **Использование** **`sync.Map`** - это структура с методами, предоставляющими обычные операции с мапой. Вы не можете использовать синтаксис **`a_map["key"]`** с **`sync.Map`**, вместо этого нужно использовать методы, такие как **`Load`**, **`Store`** и **`Delete`**. 109 | 4. **Структура** **`sync.Map`** - это сложная структура, обычно состоящая из двух мап - одной для чтения и одной для новых элементов. 110 | 111 | Важно отметить, что **`sync.Map`** следует использовать только в тех случаях, когда большинство операций с мапой - это чтение, и когда ключи мапы меняются динамически. 112 | 113 | ## Какая функция используется при хешировании ключа в мапе? 114 | 115 | При хешировании ключа в мапе golang используется функция runtime.mapaccess1_faststr, которая принимает тип мапы, саму мапу и ключ в виде строки. Функция ищет ключ в мапе и возвращает указатель на соответствующее значение. Функция runtime.mapaccess1_faststr является специализированной версией функции runtime.mapaccess1, которая работает с любым типом ключа. 116 | 117 | Хеширование ключа в мапе golang основано на алгоритме MurmurHash, который быстро и равномерно распределяет ключи по хеш-таблице. Хеш-таблица в golang состоит из массива корзин (buckets), каждая из которых содержит до восьми пар ключ-значение. Кроме того, каждая корзина имеет указатель на переполненную корзину, которая создается при коллизии хешей. 118 | 119 | Если вы хотите узнать больше о реализации мапы в golang, вы можете почитать эту статью или эту статью на Хабре. Они объясняют детали работы мапы в golang с примерами кода и иллюстрациями. 😊 120 | 121 | Источники: 122 | - [Habr](https://habr.com/ru/articles/457728/) 123 | - [Habr](https://habr.com/ru/articles/704796/) 124 | - [GolangForAll](https://golangforall.com/ru/post/map-principles-golang.html) 125 | -------------------------------------------------------------------------------- /content/3.golang/4.interface.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Интерфейсы 3 | --- 4 | 5 | ## Что такое интерфейс? 6 | 7 | Интерфейс в golang - это тип, который определяет набор сигнатур методов. Интерфейс позволяет абстрагироваться от конкретной реализации и работать с разными типами данных, которые реализуют один и тот же интерфейс. 8 | 9 | Например, интерфейс Printer определяет метод Print(), который может быть реализован разными структурами, такими как User или Document. Для того, чтобы тип реализовывал интерфейс, он должен предоставить определения всех методов интерфейса. 10 | 11 | В Go реализация интерфейса происходит неявно, то есть не требуется указывать, что тип реализует интерфейса. 12 | 13 | ## Для чего используется интерфейс? 14 | 15 | Интерфейс в golang используется для определения набора методов, которые должен реализовывать какой-либо тип данных. 16 | 17 | Интерфейсы позволяют абстрагироваться от конкретных реализаций и работать с разными типами, имеющими общее поведение. Интерфейсы также способствуют модульности, гибкости и переиспользованию кода. 18 | 19 | Например, интерфейс fmt.Stringer определяет метод String(), который возвращает строковое представление любого типа, реализующего этот интерфейс. 20 | 21 | ## Как в строго типизированным языке сделать функцию, которая работает с разными типами? 22 | 23 | Один из способов сделать это - использовать обобщения (generics), которые появились в Go 1.18. Обобщения позволяют объявлять и использовать функции или типы, которые написаны для работы с любым из набора типов, предоставляемых вызывающим кодом. Для этого в Go введены три новых элемента языка: 24 | 25 | - Параметры типов для функций и типов. Это означает, что вы можете задать переменную типа T, которая может принимать разные конкретные типы при вызове функции или создании типа. 26 | - Определение интерфейсных типов как множеств типов, включая типы, не имеющие методов. Это означает, что вы можете задать ограничение (constraint) на параметр типа, указав, какие свойства должен иметь тип, чтобы его можно было использовать в обобщенной функции или типе. 27 | - Вывод типов, который позволяет опускать аргументы типов во многих случаях при вызове функции. Это означает, что вы можете не указывать конкретный тип при вызове обобщенной функции, если компилятор может сам его определить из контекста. 28 | 29 | ## Есть интерфейс, а есть указатель на структуру, который nil. Кладем указатель в интерфейс. Что если сравнить интерфейс с nil? 30 | 31 | Интерфейс в Go является типом, который может хранить любые типы, удовлетворяющие ему. 32 | 33 | Интерфейс, в котором лежит указатель на структуру, и nil интерфейс сравнимы между собой. Если вы сравните интерфейс, в котором лежит указатель на структуру, с nil, результат будет "false", потому что интерфейс не пустой. 34 | 35 | Однако, если вы сравните интерфейс с интерфейсом, содержащим nil указатель, результат будет "false". 36 | 37 | ```go title="main.go" 38 | type SomeInterface interface { 39 | SomeMethod() 40 | } 41 | 42 | type SomeStruct struct{} 43 | 44 | func (s *SomeStruct) SomeMethod() {} 45 | 46 | func main() { 47 | var s SomeStruct 48 | var i1 SomeInterface = &s 49 | var i2 SomeInterface 50 | 51 | fmt.Println(i1 == nil) // false 52 | fmt.Println(i2 == nil) // true 53 | } 54 | ``` 55 | 56 | В примере выше мы создаем интерфейс SomeInterface с методом SomeMethod. Внутри интерфейса i1 лежит указатель на структуру SomeStruct, а интерфейс i2 является пустым интерфейсом. Сравнивая i1 с nil, мы получаем "false", так как интерфейс i1 не пустой. Сравнивая i2 с nil, мы получаем "true", так как интерфейс i2 является пустым интерфейсом. 57 | 58 | ## Чем `any` отличается от пустого интерфейса? 59 | 60 | Пустой интерфейс в Go (nil интерфейс) не хранит значение. Если интерфейс пустой, то его сравнение с nil будет всегда возвращать true. 61 | 62 | Тип any (тоже известный как интерфейс **`interface{}`**) в Go - это специальный тип интерфейса, который может хранить значение любого типа. Тип any является универсальным типом и может быть использован, когда нужно написать обобщенный код, который будет работать с различными типами. 63 | 64 | ```go title="main.go" 65 | func main() { 66 | var i1 interface{} 67 | var i2 interface{} = (*SomeStruct)(nil) 68 | 69 | fmt.Println(i1 == nil) // true 70 | fmt.Println(i2 == nil) // false 71 | } 72 | ``` 73 | 74 | ## Чем пустой интерфейс отличается от пустой структуры? 75 | 76 | Пустой интерфейс (interface{}) и пустая структура (struct{}) являются двумя различными понятиями в Go. 77 | 78 | Пустая структура (struct{}) не содержит никаких полей и не реализует никаких методов. Она используется в основном для создания переменных, которые не содержат никаких данных. Например: 79 | 80 | ```go title="main.go" 81 | var emptyStruct struct{} 82 | ``` 83 | 84 | Пустой интерфейс (interface{}), с другой стороны, может указывать на любое значение любого типа. Он не имеет никаких методов и не содержит никаких данных. Он используется в основном для передачи значений различных типов в функции или методы. Например: 85 | 86 | ```go title="main.go" 87 | var emptyInterface interface{} 88 | emptyInterface = 10 89 | emptyInterface = "hello" 90 | emptyInterface = struct{}{} 91 | ``` 92 | 93 | Таким образом, основное отличие заключается в том, что пустая структура не может указывать на значения различных типов, в то время как пустой интерфейс может. 94 | -------------------------------------------------------------------------------- /content/3.golang/5.links.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Указатели 3 | --- 4 | 5 | ## Что такое указатель? 6 | 7 | Указатель в программировании - это переменная, которая хранит адрес другой переменной в памяти. В Go, указатели используются для того, чтобы иметь возможность изменять значение переменной напрямую или для оптимизации производительности при работе с большими структурами данных. 8 | 9 | Вы можете получить адрес переменной с помощью оператора `&`, а затем этот адрес можно сохранить в указателе. Для доступа к значению, на которое указывает указатель, используется оператор `*`. 10 | 11 | Вот пример: 12 | 13 | ```go 14 | package main 15 | 16 | import "fmt" 17 | 18 | func main() { 19 | var x int = 10 20 | var p *int 21 | p = &x // Получаем адрес переменной x и сохраняем его в p 22 | 23 | fmt.Println(*p) // Выводим значение переменной, на которую указывает p 24 | } 25 | ``` 26 | 27 | В этом примере `p` является указателем на `x`, и `*p` дает нам доступ к значению `x`. 28 | 29 | ## Что такое ссылка на значение? 30 | 31 | В Go, ссылка на значение обычно означает использование указателей. Указатель - это переменная, которая хранит адрес другой переменной. 32 | 33 | Вы можете получить адрес переменной с помощью оператора `&`, а затем этот адрес можно сохранить в указателе. Для доступа к значению, на которое указывает указатель, используется оператор `*`. 34 | 35 | Вот пример: 36 | 37 | ```go 38 | package main 39 | 40 | import "fmt" 41 | 42 | func main() { 43 | var x int = 10 44 | var p *int 45 | p = &x // Получаем адрес переменной x и сохраняем его в p 46 | 47 | fmt.Println(*p) // Выводим значение переменной, на которую указывает p 48 | } 49 | ``` 50 | 51 | В этом примере `p` является указателем на `x`, и `*p` дает нам доступ к значению `x`. 52 | 53 | ## Чем отличается ссылка от указателя? 54 | 55 | В Go, термины "ссылка" и "указатель" часто используются взаимозаменяемо, но они имеют некоторые различия. 56 | 57 | 1. **Указатель** - это переменная, которая хранит адрес другой переменной. Указатели могут быть нулевыми, что означает, что они не указывают ни на одну переменную. 58 | 2. **Ссылка** - это альтернативное имя для уже существующей переменной. В Go, ссылки как таковые не существуют, но аналогом являются указатели. 59 | 60 | Основное различие между ними заключается в том, что указатели могут быть переназначены для указания на разные переменные во время выполнения, в то время как ссылки, как правило, не могут быть изменены после их инициализации (хотя в Go это не применимо, так как нет ссылок в традиционном понимании этого термина). 61 | 62 | ## Чем чревато передавать структуру по ссылке? 63 | 64 | Передача структуры по ссылке в Go означает передачу указателя на структуру. Это может иметь следующие последствия: 65 | 66 | 1. **Изменение оригинальной структуры**: Поскольку вы передаете указатель, любые изменения, которые вы вносите в структуру внутри функции, будут отражаться на оригинальной структуре. Это может быть как полезным, так и опасным, в зависимости от того, ожидаете ли вы эти изменения. 67 | 2. **Производительность**: Передача структуры по ссылке обычно быстрее, чем передача по значению, особенно для больших структур, поскольку не требуется копирование всей структуры. 68 | 3. **Nullability**: Указатели могут быть `nil`, в то время как структуры - нет. Это значит, что если вы передаете структуру по ссылке, вам нужно быть уверенным, что она не `nil`, прежде чем обращаться к ее полям, иначе вы получите панику во время выполнения. 69 | 70 | В общем, передача структуры по ссылке может быть полезной для оптимизации производительности и для изменения оригинальной структуры, но требует более внимательного обращения, чтобы избежать ошибок во время выполнения. 71 | 72 | ## Для чего используются указатели? 73 | 74 | Указатели в Go используются по нескольким причинам: 75 | 76 | 1. **Изменение значения в функции**: Если вы хотите, чтобы функция изменяла значение переменной, вы можете передать указатель на эту переменную в функцию. Это позволяет функции напрямую изменять значение переменной, а не ее копию. 77 | 2. **Оптимизация производительности**: Для больших структур данных передача указателей может быть более эффективной, чем передача значений, поскольку передача значений включает в себя копирование всей структуры данных. 78 | 3. **Работа с динамическими структурами данных**: Указатели необходимы для создания сложных структур данных, таких как связанные списки, деревья, графы и т.д. 79 | 4. **Интерфейсы и полиморфизм**: Указатели используются для реализации интерфейсов и полиморфизма. Если методы определены для указателя на тип, то только указатели этого типа могут использоваться для вызова этих методов. 80 | 81 | Вот пример использования указателей для изменения значения в функции: 82 | 83 | ```go 84 | package main 85 | 86 | import "fmt" 87 | 88 | func increment(x *int) { 89 | *x = *x + 1 90 | } 91 | 92 | func main() { 93 | var x int = 1 94 | increment(&x) 95 | fmt.Println(x) // Выводит 2 96 | } 97 | ``` 98 | 99 | В этом примере функция `increment` принимает указатель на `int` и увеличивает значение, на которое указывает этот указатель. 100 | 101 | ## Для чего в практике используешь указатели? 102 | 103 | В практическом программировании на Go указатели используются в следующих ситуациях: 104 | 105 | 1. **Изменение значения переменной в функции**: Если вы хотите изменить значение переменной внутри функции, вы передаете указатель на эту переменную в функцию. Это позволяет функции напрямую изменять значение переменной, а не ее копию. 106 | 2. **Оптимизация производительности**: Для больших структур данных передача указателей может быть более эффективной, чем передача значений, поскольку передача значений включает в себя копирование всей структуры данных. 107 | 3. **Работа с динамическими структурами данных**: Указатели необходимы для создания сложных структур данных, таких как связанные списки, деревья, графы и т.д. 108 | 4. **Интерфейсы и полиморфизм**: Указатели используются для реализации интерфейсов и полиморфизма. Если методы определены для указателя на тип, то только указатели этого типа могут использоваться для вызова этих методов. 109 | 5. **Опциональные поля в структурах**: В Go, указатели могут быть `nil`, что позволяет им использоваться для представления опциональных полей в структурах. 110 | 6. **Работа с внешними ресурсами**: Указатели часто используются при работе с внешними ресурсами, такими как файлы или сетевые соединения. Это позволяет функциям и методам изменять состояние этих ресурсов. 111 | 112 | ## Какие есть средства для работы с указателями? 113 | 114 | В Go есть несколько ключевых средств для работы с указателями: 115 | 116 | 1. **Оператор `&`**: Этот оператор используется для получения адреса переменной. Например, если `x` - это переменная, то `&x` - это адрес этой переменной. 117 | 2. **Оператор `*`**: Этот оператор используется для доступа к значению, на которое указывает указатель. Например, если `p` - это указатель на `int`, то `*p` - это `int`, на который указывает `p`. 118 | 3. **Функция `new`**: Эта функция создает новую переменную заданного типа, инициализирует ее нулем и возвращает указатель на нее. 119 | 120 | Вот пример использования этих средств: 121 | 122 | ```go 123 | package main 124 | 125 | import "fmt" 126 | 127 | func main() { 128 | x := 1 129 | p := &x // Получаем адрес переменной x 130 | fmt.Println(*p) // Выводим значение, на которое указывает p 131 | 132 | y := new(int) // Создаем новую переменную типа int и получаем указатель на нее 133 | fmt.Println(*y) // Выводит 0, так как новые переменные инициализируются нулем 134 | } 135 | ``` 136 | 137 | В этом примере `p` - это указатель на `x`, и `*p` дает нам доступ к значению `x`. `y` - это указатель на новую переменную `int`, инициализированную нулем. 138 | 139 | ## Когда лучше использовать/не использовать указатели? 140 | 141 | Указатели в Go могут быть полезными, но их использование зависит от конкретной ситуации. Вот несколько рекомендаций: 142 | 143 | **Используйте указатели, когда:** 144 | 1. **Вы хотите изменить значение переменной внутри функции.** Если вы передаете переменную по значению, функция получит копию этой переменной, и любые изменения, которые вы внесете, не повлияют на исходную переменную. Если вы передаете переменную по ссылке (т.е. передаете указатель на нее), функция сможет изменить исходную переменную. 145 | 2. **Вы работаете с большими структурами данных.** Передача больших структур данных по значению может быть накладной с точки зрения производительности, поскольку это требует копирования всей структуры. Передача указателя на структуру вместо этого может быть более эффективной. 146 | 3. **Вы хотите иметь возможность присвоить переменной значение `nil`.** В Go, только указатели могут иметь значение `nil`. 147 | 148 | **Избегайте использования указателей, когда:** 149 | 1. **Вы работаете с небольшими структурами данных или простыми типами.** Для небольших структур данных или простых типов, таких как `int` или `bool`, передача по значению обычно быстрее и безопаснее. 150 | 2. **Вы не хотите, чтобы функция изменяла исходную переменную.** Если вы передаете переменную по значению, функция не сможет изменить исходную переменную, что может быть полезно для предотвращения неожиданных побочных эффектов. 151 | 3. **Вы хотите избежать ошибок во время выполнения, связанных с `nil`.** Если вы используете указатель, вам нужно быть уверенным, что он не `nil`, прежде чем обращаться к его значению, иначе вы получите панику во время выполнения. 152 | -------------------------------------------------------------------------------- /content/3.golang/6.goroutines.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Горутины 3 | --- 4 | 5 | ## Что такое горутина? 6 | 7 | Горутина - это функция, которая может выполняться параллельно с другими функциями в одном адресном пространстве. Горутины - это особенность языка программирования Go, который позволяет создавать легковесные потоки исполнения. Горутины могут обмениваться данными с помощью каналов, которые являются потокобезопасными структурами данных. 8 | 9 | Источники: 10 | - [Habr](https://habr.com/ru/companies/otus/articles/527748/) 11 | - [Habr](https://habr.com/ru/articles/141853/) 12 | - [Metanit](https://metanit.com/go/tutorial/7.1.php) 13 | 14 | ## Чем горутина отличается от треда? 15 | 16 | Горутина отличается от треда несколькими способами: 17 | 18 | - Горутина имеет меньший размер стека, чем тред, и может динамически его расширять при необходимости. 19 | - Горутина не связана с конкретным системным потоком, а управляется планировщиком Go, который может переключать горутины между разными потоками. 20 | - Горутина может быть запущена с помощью ключевого слова go, в то время как тред требует вызова специальной функции или библиотеки. 21 | - Горутина может общаться с другими горутинами через каналы, которые обеспечивают синхронизацию и безопасность данных. Треды же обычно используют разделяемую память и механизмы блокировки. 22 | 23 | Источники: 24 | - [Habr](https://habr.com/ru/articles/141853/) 25 | - [Medium](https://medium.com/nuances-of-programming/%D0%BA%D0%BE%D0%BD%D0%BA%D1%83%D1%80%D0%B5%D0%BD%D1%82%D0%BD%D0%BE%D1%81%D1%82%D1%8C-%D0%B8-%D0%BF%D0%B0%D1%80%D0%B0%D0%BB%D0%BB%D0%B5%D0%BB%D0%B8%D0%B7%D0%BC-%D0%B2-golang-go-%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D0%B4%D1%83%D1%80%D1%8B-82bae0f92e81) 26 | 27 | ## В чем преимущества горутин над тредами? 28 | 29 | Некоторые преимущества горутин над тредами включают: 30 | 31 | - Более высокую производительность и меньшее потребление ресурсов, так как горутины занимают меньше памяти и переключаются быстрее. 32 | - Более простую и элегантную модель конкурентности, основанную на каналах, которые избегают проблем с блокировками и гонками данных. 33 | 34 | ## Что есть в Golang для многопоточности? 35 | 36 | В Golang для многопоточности есть горутины и каналы. Горутины - это легковесные потоки, которые можно запускать с помощью ключевого слова go. Каналы - это потокобезопасные структуры данных, которые позволяют обмениваться данными между горутинами. GOMAXPROCS - это параметр, который определяет, сколько ядер ЦП используется для одновременного выполнения горутин. 37 | 38 | ## Как можно остановить горутину? 39 | Остановить горутину можно с помощью контекста, канала или таймаута. Контекст позволяет передавать сигналы о завершении работы между горутинами. Канал позволяет отправлять и получать значения между горутинами, в том числе команды на остановку. Таймаут позволяет ограничить время работы горутины и прервать ее, если она не успела выполниться. 40 | 41 | Вот некоторые примеры использования этих механизмов: 42 | 43 | С помощью контекста: 44 | ```go 45 | package main 46 | 47 | import ( 48 | "context" 49 | "fmt" 50 | "time" 51 | ) 52 | 53 | func main() { 54 | // создаем контекст с отменой 55 | ctx, cancel := context.WithCancel(context.Background()) 56 | // запускаем горутину с этим контекстом 57 | go worker(ctx) 58 | // ждем 3 секунды 59 | time.Sleep(3 * time.Second) 60 | // отменяем контекст 61 | cancel() 62 | // ждем еще 2 секунды 63 | time.Sleep(2 * time.Second) 64 | } 65 | 66 | func worker(ctx context.Context) { 67 | for { 68 | select { 69 | case <-ctx.Done(): 70 | // контекст отменен, выходим из горутины 71 | fmt.Println("worker stopped") 72 | return 73 | default: 74 | // продолжаем работать 75 | fmt.Println("worker working") 76 | time.Sleep(time.Second) 77 | } 78 | } 79 | } 80 | ``` 81 | 82 | С помощью канала: 83 | ```go 84 | package main 85 | 86 | import ( 87 | "fmt" 88 | "time" 89 | ) 90 | 91 | func main() { 92 | // создаем канал для передачи сигнала остановки 93 | stop := make(chan bool) 94 | // запускаем горутину с этим каналом 95 | go worker(stop) 96 | // ждем 3 секунды 97 | time.Sleep(3 * time.Second) 98 | // отправляем сигнал остановки в канал 99 | stop <- true 100 | // ждем еще 2 секунды 101 | time.Sleep(2 * time.Second) 102 | } 103 | 104 | func worker(stop chan bool) { 105 | for { 106 | select { 107 | case <-stop: 108 | // получили сигнал остановки, выходим из горутины 109 | fmt.Println("worker stopped") 110 | return 111 | default: 112 | // продолжаем работать 113 | fmt.Println("worker working") 114 | time.Sleep(time.Second) 115 | } 116 | } 117 | } 118 | ``` 119 | 120 | С помощью таймаута: 121 | ```go 122 | package main 123 | 124 | import ( 125 | "fmt" 126 | "time" 127 | ) 128 | 129 | func main() { 130 | // создаем канал для получения результата работы 131 | result := make(chan string) 132 | // запускаем горутину с этим каналом 133 | go worker(result) 134 | // устанавливаем таймаут в 3 секунды 135 | timeout := time.After(3 * time.Second) 136 | select { 137 | case res := <-result: 138 | // получили результат работы до таймаута 139 | fmt.Println("worker finished:", res) 140 | case <-timeout: 141 | // время вышло, прерываем горутину 142 | fmt.Println("worker timeout") 143 | } 144 | } 145 | 146 | func worker(result chan string) { 147 | // имитируем долгую работу 148 | time.Sleep(5 * time.Second) 149 | // отправляем результат в канал 150 | result <- "success" 151 | } 152 | ``` 153 | 154 | ## Когда возникает утечка горутины? 155 | 156 | Утечка горутины возникает, когда горутина продолжает существовать и занимать ресурсы, хотя ее работа уже завершена или не требуется. Это может привести к замедлению работы системы или даже сбою. 157 | 158 | Некоторые причины утечки горутин могут быть: 159 | 160 | - Неправильное использование каналов, например, отправка или получение из закрытого или неполного канала, или блокировка канала без возможности выхода. 161 | - Неэффективное управление контекстами, например, отсутствие отмены или таймаута для контекстов, передаваемых в горутины. 162 | - Неожиданные ошибки или паники, которые прерывают работу горутины, не освобождая ресурсы. 163 | - Неоптимальный дизайн программы, например, создание избыточного количества горутин или недостаточный контроль над их жизненным циклом. 164 | -------------------------------------------------------------------------------- /content/3.golang/7.context.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Контекст 3 | --- 4 | 5 | ## Что такое контекст? 6 | 7 | В Go, контекст (context) используется для передачи сигналов относительно отмены операций, таймаутов и передачи метаданных между API. Это особенно полезно в ситуациях, когда у вас есть множество горутин и вы хотите контролировать их выполнение. 8 | 9 | В Go есть пакет context, который предоставляет функции и типы для работы с контекстами. Вы можете создать контекст с помощью функций `context.Background()` или `context.TODO()`. Затем вы можете создать дочерний контекст с помощью функций `context.WithCancel()`, `context.WithDeadline()`, `context.WithTimeout()`, или `context.WithValue()`. 10 | 11 | Когда контекст отменяется, все горутины, которые получают этот контекст, получают сигнал об отмене, и они должны прекратить свою работу. 12 | 13 | ## Для чего используется контекст? 14 | 15 | Контекст в Go используется для нескольких целей: 16 | 17 | - Отмена операций: Контекст может быть использован для отмены операций. Это особенно полезно, когда у вас есть долгосрочная операция, которая может быть отменена в любой момент. Когда контекст отменяется, все горутины, которые получают этот контекст, получают сигнал об отмене. 18 | - Таймауты: Контекст также может быть использован для установки таймаутов на операции. Это может быть полезно, когда вы хотите ограничить время выполнения операции. 19 | - Передача метаданных: Контекст может быть использован для передачи метаданных между API. Это может быть полезно, когда у вас есть информация, которую нужно передать между различными частями вашего приложения, например, информация о трассировке. 20 | - Контроль над горутинами: Контекст позволяет контролировать выполнение горутин, особенно когда у вас есть множество горутин и вы хотите контролировать их выполнение. 21 | 22 | ## Какие есть виды контекстов? 23 | 24 | - Background: Это базовый контекст, который обычно используется, когда другой контекст не доступен. Это обычно используется в main функции, в тестах и в пакетах, которые не знают, в каком контексте они будут использоваться. Создается с помощью функции context.Background(). 25 | - TODO: Этот контекст также используется, когда контекст не доступен. Он обычно используется, когда не ясно, какой контекст использовать, или когда контекст будет доступен в будущем. Создается с помощью функции context.TODO(). 26 | - WithCancel: Этот контекст предоставляет возможность отмены. Когда функция cancel вызывается, все горутины, которые слушают этот контекст, получают сигнал об отмене. Создается с помощью функции context.WithCancel(parentContext). 27 | - WithDeadline и WithTimeout: Эти контексты предоставляют возможность установить время, после которого контекст будет автоматически отменен. WithDeadline принимает конкретное время, после которого контекст будет отменен, а WithTimeout принимает продолжительность времени, после которой контекст будет отменен. Создаются с помощью функций context.WithDeadline(parentContext, deadline) и context.WithTimeout(parentContext, timeout) соответственно. 28 | - WithValue: Этот контекст предоставляет возможность связать значения с контекстом, которые затем могут быть извлечены в другом месте в коде. Создается с помощью функции context.WithValue(parentContext, key, value). 29 | 30 | ## Как устроен контекст? 31 | 32 | Контекст в Go устроен как древовидная структура, где каждый контекст может иметь одного родителя и множество дочерних элементов. Когда создается новый контекст с помощью функций WithCancel, WithDeadline, WithTimeout или WithValue, он наследует все свойства своего родительского контекста. 33 | 34 | Внутри, контекст представляет собой интерфейс с несколькими методами: 35 | - `Deadline() (deadline time.Time, ok bool)`: Возвращает время, когда работа должна быть завершена. Второе возвращаемое значение ok показывает, был ли установлен крайний срок. 36 | - `Done() <-chan struct{}`: Возвращает канал, который будет закрыт, когда работа должна быть отменена. Если канал закрыт, то Err() вернет не nil. 37 | - `Err()` error: Возвращает ошибку, которая описывает причину завершения контекста. Это может быть context.Canceled или context.DeadlineExceeded. 38 | - `Value(key interface{}) interface{}`: Возвращает значение, связанное с ключом. Если ключа нет, возвращается nil. 39 | 40 | Когда контекст отменяется, все его дочерние контексты также отменяются. Это позволяет управлять группами горутин, которые выполняют связанные задачи. Если одна задача отменяется, все связанные задачи также отменяются 41 | 42 | ## Как работает WithCancel? 43 | 44 | Функция `WithCancel` из пакета `context` в Go создает новый контекст из существующего (родительского) контекста, который может быть отменен. Эта функция возвращает новый контекст и функцию `cancel`, которую можно вызвать, чтобы отменить контекст. 45 | 46 | Вот как это работает: 47 | 48 | ```go 49 | ctx, cancel := context.WithCancel(parentCtx) 50 | ``` 51 | 52 | Здесь `ctx` - это новый контекст, который наследует все свойства от `parentCtx`, и `cancel` - это функция, которую можно вызвать, чтобы отменить `ctx` и все контексты, производные от `ctx`. 53 | 54 | Когда функция `cancel` вызывается, канал `Done` контекста `ctx` закрывается. Все горутины, которые слушают канал `Done`, могут проверить его закрытие, чтобы узнать, был ли контекст отменен. 55 | 56 | Важно всегда вызывать `cancel` в defer (или когда контекст больше не нужен), чтобы освободить ресурсы, связанные с контекстом. Если `cancel` не вызывается, то может произойти утечка ресурсов. 57 | 58 | ```go 59 | defer cancel() // Make sure to cancel when done with context 60 | ``` 61 | 62 | ## Перекидывали логгер в контексте? 63 | 64 | Передача логгера через контекст - это тема, которая вызывает много дискуссий в сообществе Go. Вот некоторые аргументы "за" и "против": 65 | 66 | **За:** 67 | 68 | 1. **Простота**: Передача логгера через контекст может упростить API, так как вам не нужно передавать логгер в каждую функцию. 69 | 70 | 2. **Передача метаданных**: Если вы используете структурированное логирование, вы можете добавить метаданные (например, ID запроса) в логгер, который затем передается через контекст. Это позволяет автоматически включать эти метаданные во все сообщения лога. 71 | 72 | **Против:** 73 | 74 | 1. **Неявность**: Передача логгера через контекст делает его неявным. Это может затруднить понимание того, откуда идут сообщения лога. 75 | 76 | 2. **Неправильное использование контекста**: Документация Go говорит, что контекст должен использоваться для передачи данных, которые должны быть доступны в течение жизненного цикла запроса, а не для передачи опциональных параметров функции. Некоторые люди считают, что передача логгера через контекст - это злоупотребление контекстом. 77 | 78 | 3. **Проблемы с производительностью**: Создание нового контекста для каждого запроса с логгером может привести к увеличению накладных расходов и использованию памяти. 79 | 80 | В конечном итоге, решение о том, передавать ли логгер через контекст, зависит от конкретных требований вашего приложения и вашего стиля программирования. 81 | -------------------------------------------------------------------------------- /content/3.golang/defer.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Defer 3 | --- 4 | 5 | ## Что такое defer, как работает, зачем нужен? 6 | 7 | defer - это ключевое слово в языке программирования Go, которое используется для определения функции, которая должна быть выполнена после возвращения текущей функции, но до возвращения вызвавшей ее функции. Это позволяет очистить ресурсы или выполнить другие задачи, которые необходимо выполнить после завершения основной работы функции. 8 | 9 | Когда вызывается функция с ключевым словом defer, она не выполняется немедленно. Вместо этого она добавляется в список функций, которые будут выполняться в обратном порядке после возвращения текущей функции. Это означает, что последняя функция, вызванная с ключевым словом defer, будет выполнена первой после возврата текущей функции. 10 | 11 | Ключевое слово defer полезно для обеспечения правильной очистки ресурсов, например, для закрытия файлов или сетевых соединений. Его также можно использовать для упрощения обработки ошибок, позволяя функции возвращать значение ошибки, а затем обрабатывать эту ошибку в одном месте после возврата функции. 12 | 13 | Источники: 14 | - [Habr](https://habr.com/ru/articles/492948/) 15 | - [DigitalOcean](https://www.digitalocean.com/community/tutorials/understanding-defer-in-go-ru) 16 | 17 | ## Код в defer выполняется до return или после? 18 | 19 | Оператор defer в Go выполняется перед оператором return. Это позволяет удобно управлять ресурсами, такими как закрытие файлов или освобождение памяти, перед тем как функция завершится. 20 | 21 | defer выполняется после того, как оператор return вычислит возвращаемые значения, но перед фактическим возвращением управления из функции. 22 | 23 | Пример: 24 | ```go 25 | package main 26 | 27 | import "fmt" 28 | 29 | func main() { 30 | defer fmt.Println(changePointer()) 31 | fmt.Println("Third") 32 | } 33 | 34 | func changePointer() string { 35 | defer fmt.Println("Second") 36 | fmt.Println("First") 37 | return "Fourth" 38 | } 39 | ``` 40 | 41 | ## Где инициализируется defer, в стеке или куче? 42 | Дефер-вызовы в Go инициализируются в стеке, а не в куче. Когда функция, содержащая дефер-вызовы, начинает выполняться, эти вызовы помещаются в стек. Затем, когда функция завершается, стек разворачивается, и дефер-вызовы выполняются в обратном порядке, в котором они были помещены в стек. Это позволяет гарантировать, что ресурсы, такие как файлы или соединения, будут освобождены в правильном порядке, даже если в функции произойдет ошибка. 43 | 44 | ## Задача #1 45 | 46 | ```go title="main.go" 47 | package main 48 | 49 | import ( 50 | "fmt" 51 | ) 52 | 53 | func main() { 54 | tmp := 101 55 | fmt.Println(tmp) 56 | defer func() { 57 | fmt.Println(tmp) 58 | }() 59 | tmp = 202 60 | return 61 | } 62 | ``` 63 | 64 | Этот код использует концепцию "замыкания". Замыкание - это функция, которая имеет доступ к переменным из своего внешнего контекста. 65 | В данном случае, анонимная функция, которая откладывается с помощью defer, имеет доступ к переменной tmp из внешнего контекста. 66 | 67 | Когда вы вызываете `defer func() { fmt.Println(tmp) }()`, функция "захватывает" текущее значение tmp на момент вызова. 68 | 69 | Однако в этом случае tmp является ссылкой на переменную во внешнем контексте. Это значит, что если tmp изменяется после вызова defer, то измененное значение будет использоваться в отложенной функции. 70 | 71 | tmp сначала устанавливается в 101, затем выводится, затем откладывается функция, которая выводит tmp, и наконец tmp изменяется на 202. 72 | Когда функция main завершается, отложенная функция вызывается и выводит текущее значение tmp, которое теперь равно 202. 73 | В результате, вывод программы будет 101 и 202. 74 | 75 | ## Задача #2 76 | 77 | ```go title="main.go" 78 | package main 79 | 80 | import ( 81 | "fmt" 82 | ) 83 | 84 | func main() { 85 | tmp := 101 86 | fmt.Println(tmp) 87 | defer func(tmpx int) { 88 | fmt.Println(tmpx) 89 | }(tmp) 90 | tmp = 202 91 | return 92 | } 93 | ``` 94 | 95 | В этом примере мы передаем tmp в отложенную функцию как аргумент. Это означает, что значение tmp будет скопировано в отложенную функцию, а не ссылаться на переменную во внешнем контексте. 96 | 97 | В результате, вывод программы будет 101 и 101. 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /content/3.golang/errors-panics.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Ошибки / Panic 3 | --- 4 | 5 | ## Что такое паника? 6 | Паника в программировании на Go обозначает серьезную ошибку, которая обычно приводит к немедленному завершению программы. Паника вызывает немедленное прекращение выполнения функции и начинает раскрутку стека Вы можете использовать функции defer и recover для перехвата и обработки паники. 7 | 8 | ## Что используется для обработки паники / Куда нужно помещать recover?? 9 | 10 | В Go для обработки паники используются функции `defer` и `recover`: 11 | 12 | - Defer: Функция `defer` в Go позволяет отложить выполнение функции до того, как окружающая функция завершит свое выполнение. Это полезно для обработки ошибок и очистки ресурсов. 13 | - Recover: Функция `recover` используется для перехвата и обработки паник. Она должна вызываться внутри отложенной функции. 14 | 15 | ```go 16 | func someFunction() { 17 | defer func() { 18 | if r := recover(); r != nil { 19 | fmt.Println("Recovered from", r) 20 | } 21 | }() 22 | // Код, который может вызвать панику 23 | } 24 | ``` 25 | 26 | ## Какая парадигма в Golang с точки зрения обработки исключений и ошибок? 27 | 28 | - Явная обработка ошибок: В Go ошибки считаются обычной частью работы программы и должны быть явно обработаны. Если функция может вызвать ошибку, она обычно возвращает значение ошибки в качестве одного из своих возвращаемых значений. 29 | 30 | - Нет исключений: В отличие от некоторых других языков, в Go нет встроенной поддержки исключений. Вместо этого функции возвращают ошибки, которые затем проверяются и обрабатываются. 31 | 32 | - Panic и recover: Go предоставляет функции `panic` и `recover` для обработки серьезных ошибок, которые обычно приводят к завершению программы. Однако эти функции обычно используются только в случае критических ошибок, и их использование для обычной обработки ошибок не рекомендуется. 33 | 34 | - Defer: Go также предоставляет ключевое слово `defer`, которое позволяет отложить выполнение функции до того, как окружающая функция завершит свое выполнение. Это полезно для обработки ошибок и очистки ресурсов. 35 | 36 | ## Какие есть функции для оборачивания и сравнения ошибок? 37 | 38 | В Go есть несколько функций, которые можно использовать для оборачивания и сравнения ошибок: 39 | 40 | Оборачивание ошибок: 41 | 42 | - `fmt.Errorf`: Эта функция используется для создания новой ошибки, которая включает в себя сообщение об ошибке и другую ошибку в качестве причины. Начиная с Go 1.13, `fmt.Errorf` поддерживает оборачивание ошибок с помощью нового модификатора формата `%w`. 43 | 44 | - Сравнение ошибок: 45 | - `errors.Is`: Эта функция используется для проверки, является ли одна ошибка другой ошибкой. Она выполняет проверку на равенство ошибок и учитывает обернутые ошибки. 46 | - `errors.As`: Эта функция используется для проверки, является ли ошибка или обернутая ошибка определенным типом. Если это так, она присваивает эту ошибку указанному значению. 47 | Эти функции позволяют более эффективно работать с ошибками в Go, обеспечивая больше информации об ошибках и делая код более читаемым. 48 | 49 | ## Для чего используются ошибки, а для чего паника? 50 | 51 | В Go ошибки и паника используются для разных целей: 52 | 53 | Ошибки: 54 | - Ошибки в Go обычно используются для обработки ожидаемых проблем, которые могут возникнуть во время выполнения программы. 55 | - Если функция может вызвать ошибку, она обычно возвращает значение ошибки в качестве одного из своих возвращаемых значений. 56 | - Эти ошибки затем проверяются и обрабатываются явно в коде. 57 | 58 | Паника: 59 | - Паника в Go обычно используется для обработки неожиданных ошибок, которые не могут быть обработаны или которые должны привести к немедленному завершению программы. 60 | - Паника вызывает немедленное прекращение выполнения функции и начинает раскрутку стека. 61 | - Вы можете использовать функции `defer` и `recover` для перехвата и обработки паники. 62 | - Важно отметить, что хотя паника может казаться похожей на исключения в некоторых других языках, в Go предпочитают явную обработку ошибок, и паника обычно используется только в случае серьезных ошибок. 63 | -------------------------------------------------------------------------------- /content/3.golang/garbage-collector.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Конструкции 3 | --- 4 | 5 | ## Гарантирует ли порядок выполнения конструкция select-case? 6 | 7 | В Go, порядок выполнения операторов в конструкции select-case не гарантируется. 8 | 9 | Когда у вас есть несколько операторов case в конструкции select, и они все готовы для выполнения (то есть каналы готовы для чтения/записи), Go выбирает один из них случайным образом. 10 | 11 | Это означает, что вы не можете предсказать или контролировать, какой оператор case будет выполнен первым, если у вас есть несколько готовых для выполнения операторов case. Это помогает предотвратить блокировку и гарантировать, что все горутины имеют равные шансы на выполнение. 12 | 13 | Источники: 14 | - [Advicemama](https://advicemama.ru/primenenie-operatora-select-v-yazyke-golang-osnovnye-principy-i-sposoby-ispolzovaniya/) 15 | 16 | ## Как выглядит конструкция утверждения типа switch? 17 | 18 | ```go 19 | switch v := i.(type) { 20 | case Type2: 21 | case SomeType: 22 | default: 23 | } 24 | ``` 25 | 26 | ## Нужно ли в switch-case проставлять break? 27 | 28 | В языке программирования Go, вам не нужно использовать break в конструкции switch-case. В отличие от некоторых других языков программирования, таких как C++ или Java, каждый case в Go автоматически прерывается, и выполнение программы продолжается после конструкции switch-case. Это означает, что после каждого case не нужно явно указывать break. 29 | 30 | Однако, если вы хотите, чтобы выполнение продолжилось и в следующем case, вы можете использовать ключевое слово fallthrough. 31 | 32 | Надеюсь, это помогло вам лучше понять, как работает конструкция switch-case в Go! Если у вас есть еще вопросы, не стесняйтесь задавать. 33 | -------------------------------------------------------------------------------- /content/3.golang/generics.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Дженерики 3 | --- 4 | 5 | ## Чем кодогенерация отличается от дженериков? 6 | 7 | Кодогенерация и дженерики - это два способа написания кода, который не зависит от конкретных типов данных. Однако, они отличаются по тому, когда и как происходит привязка к типам. 8 | 9 | Кодогенерация - это процесс создания кода из другого кода или данных с помощью специальных инструментов или команд. Кодогенерация позволяет писать обобщенный код, который затем преобразуется в конкретный код для каждого типа данных. Кодогенерация происходит на этапе компиляции или до него, то есть до выполнения программы. Кодогенерация может быть полезна для увеличения производительности, избежания дублирования кода или реализации сложной логики. 10 | 11 | Дженерики - это способ написания кода, который параметризован по типам данных. Дженерики позволяют писать функции и типы, которые работают с любыми типами данных, передавая их в качестве аргументов. Дженерики происходят на этапе выполнения, то есть во время работы программы. Дженерики могут быть полезны для повышения выразительности, уменьшения количества кода или поддержки полиморфизма. 12 | 13 | ## В какой версии появились дженерики? 14 | 15 | В языке Go до версии 1.18 не было поддержки дженериков, поэтому разработчики часто использовали кодогенерацию или рефлексию для написания обобщенного кода. 16 | 17 | В версии 1.18 добавлена поддержка дженериков с помощью параметров типов (type parameters), которые позволяют определять функции и типы, которые принимают любые типы данных в качестве аргументов. 18 | 19 | ## Как работают дженерики под капотом? 20 | 21 | Дженерики под капотом в golang работают с помощью механизма, который называется type substitution (подстановка типов). Это означает, что при компиляции программы, компилятор заменяет параметры типов в дженерических функциях и типах на конкретные типы, которые передаются в качестве аргументов. 22 | 23 | Например, если мы имеем такую дженерическую функцию: 24 | 25 | ```go 26 | func Map[F, T any](s T, f F) []T { 27 | r := make([]T, len(s)) 28 | for i, v := range s { 29 | r[i] = f(v) 30 | } 31 | return r 32 | } 33 | ``` 34 | 35 | И мы вызываем ее так: 36 | 37 | ```go 38 | s := []int{1, 2, 3} 39 | f := func(x int) string { return strconv.Itoa(x) } 40 | t := Map(s, f) 41 | ``` 42 | 43 | То компилятор преобразует ее в такую функцию: 44 | 45 | ```go 46 | func Map_int_string(s []int, f func(int) string) []string { 47 | r := make([]string, len(s)) 48 | for i, v := range s { 49 | r[i] = f(v) 50 | } 51 | return r 52 | } 53 | ``` 54 | 55 | И вызывает ее так: 56 | 57 | ```go 58 | s := []int{1, 2, 3} 59 | f := func(x int) string { return strconv.Itoa(x) } 60 | t := Map_int_string(s, f) 61 | ``` 62 | 63 | Таким образом, дженерики под капотом в golang не требуют дополнительной памяти или рефлексии, так как они превращаются в обычный код для каждого типа данных. 64 | 65 | Однако, это также означает, что дженерики под капотом в golang могут привести к увеличению размера исполняемого файла, так как для каждого типа данных создается своя версия дженерической функции или типа 66 | 67 | Источники: 68 | - [Habr](https://habr.com/ru/companies/karuna/articles/552944/) 69 | - [Habr](https://habr.com/ru/companies/skillfactory/articles/657853/) 70 | 71 | -------------------------------------------------------------------------------- /content/3.golang/overall.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Общие вопросы 3 | --- 4 | 5 | ## Что такое type switch? 6 | 7 | > **`Type switch`** в Go - это способ проверить, какого типа является значение, хранящееся в интерфейсе. Это похоже на обычный **`switch`**, но вместо значений в кейсах указываются типы. 8 | 9 | ```go 10 | package main 11 | 12 | import "fmt" 13 | 14 | func do(i interface{}) { 15 | switch v := i.(type) { 16 | case int: 17 | fmt.Printf("Twice %v is %v\n", v, v*2) 18 | case string: 19 | fmt.Printf("%q is %v bytes long\n", v, len(v)) 20 | default: 21 | fmt.Printf("I don't know about type %T!\n", v) 22 | } 23 | } 24 | 25 | func main() { 26 | do(21) 27 | do("hello") 28 | do(true) 29 | } 30 | ``` 31 | 32 | ## Какие типы данных есть в Golang? 33 | 34 | 1. **Целочисленные типы**: **`int8`**, **`int16`**, **`int32`**, **`int64`**, **`uint8`**, **`uint16`**, **`uint32`**, **`uint64`**, **`byte`** (синоним для **`uint8`**), **`rune`** (синоним для **`int32`**), **`int`** и **`uint`.** 35 | 2. **Числа с плавающей точкой**: **`float32`** и **`float64`.** 36 | 3. **Строки**: **`string`**. 37 | 4. **Булев тип**: **`bool`**. 38 | 5. **Составные типы**: Массивы, срезы, структуры, ассоциативные массивы 39 | 40 | ## Какой длины руна? 41 | 42 | **`rune`** - это алиас для **`int32`**. Это означает, что **`rune`** занимает 4 байта или 32 бита. Каждая **`rune`** представляет собой один Юникод символ. Например, строка может быть преобразована в срез **`rune`** (**`[]rune`**), и каждый элемент этого среза будет представлять собой один Юникод символ. 43 | 44 | ## Как реализовать Enum в Golang? 45 | 46 | В Go нет встроенной поддержки перечислений (enum), но их можно эмулировать с помощью констант. Вот пример реализации enum в Go: 47 | 48 | ```go title="main.go" 49 | package main 50 | 51 | import "fmt" 52 | 53 | type Season int 54 | 55 | const ( 56 | Summer Season = iota 57 | Autumn 58 | Winter 59 | Spring 60 | ) 61 | 62 | func (s Season) String() string { 63 | return [...]string{"Лето", "Осень", "Зима", "Весна"}[s] 64 | } 65 | 66 | func main() { 67 | s := Winter 68 | fmt.Println(s) 69 | } 70 | ``` 71 | 72 | ## Что если во время компиляции переполним тип? В int8 запишем 256? 73 | 74 | Если вы попытаетесь записать значение, превышающее максимально допустимое для данного типа, в Go, то произойдет переполнение типа. 75 | 76 | Например, **`int8`** может хранить значения от -128 до 127. Если вы попытаетесь записать 256 в переменную типа **`int8`**, то произойдет переполнение. В результате, вместо 256 будет записано значение, которое соответствует остатку от деления 256 на 256 (количество возможных значений для **`int8`**), то есть 01. 77 | 78 | Важно отметить, что такое поведение может привести к неожиданным результатам, поэтому всегда следует убедиться, что значения, которые вы записываете в переменные, не превышают максимально допустимые для их типа 79 | 80 | ## Что происходит при переполнении числа, например в uint8 выйдем за границу на 1? 81 | 82 | Если вы попытаетесь записать значение, превышающее максимально допустимое для данного типа, в Go, то произойдет переполнение типа. 83 | 84 | Например, **`uint8`** может хранить значения от 0 до 255. Если вы попытаетесь записать 256 в переменную типа **`uint8`**, то произойдет переполнение. В результате, вместо 256 будет записано значение, которое соответствует остатку от деления 256 на 256 (количество возможных значений для **`uint8`**), то есть 0. 85 | 86 | Важно отметить, что такое поведение может привести к неожиданным результатам, поэтому всегда следует убедиться, что значения, которые вы записываете в переменные, не превышают максимально допустимые для их типа. 87 | 88 | ## Какие технологические преимущества экосистемы Go вы можете назвать? 89 | 90 | В случае с экосистемой Go к потенциальным преимуществам можно отнести: 91 | 92 | - Большое и активное сообщество разработчиков, которые вносят свой вклад в развитие языка Go и его экосистемы. 93 | - Богатый набор библиотек и инструментов, облегчающих разработку, тестирование и развертывание приложений на Go. 94 | - Совместимость с широким спектром платформ и систем, что делает его универсальным выбором для создания приложений. 95 | - Сильная поддержка конкурентного и параллельного программирования, что позволяет повысить производительность и масштабируемость приложений. 96 | - Упор на простоту и читабельность, что облегчает изучение и использование языка разработчиками и способствует развитию экосистемы. 97 | 98 | ## Go - императивный или декларативный? А в чем разница? 99 | 100 | Go - императивный язык программирования. Это означает, что в нем используются инструкции, которые описывают, как выполнять задачу. В декларативных языках программирования, например, SQL, описывается, что должно быть сделано, а не как это сделать. 101 | 102 | ## Какие особенности Go вы можете назвать? 103 | 104 | - Статическая типизация 105 | - Сборка мусора 106 | - Конкурентность 107 | - Параллелизм 108 | - Отсутствие исключений 109 | - Строгая типизация 110 | - Строгая проверка ошибок 111 | 112 | ## Почему треды в Go - легковесные 113 | 114 | В Go горутины - это легковесные потоки, управляемые средой выполнения Go. Они более легковесны, чем потоки операционной системы, потому что занимают меньше места в памяти, обычно около 2 килобайт, по сравнению с памятью потока операционной системы, которая может составлять несколько мегабайт. Такой меньший объем памяти позволяет среде выполнения Go создавать большое количество горутин, часто тысячи или даже миллионы, не исчерпывая системных ресурсов. 115 | 116 | Кроме того, среда выполнения Go использует планировщик, чтобы мультиплексировать горутины на меньшее число потоков операционной системы, что позволяет снизить накладные расходы на создание и управление потоками. Планировщик также может выполнять контекстные переключения между горутинами без участия операционной системы, что может быть быстрее, чем полное контекстное переключение между потоками операционной системы. 117 | 118 | В целом, сочетание меньшего объема памяти и планировщика пользовательского пространства делает горутины легким и эффективным механизмом для параллельного выполнения в программах на Go. 119 | 120 | ## Какие средства обобщенного программирования есть в Go? 121 | 122 | В Go 1.18 появилась поддержка обобщенного программирования. Обобщенное программирование - это способность писать функции и типы, которые работают с любыми типами данных, а не только с конкретными типами данных. В Go 1.18 обобщенное программирование реализовано с помощью параметризованных типов и функций. 123 | 124 | ## Какие средства метапрограммирования есть в Go? 125 | 126 | В Go нет средств метапрограммирования, таких как макросы, шаблоны. Вместо этого Go предоставляет мощную систему интерфейсов, которая позволяет писать гибкий и расширяемый код, который может быть легко адаптирован к различным типам данных. 127 | 128 | ## Какая сортировка используется в Golang? 129 | 130 | В Go используется алгоритм сортировки "Quicksort", который является эффективным алгоритмом сортировки с асимптотической сложностью O(n log n). Этот алгоритм используется в стандартной библиотеке "sort" для сортировки слайсов и пользовательских коллекций данных. 131 | 132 | ## Какая кодировка используется в Golang? 133 | 134 | Go поддерживает UTF-8 кодировку по умолчанию для всех строковых типов. Это означает, что вы можете безопасно использовать Unicode символы в строках в Go. Библиотека "unicode/utf8" предоставляет функции для работы с UTF-8 кодированными строками. 135 | 136 | ## Можно ли в Golang создать статический метод? 137 | 138 | В Go нет классов и, следовательно, нет статических методов в традиционном понимании этого термина, как в языках, таких как Java или C++. Однако, вы можете создать функции, которые привязаны к определенному типу данных, что близко к понятию статического метода. 139 | 140 | Вот пример: 141 | 142 | ```go 143 | type MyType struct { 144 | value int 145 | } 146 | 147 | func (t MyType) StaticMethod() int { 148 | return t.value * 2 149 | } 150 | ``` 151 | 152 | В этом примере `StaticMethod` является функцией, которая привязана к типу `MyType`. Вы можете вызвать эту функцию на экземпляре `MyType`. 153 | 154 | ```go 155 | t := MyType{value: 5} 156 | result := t.StaticMethod() // result is 10 157 | ``` 158 | 159 | Это не совсем то же самое, что статический метод в языках с классами, но это ближайший аналог в Go. 160 | -------------------------------------------------------------------------------- /content/3.golang/package.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Пакеты 3 | --- 4 | 5 | ## Что такое пакет? 6 | 7 | В языке программирования Go, пакет (package) - это коллекция исходных файлов Go, которые находятся в одной и той же директории. Все файлы в одном пакете должны иметь одно и то же имя пакета в начале файла. 8 | 9 | Пакеты в Go используются для организации и повторного использования кода. Они представляют собой удобный способ разделения кода на отдельные модули. Это позволяет определить пакет с нужной функциональностью один раз и затем использовать его многократно в различных программах. 10 | 11 | В Go есть два типа пакетов: исполняемые (executable) и библиотеки (reusable). Исполняемые пакеты должны иметь имя **`main`** и содержать функцию **`main`**, которая является входной точкой в приложение. Все остальные пакеты являются библиотеками и не могут быть напрямую выполнены. 12 | 13 | ## Расскажи про папку internal 14 | 15 | В Go, папка **`internal`** используется для хранения кода, который не должен быть доступен за пределами текущего модуля. Это означает, что любой код, который находится в папке **`internal`** (или в подпапках этой папки), может быть импортирован и использован только внутри того же модуля. Это полезно, когда вы хотите скрыть определенные части вашего кода от внешнего мира, чтобы предотвратить его неправильное использование. 16 | 17 | ```none title="Пример структуры проекта с папкой internal" 18 | test 19 | ├─ go.mod 20 | ├─ internal 21 | │ └─ handler 22 | │ └─ opkg.go 23 | ├─ main.go 24 | ``` 25 | -------------------------------------------------------------------------------- /content/3.golang/race-condition.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Гонка данных 3 | --- 4 | 5 | ## Что такое race condition? 6 | 7 | Состояние гонки (англ. race condition), также известное как конкуренция, - это ошибка проектирования многопоточной системы или приложения, при которой работа системы или приложения зависит от того, в каком порядке выполняются части кода. 8 | 9 | Это может произойти, когда две или более операций должны выполняться в последовательности, но из-за неконтролируемых событий они выполняются в непредсказуемом порядке. В результате система или приложение может вести себя непредсказуемо или даже вызывать сбои. 10 | 11 | Состояние гонки - это “плавающая” ошибка (гейзенбаг), проявляющаяся в случайные моменты времени и “пропадающая” при попытке её локализовать. Из-за неконтролируемого доступа к общей памяти состояние гонки может приводить к совершенно различным ошибкам, которые могут проявляться в непредсказуемые моменты времени, а попытка повторения ошибки в целях отладки со схожими условиями работы может оказаться безуспешной. 12 | 13 | Основными последствиями могут быть: утечки памяти, ошибки сегментирования, порча данных, уязвимости, взаимные блокировки, утечки других ресурсов, например файловых дескрипторов. 14 | 15 | Источники: 16 | - [Habr](https://habr.com/ru/articles/764234/) 17 | 18 | ## Как обнаружить race condition? 19 | В Go есть встроенный инструмент для обнаружения состояний гонки, который можно использовать при запуске или сборке вашего приложения. Вы можете использовать флаг `-race` для обнаружения состояний гонки. Например, если ваш файл называется `write.go`, команда будет выглядеть так: `go run -race write.go` или `go build -race write.go`. 20 | 21 | При запуске этой команды Go выводит на стандартный вывод, который сообщает нам о наличии состояния гонки. Это очень полезный инструмент для обнаружения и исправления состояний гонки в ваших программах на Go. 22 | 23 | Также стоит отметить, что состояния гонки могут быть сложными для обнаружения и воспроизведения, поскольку они могут проявляться только при определенных условиях выполнения и могут не проявляться при повторном запуске того же кода. Поэтому использование инструментов, таких как детектор состояний гонки в Go, может быть очень полезным. 24 | 25 | ## Какие есть способы устранения race condition? 26 | 27 | - Использование Mutex: (RW)Mutex предоставляет взаимоисключающую блокировку, которая позволяет только одной горутине в любой момент времени иметь доступ к защищенным данным. 28 | - Использование каналов: Каналы в Go обеспечивают синхронизацию между горутинами и могут быть использованы для предотвращения состояний гонки. 29 | - Использование атомарных операций: Пакет sync/atomic в Go предоставляет функции для атомарных операций, которые могут быть использованы для безопасного доступа к данным из нескольких горутин. 30 | 31 | ## Есть глобальный слайс и я в разных горутинах присваиваю ему аппенд к нему же одного элемента. Будет ли гонка? 32 | 33 | Да, будет состояние гонки. Функция append не является потокобезопасной, и если вы пытаетесь одновременно добавить элементы в слайс из разных горутин, это может привести к состоянию гонки. 34 | 35 | В Go, когда вы вызываете append, он может изменить размер слайса. Если размер нового слайса больше текущей вместимости, Go создаст новый массив в памяти и скопирует в него все элементы. Если две горутины одновременно пытаются добавить элементы, они могут получить разные копии массива, что приведет к непредсказуемым результатам. 36 | 37 | Чтобы избежать состояния гонки, вы можете использовать мьютекс (sync.Mutex или sync.RWMutex) для синхронизации доступа к слайсу. 38 | 39 | ## Есть глобальный слайс и я в разных горутинах присваиваю ему аппенд к нему же одного элемента. Будет ли гонка? 40 | 41 | Да, будет гонка данных. В Go, горутины выполняются параллельно, и если они обе пытаются изменить одну и ту же переменную (в данном случае, глобальный слайс), это может привести к неопределенному поведению. Это известно как гонка данных. 42 | 43 | Чтобы избежать этого, вы можете использовать мьютексы или каналы для синхронизации доступа к общим ресурсам. 44 | -------------------------------------------------------------------------------- /content/3.golang/sheduler.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Планировщик 3 | --- 4 | 5 | ## Как работает планировщик в Golang? 6 | 7 | Планировщик в Golang - это механизм, который отвечает за распределение и выполнение горутин (G) на логических процессорах (P), которые в свою очередь работают на физических потоках (M) операционной системы. Планировщик в Golang использует модель M:N, то есть может запускать произвольное количество горутин на произвольном количестве потоков. 8 | 9 | Горутина (G) - это легковесный поток выполнения, который может быть создан, переключен и уничтожен планировщиком Go без взаимодействия с операционной системой. Горутины занимают мало памяти и могут быть заспавнены в большом количестве. Горутины не привязаны к конкретному потоку или процессору, а могут динамически менять свой контекст выполнения. 10 | 11 | Логический процессор (P) - это абстракция, которая представляет собой ресурс, необходимый для выполнения горутин. Каждый P имеет свою локальную очередь (LRQ), в которой хранятся горутины, ожидающие запуска. Количество P обычно равно количеству ядер в системе, чтобы максимально использовать параллелизм. P не может выполнять горутину самостоятельно, а нуждается в M для этого. 12 | 13 | Физический поток (M) - это поток операционной системы, который может быть связан с P и выполнять горутину из LRQ. M может переключаться между разными P в зависимости от ситуации. Например, если M блокируется на системном вызове или операции ввода-вывода, он может быть отсоединен от P и заменен другим M. Также M может участвовать в процессе work stealing, когда он пытается украсть горутину из LRQ или глобальной очереди (GRQ) другого P. 14 | 15 | Планировщик Go использует алгоритм work stealing для балансировки нагрузки между P. Если LRQ одного P пуста, он может попытаться украсть горутину из LRQ или GRQ другого P. Это позволяет эффективно использовать ресурсы и избегать простоя. 16 | 17 | Источники: 18 | - [Habr](https://habr.com/ru/articles/478168/) 19 | - [Habr](https://habr.com/ru/articles/489862/) 20 | - [Backend Interview](https://backendinterview.ru/goLang/scheduler.html) 21 | 22 | ## В чем разница между вытесняющим и кооперативным планировщиком? 23 | 24 | Вытесняющий планировщик - это такой планировщик, который может прервать выполнение текущей задачи в любой момент и передать управление другой задаче, если он посчитает это необходимым. Вытесняющий планировщик обычно использует таймер или другие прерывания, чтобы определить, когда нужно сменить задачу. Вытесняющий планировщик позволяет более эффективно распределять ресурсы между задачами и обеспечивать более высокую отзывчивость системы. 25 | 26 | Кооперативный планировщик - это такой планировщик, который не может прервать выполнение текущей задачи, пока она сама не отдаст управление. Кооперативный планировщик полагается на то, что задачи будут добровольно освобождать процессор, когда они закончат свою работу или будут ждать ввода-вывода. Кооперативный планировщик проще в реализации и потребляет меньше ресурсов, но он может привести к проблемам с производительностью и справедливостью, если одна из задач будет занимать процессор слишком долго. 27 | 28 | Если вы хотите узнать больше о том, в чем разница между вытесняющим и кооперативным планировщиком, вы можете посмотреть следующие источники: 29 | - [Планирование в Go: Часть II — Планировщик Go](https://habr.com/ru/articles/489862/) 30 | - [Разница между вытесняющим и невытесняющим планированием в операционных системах](https://t-fakt.ru/raznica/raznicza-mezhdu-vytesnyayushhim-i-nevytesnyayushhim-planirovaniem-v-operaczionnyh-sistemah/) 31 | - [Упреждающее и невытесняющее планирование](https://www.guru99.com/ru/preemptive-vs-non-preemptive-scheduling.html) 32 | 33 | ## Какой тип планировщика в Golang? 34 | 35 | Планировщик в Golang - это вытесняющий планировщик с кооперативными элементами. 36 | 37 | Это означает, что он может прервать выполнение текущей горутины в любой момент и передать управление другой горутине, если он посчитает это необходимым, но он также полагается на то, что горутины будут добровольно освобождать процессор, когда они будут ждать ввода-вывода или блокироваться на каналах. 38 | 39 | Планировщик в Golang использует таймер или другие прерывания, чтобы определить, когда нужно сменить горутину, а также учитывает приоритеты и аффинность горутин к процессорам. 40 | 41 | Источники: 42 | - [Планирование в Go: Часть II — Планировщик Go](https://habr.com/ru/articles/489862/) 43 | - [Планирование в Go: Часть I — Планировщик ОС](https://habr.com/ru/articles/478168/) 44 | 45 | ## В планировщике до версии 1.15 какие операции приводят к переключению контекста горутин? 46 | 47 | В планировщике Golang до версии 1.15 переключение контекста горутин происходило только при выполнении следующих операций: 48 | 49 | - Ожидание ввода-вывода (I/O), такого как чтение или запись в файл, сеть, канал или таймер. 50 | - Блокировка на мьютексе (mutex) или другом примитиве синхронизации. 51 | - Вызов функции runtime.Gosched, которая явно передает управление другой горутине. 52 | - Вызов функции runtime.GC, которая запускает сборку мусора и приостанавливает все горутины. 53 | - Вызов функции runtime.LockOSThread, которая привязывает горутину к одному системному потоку и не позволяет ей переключаться на другой. 54 | 55 | В версии 1.15 планировщик Golang стал вытесняющим, то есть он может прервать выполнение любой горутины, которая работает слишком долго, и передать управление другой горутине, если он посчитает это необходимым. Это улучшило отзывчивость и справедливость системы, а также уменьшило вероятность возникновения состояний гонки и взаимных блокировок. 56 | 57 | ## Можно ли руками переключить контекст горутины? 58 | 59 | В общем случае, нет, нельзя руками переключить контекст горутины, так как это делает планировщик Golang автоматически, когда он посчитает это необходимым. 60 | 61 | Однако, в некоторых редких случаях, можно руками переключить контекст горутины, используя низкоуровневые функции из пакета runtime, такие как runtime.Gopark и runtime.Goready, которые позволяют приостановить и возобновить горутину по собственному условию. Эти функции не рекомендуются для обычного использования, так как они могут привести к ошибкам и несовместимостям с планировщиком Golang. 62 | 63 | Источники: 64 | - [Планирование в Go: Часть II — Планировщик Go](https://habr.com/ru/articles/489862/) 65 | - [Планирование в Go: Часть I — Планировщик ОС](https://habr.com/ru/articles/478168/) 66 | 67 | ## Сколько потоков операционной системы мы можем создать? 68 | 69 | Количество потоков операционной системы, которые мы можем создать в Golang, зависит от нескольких факторов, таких как: 70 | 71 | - Количество горутин (goroutines), которые мы запускаем в нашей программе. 72 | - Количество процессоров или ядер на компьютере. По умолчанию Golang создает столько системных потоков, сколько доступно логических процессоров на компьютере, но это можно изменить с помощью переменной окружения GOMAXPROCS. 73 | - Размер стека для каждого системного потока. Стек - это область памяти, которая используется для хранения локальных переменных, параметров и адресов возврата функций. Размер стека ограничивает количество системных потоков, которые могут быть созданы, так как память не бесконечна. 74 | 75 | ## Расскажи про глобальную и локальную очереди 76 | 77 | Глобальная и локальная очереди в Golang - это структуры данных, которые используются планировщиком Go для распределения горутин между логическими процессорами (P). Горутина - это легковесный поток выполнения, который может быть переключен планировщиком без блокировки операционной системы. 78 | 79 | Глобальная очередь выполнения (GRQ) содержит горутины, которые еще не были назначены ни одному логическому процессору. Когда логический процессор создается или освобождается, он может взять горутину из GRQ и поместить ее в свою локальную очередь выполнения (LRQ). LRQ содержит горутины, которые принадлежат конкретному логическому процессору и ожидают своего запуска. 80 | 81 | Планировщик Go использует алгоритм work stealing для балансировки нагрузки между логическими процессорами. Если LRQ одного логического процессора пуста, он может попытаться украсть горутину из LRQ другого логического процессора или из GRQ. Это позволяет эффективно использовать ресурсы и избегать простоя. 82 | 83 | Источники: 84 | - [Habr](https://habr.com/ru/articles/743266/) 85 | - [Habr](https://habr.com/ru/articles/489862/) 86 | 87 | ## Может ли горутина начать работу на одном P, приостановиться и продолжить работу на другом P? 88 | 89 | Да, горутина может переключаться между разными логическими процессорами (P) во время своей работы. Это может произойти по нескольким причинам, например: 90 | 91 | - Горутина блокируется на операции ввода-вывода или системном вызове, и планировщик Go переназначает ее P другой горутине. 92 | - Горутина добровольно отступает от своего P, вызывая функцию yield или resume, которые позволяют использовать корутины в Go. 93 | - Горутина истекает свой квант времени, и планировщик Go прерывает ее выполнение и переводит ее в конец своей локальной очереди (LRQ). 94 | - Горутина становится жертвой work stealing, когда другой P, у которого пустая LRQ, пытается украсть горутину из LRQ или глобальной очереди (GRQ) другого P. 95 | 96 | Таким образом, горутины в Go не привязаны к конкретному P на всю свою жизнь, а могут динамически менять свой контекст выполнения в зависимости от ситуации. Это повышает эффективность и отзывчивость приложений, написанных на Go. 97 | 98 | Источники: 99 | - [Habr](https://habr.com/ru/articles/775532/) 100 | - [Habr](https://habr.com/ru/articles/743266/) 101 | - [Habr](https://habr.com/ru/articles/489862/) 102 | 103 | ## Может ли одна очередь украсть горутины у другой? 104 | 105 | Да, одна очередь может украсть горутины у другой в процессе work stealing. Это алгоритм, который позволяет планировщику Go балансировать нагрузку между логическими процессорами (P). Когда один P имеет пустую локальную очередь (LRQ), он может попытаться украсть горутину из LRQ или глобальной очереди (GRQ) другого P. Это увеличивает вероятность того, что каждый P будет иметь работу, и избегает простоя. 106 | 107 | ## В чем профит горутин, если мы можем заспавнить миллион потоков в процессе, и почему упадет процесс с таким количеством потоков, или нет? 108 | 109 | Профит горутин в том, что они легковеснее и эффективнее обычных потоков. Горутины занимают меньше памяти (порядка килобайтов), чем потоки (порядка мегабайтов), и могут динамически расти и уменьшаться в зависимости от потребностей. Горутины также имеют меньший оверхед на создание, переключение и уничтожение, чем потоки, которые требуют взаимодействия с операционной системой. 110 | 111 | Если мы попытаемся заспавнить миллион потоков в процессе, то скорее всего процесс упадет из-за нехватки памяти или ресурсов. Каждый поток требует выделения стека, регистров, дескрипторов и других ресурсов, которые ограничены операционной системой. Кроме того, большое количество потоков приводит к частому переключению контекста, которое снижает производительность и увеличивает задержки. 112 | 113 | В отличие от потоков, горутины могут быть заспавнены в большом количестве без существенных потерь производительности и памяти. Планировщик Go оптимально распределяет горутины между логическими процессорами (P), используя глобальную и локальные очереди, а также алгоритм work stealing. Таким образом, горутины позволяют писать высокопроизводительные и масштабируемые приложения на Go. 114 | -------------------------------------------------------------------------------- /content/3.golang/structs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Структуры 3 | --- 4 | 5 | ## Имеет ли значение порядок полей в структуре? 6 | 7 | Да, порядок полей в структуре Go может иметь значение в некоторых случаях. 8 | 9 | Выравнивание памяти: Go размещает поля структур в соответствии с гарантиями выравнивания для типов полей. Это может влиять на общий размер структуры и использование памяти. 10 | 11 | Источники: 12 | - [Stackoverflow](https://ru.stackoverflow.com/questions/1541584/%d0%9f%d0%be%d1%80%d1%8f%d0%b4%d0%be%d0%ba-%d0%bf%d0%b5%d1%80%d0%b5%d0%bc%d0%bd%d0%bd%d1%8b%d1%85-%d0%b2-%d1%81%d1%82%d1%80%d1%83%d0%ba%d1%82%d1%83%d1%80%d0%b5-go-golang) 13 | 14 | 15 | ## От чего зависит размер выравнивания в структуре? 16 | 17 | Размер выравнивания в структуре в Go зависит от нескольких факторов: 18 | 19 | - Тип данных: Размер выравнивания обычно зависит от типа данных. Например, int32 требует выравнивания по 4 байтам. 20 | - Порядок полей: Порядок полей в структуре может влиять на размер выравнивания. Если поля структуры переставить, размер структуры может измениться. 21 | - Архитектура системы: Размер выравнивания также может зависеть от архитектуры системы. Например, на 32-битной архитектуре структуры выравниваются под 4 байта. 22 | 23 | Важно отметить, что выравнивание влияет на производительность и использование памяти, поэтому его следует учитывать при проектировании структур. 24 | -------------------------------------------------------------------------------- /content/3.golang/sync-primitives.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Примитивы синхронизации 3 | --- 4 | 5 | ## Какие примитивы синхронизации есть в Golang? 6 | 7 | Примитивы синхронизации - это специальные конструкции, которые позволяют координировать и контролировать доступ к общим ресурсам в многопоточных или конкурентных программах. В Golang есть несколько пакетов, которые предоставляют различные примитивы синхронизации, такие как: 8 | 9 | sync - содержит базовые примитивы, такие как мьютексы (mutexes), блокировки чтения-записи (read-write locks), группы ожидания (wait groups), однократные исполнители (once), условные переменные (condition variables) и другие. 10 | sync/atomic - содержит функции для атомарных операций с примитивными типами данных, такими как целые числа, указатели, булевы значения и т.д. 11 | context - содержит примитивы для управления жизненным циклом и отменой горутин (goroutines), а также для передачи метаданных между ними. 12 | chan - является встроенным типом данных, который представляет собой канал для обмена данными между горутинами, который может быть использован как примитив синхронизации или коммуникации. 13 | 14 | Источники: 15 | - [Medium](https://medium.com/german-gorelkin/synchronization-primitives-go-8857747d9660) 16 | - [Medium](https://medium.com/nuances-of-programming/%D0%BF%D1%80%D0%B8%D0%BC%D0%B8%D1%82%D0%B8%D0%B2%D1%8B-%D1%81%D0%B8%D0%BD%D1%85%D1%80%D0%BE%D0%BD%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8-%D0%B2-go-403b163c49d5) 17 | 18 | 19 | ## Чем мьютекс отличается от семафора? 20 | 21 | Мьютекс и семафор - это два способа синхронизации доступа к общим ресурсам в многопоточных программах. Они имеют разные преимущества и недостатки, и их выбор зависит от конкретной задачи. 22 | 23 | Вот некоторые основные различия между мьютексом и семафором: 24 | 25 | Мьютекс - это объект, который позволяет блокировать и разблокировать доступ к одному ресурсу, тогда как семафор - это переменная, которая позволяет контролировать доступ к нескольким ресурсам. 26 | Мьютекс может быть захвачен или освобожден только тем потоком, который его заблокировал, тогда как семафор может быть изменен любым потоком. 27 | Мьютекс обычно используется для взаимного исключения, т.е. для предотвращения состояний гонки, тогда как семафор обычно используется для координации, т.е. для синхронизации действий между потоками. 28 | Мьютекс имеет только два состояния: заблокирован или разблокирован, тогда как семафор имеет целочисленное значение, которое может быть больше нуля. 29 | 30 | Источники: 31 | - [Guru99](https://www.guru99.com/ru/mutex-vs-semaphore.html) 32 | 33 | ## Что такое atomic и чем отличается от мьютекса? 34 | 35 | Атомики (atomics) - это функции, которые позволяют выполнять простые операции с общим ресурсом, такие как чтение, запись, инкремент, декремент, обмен или сравнение и присваивание. Атомики гарантируют, что эти операции будут выполнены атомарно, то есть без вмешательства других горутин. Атомики реализованы в пакете sync/atomic, и для их использования нужно вызывать соответствующие функции с указателем на ресурс. Атомики подходят для ситуаций, когда требуется выполнить быстрые и простые операции с ресурсом, такие как увеличение или уменьшение счетчика, установка или сброс флага, или проверка или изменение состояния. 36 | 37 | В общем, мьютексы и атомики имеют следующие отличия: 38 | - Мьютексы работают с любыми типами данных, а атомики - только с примитивными типами, такими как int, uint, bool, pointer и т.д. 39 | - Мьютексы требуют явной блокировки и разблокировки, а атомики - нет. 40 | - Мьютексы позволяют выполнять любые операции с ресурсом, а атомики - только ограниченный набор операций. 41 | - Мьютексы могут быть более медленными и затратными, чем атомики, из-за переключения контекста и ожидания блокировки. 42 | - Мьютексы могут приводить к взаимным блокировкам (deadlocks), если не использовать их правильно, а атомики - нет. 43 | 44 | Источники: 45 | - [Golangify](https://golangify.com/concurency) 46 | - [Metanit](https://metanit.com/go/tutorial/7.6.php) 47 | 48 | ## Что можно использовать для ожидания выполнения N горутин? 49 | 50 | Один из способов сделать это - использовать встроенную конструкцию WaitGroup из пакета sync. WaitGroup позволяет организовать синхронизацию между несколькими горутинами, которые выполняют параллельные или конкурентные задачи. WaitGroup имеет счетчик, который увеличивается при добавлении новой горутины и уменьшается при ее завершении. Основная горутина может вызвать метод Wait, который блокирует ее до тех пор, пока счетчик не станет равным нулю, то есть пока все горутины не закончат свою работу. 51 | 52 | Например, вы можете написать следующий код, который запускает N горутин, каждая из которых печатает свой номер и засыпает на случайное время, а затем ожидает их завершения с помощью WaitGroup: 53 | 54 | ```go title="main.go" 55 | package main 56 | 57 | import ( 58 | "fmt" 59 | "math/rand" 60 | "sync" 61 | "time" 62 | ) 63 | 64 | const N = 5 // количество горутин 65 | 66 | func main() { 67 | var wg sync.WaitGroup // создаем WaitGroup 68 | wg.Add(N) // устанавливаем счетчик на N 69 | 70 | for i := 1; i <= N; i++ { 71 | go func(n int) { // запускаем горутину с номером n 72 | // при выходе из горутины уменьшаем счетчик на 1 73 | defer wg.Done() 74 | 75 | fmt.Println("Горутина", n, "начала работу") 76 | // засыпаем на случайное время от 0 до 10 секунд 77 | time.Sleep(time.Duration(rand.Intn(10)) * time.Second) 78 | 79 | fmt.Println("Горутина", n, "закончила работу") 80 | }(i) 81 | } 82 | 83 | fmt.Println("Ожидаем завершения горутин") 84 | // блокируем основную горутину, пока счетчик не станет равным нулю 85 | wg.Wait() 86 | 87 | fmt.Println("Все горутины завершились") 88 | } 89 | ``` 90 | 91 | ## Есть общий ресурс. Хотим, чтобы к нему одновременно обращались только N горутин. Как это сделать? 92 | 93 | Один из способов сделать это - использовать семафор (semaphore), который представляет собой переменную, которая хранит количество доступных ресурсов. Семафор может быть реализован с помощью канала (channel) с буфером размера N, который будет заполнен пустыми значениями. 94 | 95 | Каждая горутина, которая хочет получить доступ к ресурсу, должна сначала получить значение из канала, а затем вернуть его обратно после завершения работы. Таким образом, канал будет выступать в роли блокировки, которая разрешает доступ только N горутинам одновременно. 96 | 97 | Например, вы можете написать следующий код, который запускает M горутин, каждая из которых печатает свой номер и засыпает на случайное время, а затем ожидает их завершения с помощью семафора: 98 | 99 | ```go title="main.go" 100 | package main 101 | 102 | import ( 103 | "fmt" 104 | "math/rand" 105 | "time" 106 | ) 107 | 108 | const N = 3 // количество доступных ресурсов 109 | const M = 10 // количество горутин 110 | 111 | func main() { 112 | sem := make(chan struct{}, N) // создаем канал с буфером размера N 113 | for i := 1; i <= N; i++ { 114 | sem <- struct{}{} // заполняем канал пустыми значениями 115 | } 116 | for i := 1; i <= M; i++ { 117 | go work(i, sem) // запускаем горутину с номером i и каналом sem 118 | } 119 | time.Sleep(20 * time.Second) // ждем 20 секунд, пока все горутины закончат работу 120 | fmt.Println("The End") 121 | } 122 | 123 | func work(number int, sem chan struct{}) { 124 | // получаем значение из канала, блокируя доступ к ресурсу 125 | <-sem 126 | 127 | fmt.Println("Горутина", number, "начала работу") 128 | // засыпаем на случайное время от 0 до 10 секунд 129 | time.Sleep(time.Duration(rand.Intn(10)) * time.Second) 130 | 131 | fmt.Println("Горутина", number, "закончила работу") 132 | 133 | // возвращаем значение в канал, разблокируя доступ к ресурсу 134 | sem <- struct{}{} 135 | } 136 | ``` 137 | 138 | ## Запустим 1000 горутин с инкрементом инта. Получим в конце тысячу? Что делать, чтобы получить тысячу? 139 | 140 | Если вы запустите 1000 горутин с инкрементом инта, то скорее всего вы не получите в конце тысячу. Это потому, что инкремент не является атомарной операцией, то есть он состоит из трех шагов: чтения, изменения и записи значения. Если несколько горутин одновременно пытаются выполнить инкремент, то может возникнуть состояние гонки (race condition), когда одна горутина перезаписывает значение, измененное другой горутиной, и тем самым теряет часть инкрементов. 141 | 142 | Чтобы получить тысячу в конце, нужно синхронизировать доступ к общей переменной, которая хранит инт. Для этого можно использовать один из следующих способов: 143 | 144 | - Использовать мьютекс (mutex) из пакета sync, который позволяет блокировать и разблокировать доступ к переменной. Каждая горутина должна вызвать метод Lock перед инкрементом и метод Unlock после него, чтобы гарантировать, что только одна горутина может работать с переменной в один момент времени. 145 | - Использовать атомик (atomic) из пакета sync/atomic, который позволяет выполнять атомарные операции с примитивными типами данных. Вместо обычного инкремента можно использовать функцию AddInt32 или AddInt64, которая атомарно увеличивает значение переменной на заданное число и возвращает новое значение. 146 | 147 | ## Есть глобальная мапа, глобальный мьютекс. Две функции. Одна блочит мьютекс, а вторая нет. Что произойдет? 148 | 149 | Если одна функция блокирует мьютекс перед работой с глобальной мапой, а другая нет, то может возникнуть ситуация, когда две функции одновременно пытаются изменить мапу, что может привести к состоянию гонки (race condition) и неопределенному поведению программы. 150 | 151 | Состояние гонки означает, что результат работы программы зависит от случайного порядка выполнения операций, и может быть непредсказуемым или некорректным. 152 | 153 | Например, если одна функция пытается добавить элемент в мапу, а другая пытается удалить элемент из мапы, то может случиться, что элемент будет удален до того, как он будет добавлен, или наоборот, или что элемент будет добавлен или удален дважды, или что мапа будет повреждена. 154 | 155 | ## Сколько нужно ядер, чтобы начать использовать sync.Map? 156 | sync.Map может быть полезен, если у вас высоконагруженная система с большим количеством ядер процессора (32+), и вы сталкиваетесь с проблемой ложной разделяемости (false sharing), когда разные горутины конкурируют за доступ к одному и тому же кеш-линии (cache line). В этом случае sync.Map может снизить количество конфликтов и повысить скорость работы с картой. 157 | 158 | Однако, если у вас небольшое количество ядер (меньше 8), и вы часто записываете в карту, то sync.Map может быть неэффективнее, чем обычная карта с мьютексом, так как он использует сложную внутреннюю структуру, которая требует дополнительных вычислений и памяти. 159 | 160 | Таким образом, нет однозначного ответа на вопрос, сколько нужно ядер, чтобы начать использовать sync.Map. Это зависит от конкретной задачи, характера операций с картой, нагрузки на систему и других факторов. 161 | 162 | 163 | > Ложная разделяемость - это проблема, которая может возникнуть в многопроцессорных системах, 164 | когда разные процессоры или ядра конкурируют за доступ к одному и тому же кеш-линии (cache line), то есть блоку памяти, 165 | который загружается в кеш процессора для ускорения работы с данными. 166 | Если один процессор изменяет данные в кеш-линии, то другие процессоры 167 | должны обновить свои копии этой кеш-линии, что приводит к дополнительным задержкам и снижению производительности. 168 | Ложная разделяемость может возникать, когда разные процессоры работают с разными данными, которые случайно попадают в одну кеш-линию, или когда разные процессоры работают с одними и теми же данными, но не синхронизируют свой доступ к ним. 169 | Ложная разделяемость может быть устранена с помощью различных методов, таких как: 170 | > - Изменение размера или выравнивания структур данных, чтобы избежать перекрытия кеш-линий. 171 | > - Использование атомарных (atomic) операций, которые не требуют блокировки кеш-линии. 172 | > - Использование специальных инструкций, которые позволяют указать процессору, что данные в кеш-линии не будут изменяться. 173 | > - Использование разных уровней кеша для разных типов данных. 174 | 175 | 176 | Источники: 177 | - [Habr](https://habr.com/ru/articles/338718/) 178 | - [Blogspot](https://habrparser.blogspot.com/2013/10/lock-free.html) 179 | 180 | ## Как устроена WaitGroup под капотом и как ее можно реализовать самому? 181 | 182 | Под капотом WaitGroup реализован с помощью атомарных операций, которые обеспечивают потокобезопасность и высокую производительность. WaitGroup использует 64-битное целое число, которое разделено на две части: старшие 32 бита хранят счетчик горутин, а младшие 32 бита хранят счетчик ожидающих горутин. 183 | 184 | Каждый раз, когда вызывается метод Add, WaitGroup атомарно увеличивает счетчик горутин на заданное значение. Каждый раз, когда вызывается метод Done, WaitGroup атомарно уменьшает счетчик горутин на единицу и проверяет, не стал ли он равным нулю. Если да, то WaitGroup атомарно увеличивает счетчик ожидающих горутин на единицу и разблокирует одну из ожидающих горутин с помощью сигнальной переменной (signal variable). 185 | 186 | Каждый раз, когда вызывается метод Wait, WaitGroup атомарно уменьшает счетчик ожидающих горутин на единицу и проверяет, не стал ли он отрицательным. Если да, то WaitGroup атомарно возвращает счетчик ожидающих горутин в исходное состояние и блокирует текущую горутину с помощью сигнальной переменной. 187 | 188 | ```go title="main.go" 189 | package main 190 | 191 | import ( 192 | "fmt" 193 | "sync" 194 | "sync/atomic" 195 | "time" 196 | ) 197 | 198 | // MyWaitGroup - собственная реализация WaitGroup 199 | type MyWaitGroup struct { 200 | counter int64 // счетчик горутин 201 | waiter int64 // счетчик ожидающих горутин 202 | signal sync.Cond // сигнальная переменная 203 | } 204 | 205 | // Add - добавляет n горутин в группу 206 | func (wg *MyWaitGroup) Add(n int) { 207 | atomic.AddInt64(&wg.counter, int64(n)) // атомарно увеличиваем счетчик горутин на n 208 | } 209 | 210 | // Done - уменьшает счетчик горутин на 1 и разблокирует одну ожидающую горутину, если счетчик стал равным 0 211 | func (wg *MyWaitGroup) Done() { 212 | if atomic.AddInt64(&wg.counter, -1) == 0 { // атомарно уменьшаем счетчик горутин на 1 и проверяем, не стал ли он равным 0 213 | wg.signal.L.Lock() // блокируем сигнальную переменную 214 | atomic.AddInt64(&wg.waiter, 1) // атомарно увеличиваем счетчик ожидающих горутин на 1 215 | wg.signal.Broadcast() // разблокируем все ожидающие горутины 216 | wg.signal.L.Unlock() // разблокируем сигнальную переменную 217 | } 218 | } 219 | 220 | // Wait - блокирует текущую горутину, пока счетчик горутин не станет равным 0 221 | func (wg *MyWaitGroup) Wait() { 222 | wg.signal.L.Lock() // блокируем сигнальную переменную 223 | if atomic.AddInt64(&wg.waiter, -1) < 0 { // атомарно уменьшаем счетчик ожидающих горутин на 1 и проверяем, не стал ли он отрицательным 224 | atomic.StoreInt64(&wg.waiter, 0) // атомарно возвращаем счетчик ожидающих горутин в исходное состояние 225 | wg.signal.Wait() // блокируем текущую горутину 226 | } 227 | wg.signal.L.Unlock() // разблокируем сигнальную переменную 228 | } 229 | 230 | func main() { 231 | var wg MyWaitGroup // создаем экземпляр MyWaitGroup 232 | wg.Add(2) // добавляем две горутины в группу 233 | work := func(id int) { 234 | defer wg.Done() // при выходе из горутины вызываем метод Done 235 | fmt.Printf("Горутина %d начала работу\n", id) 236 | time.Sleep(2 * time.Second) // имитируем работу горутины 237 | fmt.Printf("Горутина %d завершила работу\n", id) 238 | } 239 | // запускаем две горутины 240 | go work(1) 241 | go work(2) 242 | wg.Wait() // ожидаем завершения всех горутин в группе 243 | fmt.Println("Горутины завершились") 244 | } 245 | ``` 246 | 247 | Источники: 248 | - [Habr](https://habr.com/ru/companies/timeweb/articles/712542/) 249 | - [Metanit](https://metanit.com/go/tutorial/7.7.php) 250 | - [Medium](https://medium.com/nuances-of-programming/%D0%BF%D1%80%D0%B8%D0%BC%D0%B8%D1%82%D0%B8%D0%B2%D1%8B-%D1%81%D0%B8%D0%BD%D1%85%D1%80%D0%BE%D0%BD%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8-%D0%B2-go-403b163c49d5) 251 | 252 | ## В чем разница между Mutex и RWMutex? 253 | 254 | Mutex и RWMutex в Go оба используются для синхронизации доступа к данным в многопоточной среде, но они работают немного по-разному: 255 | 256 | Mutex: 257 | - Mutex предоставляет взаимоисключающую блокировку, которая позволяет только одной горутине в любой момент времени иметь доступ к защищенным данным. 258 | - Если другая горутина пытается получить доступ к данным, когда Mutex заблокирован, она будет заблокирована до тех пор, пока Mutex не будет разблокирован. 259 | 260 | RWMutex: 261 | - RWMutex (Reader-Writer Mutex) предоставляет более гибкую семантику блокировки. 262 | - RWMutex позволяет множеству горутин получить параллельный доступ для чтения (блокировка чтения), но только одной горутине получить эксклюзивный доступ на запись (блокировка записи). 263 | Это означает, что несколько горутин могут одновременно читать данные, но запись данных может производить только одна горутина. 264 | 265 | ## Когда нужно использовать Mutex, а когда RWMutex? 266 | 267 | Использование Mutex: 268 | - Mutex следует использовать, когда у вас есть данные, которые могут быть изменены одновременно несколькими горутинами. 269 | - Mutex обеспечивает взаимоисключающую блокировку, что позволяет только одной горутине в любой момент времени иметь доступ к защищенным данным. 270 | 271 | Использование RWMutex: 272 | - RWMutex следует использовать, когда у вас есть данные, которые часто читаются, но редко обновляются. 273 | - RWMutex позволяет множеству горутин получить параллельный доступ для чтения, но только одной горутине получить эксклюзивный доступ на запись. 274 | Это может улучшить производительность, если у вас есть данные, которые часто читаются, так как несколько горутин могут одновременно читать данные. 275 | 276 | Источники: 277 | - [Habr](https://qna.habr.com/q/787559) 278 | - [Stackoverflow](https://stackoverflow.com/questions/48861029/what-is-the-benefit-of-using-rwmutex-instead-of-mutex) 279 | -------------------------------------------------------------------------------- /content/4.infrastructure/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Вопросы по окружению", 3 | "position": 4, 4 | "link": { 5 | "type": "generated-index", 6 | "description": "Вопросы с собеседований по инфраструктуре" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /content/4.infrastructure/db-relation.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Базы данных (реляционные) 3 | sidebar_position: 2 4 | description: Вопросы по реляционным базам данных 5 | --- 6 | 7 | ## Что такое индексы, для чего используются? 8 | 9 | Индексы в реляционных базах данных используются для ускорения процесса поиска данных. Они работают по принципу индексов в книге: вместо того чтобы просматривать каждую страницу книги (или каждую строку в таблице данных), вы можете просто посмотреть в индекс, найти нужное значение и сразу перейти к нужной странице (или строке данных). 10 | 11 | Индексы могут быть особенно полезны при выполнении операций сравнения, таких как операторы `WHERE`, `ORDER BY` и `JOIN` в SQL. Однако стоит помнить, что индексы также занимают место в памяти и могут замедлить операции вставки, обновления и удаления, поскольку индекс также нужно обновлять при каждом изменении данных. 12 | 13 | ## Какие примеры индексов знаешь, на каких структурах данных они основаны? (PostgreSQL) 14 | 15 | PostgreSQL поддерживает несколько типов индексов: 16 | 17 | 1. B-Tree: Это наиболее часто используемый тип индекса в PostgreSQL. Он хорошо работает для операций равенства и диапазонов. 18 | 2. Hash: Этот тип индекса эффективен для операций равенства. Однако, он не поддерживает диапазонные запросы. 19 | 3. GiST (Generalized Search Tree): Этот тип индекса поддерживает балансировку дерева и может быть использован для различных типов данных. 20 | 4. GIN (Generalized Inverted Index): Этот тип индекса эффективен для данных, которые содержат несколько значений в одной колонке (например, массивы). 21 | 22 | B-Tree (сбалансированное дерево) - это структура данных, которая поддерживает логарифмическое время для операций поиска, вставки и удаления. В B-Tree каждый узел имеет множество ключей и ссылок на дочерние узлы. Ключи делят связи на диапазоны, так что B-Tree может быстро определить, в каком диапазоне находится искомый ключ, и перейти к соответствующему дочернему узлу. Этот процесс повторяется, пока не будет найден искомый ключ или не будет достигнут листовой узел. 23 | 24 | ## Какие есть уровни изоляций транзакций. Зачем они нужны? Как отличаются? 25 | 26 | Уровни изоляции транзакций определяют, как транзакции взаимодействуют друг с другом. Они нужны для обеспечения консистентности данных при одновременном выполнении нескольких транзакций. В SQL стандарте определены следующие уровни изоляции: 27 | 28 | 1. `READ UNCOMMITTED`: На этом уровне транзакции могут видеть незавершенные изменения (так называемые "грязные" данные) от других транзакций. Это самый низкий уровень изоляции, и он может привести к множеству проблем, таких как проблемы с чтением "грязных" данных. 29 | 2. `READ COMMITTED`: На этом уровне транзакция может видеть только те данные, которые были зафиксированы до начала транзакции. Это предотвращает чтение "грязных" данных, но все еще может привести к некоторым проблемам, таким как неповторяемое чтение. 30 | 3. `REPEATABLE READ`: На этом уровне транзакция видит только те данные, которые были зафиксированы до начала транзакции, и эти данные не изменяются в течение всей транзакции. Это предотвращает неповторяемое чтение, но может привести к фантомным чтениям. 31 | 4. `SERIALIZABLE`: Это самый высокий уровень изоляции. Он предотвращает все виды конфликтов чтения и записи, обеспечивая, что транзакции выполняются в строгом последовательном порядке, как если бы они выполнялись одна за другой. 32 | 33 | Уровни изоляции транзакций служат для балансировки между производительностью (более высокие уровни изоляции могут замедлить выполнение транзакций) и консистентностью данных (более низкие уровни изоляции могут привести к проблемам с данными). 34 | 35 | ## Зачем нужна денормализация БД? 36 | 37 | Денормализация базы данных - это процесс, при котором мы уменьшаем нормализацию (сокращаем количество таблиц и/или объединяем данные в одной таблице), чтобы улучшить производительность. 38 | 39 | Вот некоторые причины, по которым может потребоваться денормализация: 40 | 1. **Улучшение производительности запросов**: Денормализация может уменьшить количество соединений между таблицами при выполнении запросов, что может улучшить производительность. 41 | 2. **Упрощение запросов**: Денормализация может сделать запросы более простыми и понятными, поскольку данные могут быть организованы таким образом, чтобы лучше соответствовать запросам, которые вы делаете. 42 | 3. **Увеличение скорости чтения**: Денормализация может увеличить скорость чтения за счет уменьшения количества операций соединения. 43 | 44 | 45 | Однако стоит помнить, что денормализация может привести к увеличению объема хранимых данных и сложности обслуживания, поскольку изменения данных теперь нужно будет вносить в несколько мест. Поэтому решение о денормализации должно быть обоснованным и взвешенным. 46 | 47 | ## Что такое JOIN? 48 | 49 | `JOIN` - это операция в SQL, которая используется для объединения строк из двух или более таблиц на основе общего поля между ними. 50 | 51 | Существуют различные типы операций `JOIN`: 52 | 1. `INNER JOIN`: Возвращает строки, когда есть совпадение в обеих таблицах. 53 | 2. `LEFT JOIN` (или `LEFT OUTER JOIN`): Возвращает все строки из левой таблицы и совпадающие строки из правой таблицы. Если нет совпадения, результатом является NULL с правой стороны. 54 | 3. `RIGHT JOIN` (или `RIGHT OUTER JOIN`): Возвращает все строки из правой таблицы и совпадающие строки из левой таблицы. Если нет совпадения, результатом является NULL с левой стороны. 55 | 4. `FULL JOIN` (или `FULL OUTER JOIN`): Возвращает строки, когда есть совпадение в одной из таблиц. То есть, если в левой таблице есть совпадение, оно возвращается, и если в правой таблице есть совпадение, оно также возвращается. 56 | 57 | Операция `JOIN` позволяет извлекать данные из нескольких таблиц как один набор данных, что может быть очень полезно при работе с нормализованными базами данных, где данные распределены по нескольким таблицам. 58 | 59 | ## Что можем сделать если запрос жирный и много JOINов? 60 | 61 | Если запрос содержит много операций `JOIN` и становится "жирным" (т.е. медленным или ресурсоемким), есть несколько способов оптимизации: 62 | 1. **Индексация**: Убедитесь, что поля, используемые в операциях `JOIN`, индексированы. Индексы могут значительно ускорить операции `JOIN`. 63 | 2. **Уменьшение количества JOIN'ов**: Попробуйте уменьшить количество операций `JOIN`, если это возможно. Это может включать в себя денормализацию данных или изменение структуры запроса. 64 | 3. **Использование подзапросов**: В некоторых случаях подзапросы могут быть более эффективными, чем сложные операции `JOIN`. 65 | 4. **Оптимизация запроса**: Пересмотрите свой запрос и убедитесь, что он написан максимально эффективно. Это может включать в себя использование `EXPLAIN PLAN` (или аналогичного инструмента в вашей СУБД) для понимания, как СУБД выполняет ваш запрос, и определения возможных узких мест. 66 | 5. **Использование представлений**: Если один и тот же сложный запрос выполняется многократно, может быть полезно создать представление (view), которое хранит результат этого запроса. 67 | 6. **Вертикальное разделение**: Если таблицы, используемые в `JOIN`, очень большие, может быть полезно применить вертикальное разделение, т.е. разделить таблицы на более мелкие по набору колонок. 68 | 69 | Помните, что оптимальный подход зависит от конкретной ситуации и может потребовать тестирования различных стратегий. 70 | 71 | ## Расскажи подробнее про триггеры (ППО). Какие есть плюсы-минусы? 72 | 73 | Триггеры в базах данных - это специальные процедуры, которые автоматически запускаются (или "срабатывают") при определенных событиях, таких как вставка, обновление или удаление записей в таблице. 74 | 75 | **Плюсы триггеров:** 76 | 77 | 1. **Автоматизация**: Триггеры позволяют автоматизировать рутинные операции, которые должны происходить при определенных изменениях данных. 78 | 2. **Целостность данных**: Триггеры могут быть использованы для поддержания целостности данных, автоматически обновляя данные в одной таблице при изменении данных в другой. 79 | 3. **Аудит**: Триггеры могут быть использованы для отслеживания изменений в данных, автоматически записывая информацию об изменениях в журнал аудита. 80 | 81 | **Минусы триггеров:** 82 | 83 | 1. **Производительность**: Триггеры могут замедлить операции с базой данных, поскольку каждое изменение данных может вызывать выполнение дополнительного кода. 84 | 2. **Сложность отладки**: Триггеры могут сделать систему сложнее для понимания и отладки, поскольку они работают "за кулисами" и могут вызывать неожиданные изменения данных. 85 | 3. **Порядок выполнения**: Если есть несколько триггеров, которые срабатывают при одном и том же событии, порядок их выполнения может быть неочевидным и привести к неожиданным результатам. 86 | 87 | Важно помнить, что триггеры - это мощный инструмент, но они должны использоваться с осторожностью. Они лучше всего подходят для обработки сложных требований к целостности данных, которые не могут быть легко реализованы с помощью стандартных ограничений SQL. 88 | 89 | 90 | 91 | ## Почему плохо использовать много индексов? 92 | 93 | Использование индексов в базе данных ускоряет операции чтения, но есть и обратная сторона медали. Вот несколько причин, по которым использование большого количества индексов может быть нежелательным: 94 | 95 | 1. **Замедление операций записи**: Каждый раз, когда вы вставляете, обновляете или удаляете данные, все индексы, связанные с этими данными, также должны быть обновлены. Это может значительно замедлить операции записи. 96 | 2. **Увеличение использования дискового пространства**: Индексы занимают дисковое пространство. Чем больше индексов у вас есть, тем больше дискового пространства они занимают. 97 | 3. **Увеличение сложности обслуживания**: Индексы требуют обслуживания (например, перестройки или переиндексации), что может увеличить сложность обслуживания базы данных. 98 | 4. **Неправильное использование индексов**: СУБД может не всегда эффективно использовать индексы, особенно если есть много индексов на выбор. Это может привести к неоптимальным планам выполнения запросов. 99 | 100 | Важно помнить, что индексы - это инструмент, который должен использоваться обдуманно. Необходимо регулярно анализировать и оптимизировать индексы для обеспечения наилучшей производительности. 101 | 102 | ## Слышал про нормализацию? Расскажи в двух словах. 103 | 104 | Нормализация в контексте баз данных - это процесс организации данных в таблицах для минимизации дублирования и обеспечения целостности данных. Это достигается путем разделения данных на несколько связанных таблиц и установления отношений между ними. Нормализация помогает улучшить производительность, упрощает обслуживание и обновление данных, и предотвращает возникновение аномалий данных. 105 | 106 | ## Приходилось сталкиваться с оконными функциями? Расскажи про них 107 | 108 | Оконные функции в SQL позволяют выполнять вычисления относительно набора строк, связанных с текущей строкой. Они называются "оконными", потому что они работают с "окном" строк в результате запроса. 109 | 110 | Оконные функции могут быть очень полезны для выполнения сложных аналитических и статистических вычислений, таких как ранжирование, вычисление скользящих средних, суммирование и т.д. 111 | 112 | Пример оконной функции в SQL: 113 | 114 | ```sql 115 | SELECT name, salary, AVG(salary) OVER (PARTITION BY department) as avg_salary 116 | FROM employees; 117 | ``` 118 | 119 | В этом примере `AVG(salary) OVER (PARTITION BY department)` - это оконная функция. Она вычисляет среднюю зарплату для каждого отдела (т.е. "окна" данных, определенного `PARTITION BY department`). 120 | 121 | Еще пример: 122 | 123 | ```sql 124 | SELECT name, salary, RANK() OVER (ORDER BY salary DESC) as salary_rank 125 | FROM employees; 126 | ``` 127 | 128 | Оконные функции не изменяют набор результатов запроса. Вместо этого они добавляют новые столбцы, которые могут содержать информацию, вычисленную на основе окна строк. 129 | 130 | ## Что такое ACID? 131 | 132 | ACID - это акроним, который описывает четыре основных свойства, которые гарантируют надежные обработку транзакций и обновления в базе данных: 133 | 134 | 1. **Atomicity (Атомарность)**: Это свойство гарантирует, что транзакция рассматривается как единое целое. Это означает, что либо все операции транзакции выполняются успешно, либо, если какая-либо операция в транзакции не удается, то все операции откатываются, и состояние базы данных остается неизменным. 135 | 2. **Consistency (Согласованность)**: Это свойство гарантирует, что транзакция приводит базу данных из одного согласованного состояния в другое. Правила целостности данных должны быть соблюдены. 136 | 3. **Isolation (Изолированность)**: Это свойство гарантирует, что параллельное выполнение транзакций оставляет базу данных в таком же состоянии, как если бы транзакции выполнялись последовательно. 137 | 4. **Durability (Долговечность)**: Это свойство гарантирует, что после успешного завершения транзакции любые изменения, внесенные в базу данных, сохраняются и остаются постоянными, даже в случае сбоя системы. 138 | 139 | Эти свойства являются важными для обеспечения надежности и корректности работы системы баз данных. 140 | 141 | ## Как реализована буква D из ACID в PostgreSQL? 142 | 143 | Буква "D" в ACID означает Durability (Долговечность). В контексте баз данных это свойство гарантирует, что после успешного завершения транзакции любые изменения, внесенные в базу данных, сохраняются и остаются постоянными, даже в случае сбоя системы. 144 | 145 | В PostgreSQL долговечность обеспечивается с помощью механизма Write-Ahead Logging (WAL). Вот как это работает: 146 | 1. Когда транзакция выполняется, все изменения данных сначала записываются в журнал предварительной записи (WAL). Это включает в себя как данные, так и информацию о транзакции. 147 | 2. Эти записи WAL затем физически записываются на диск. Это гарантирует, что даже если система внезапно упадет, информация о транзакции и изменениях данных не будет потеряна. 148 | 3. Только после того, как запись WAL была успешно записана на диск, транзакция считается завершенной, и изменения данных могут быть применены к основной базе данных. 149 | 4. Если система восстанавливается после сбоя, PostgreSQL может использовать журнал WAL, чтобы узнать, какие транзакции были завершены и какие изменения данных должны быть применены. 150 | 151 | Таким образом, PostgreSQL обеспечивает долговечность данных, гарантируя, что завершенные транзакции будут сохранены, даже если произойдет сбой системы. 152 | 153 | ## Что такое репликация? Что происходит при репликации, что передается на реплики? 154 | 155 | Репликация в контексте баз данных - это процесс копирования данных из одной базы данных (обычно называемой "мастер" или "первичной") в одну или несколько других баз данных (обычно называемых "репликами" или "вторичными"). 156 | 157 | Цель репликации - увеличить доступность и надежность данных. Если основная база данных становится недоступной или теряет данные, вы можете переключиться на одну из реплик без потери данных. Репликация также может улучшить производительность, позволяя распределить нагрузку чтения между несколькими серверами. 158 | 159 | Во время репликации обычно передаются следующие данные: 160 | 161 | 1. **Транзакции**: Все изменения данных, которые произошли в основной базе данных, передаются на реплики. Это включает в себя операции вставки, обновления и удаления. 162 | 2. **Состояние базы данных**: В некоторых системах репликации, таких как снимки или полная репликация, также может передаваться текущее состояние базы данных. 163 | 3. **Метаданные**: Информация о структуре базы данных и ее объектах (таблицы, индексы, ограничения и т.д.) также может передаваться в процессе репликации. 164 | 165 | Существуют различные стратегии репликации (например, асинхронная vs синхронная, однонаправленная vs двунаправленная), и выбор подходящей стратегии зависит от конкретных требований к доступности, надежности и производительности. 166 | 167 | ## Что такое TOASTING? 168 | 169 | TOAST (The Oversized-Attribute Storage Technique) в PostgreSQL - это механизм, который позволяет хранить большие значения данных, которые не умещаются в обычной странице базы данных (обычно размером 8 КБ). 170 | 171 | Когда строка данных превышает определенный размер (обычно около 2 КБ), PostgreSQL автоматически использует TOAST для сжатия и/или разделения данных на несколько кусков, которые затем хранятся в отдельной TOAST-таблице. Каждая основная таблица в PostgreSQL имеет связанную TOAST-таблицу для хранения таких больших значений. 172 | 173 | Когда вы запрашиваете данные, PostgreSQL автоматически собирает и распаковывает данные из TOAST-таблицы, делая этот процесс прозрачным для пользователя. 174 | 175 | TOAST позволяет PostgreSQL эффективно работать с большими значениями данных, минимизируя использование дискового пространства и улучшая производительность. 176 | 177 | 178 | ## Что такое MVCC? 179 | 180 | MVCC (Multi-Version Concurrency Control) - это метод управления параллельным доступом к базе данных, который позволяет одновременно выполнять транзакции чтения и записи без блокировки данных. 181 | 182 | В MVCC каждая транзакция видит базу данных в определенном состоянии, которое соответствует моменту времени, когда транзакция начала выполнение. Это достигается путем хранения нескольких версий каждой строки данных, каждая из которых соответствует состоянию строки в определенный момент времени. 183 | 184 | Когда транзакция выполняет операцию чтения, она видит последнюю версию строки, которая была актуальной на момент начала транзакции. Это позволяет избежать блокировок чтения и записи, поскольку транзакции могут одновременно читать данные, даже если другие транзакции изменяют эти данные. 185 | 186 | MVCC широко используется в современных СУБД, таких как PostgreSQL, Oracle и других, и является важным механизмом для обеспечения высокой производительности и надежности при работе с базами данных. 187 | 188 | ## Что такое хранимая процедура? Зачем они нужны? 189 | 190 | Хранимая процедура (или хранимая функция) - это набор инструкций SQL, который сохраняется в базе данных и может быть вызван из других программ или запросов. 191 | Хранимые процедуры могут быть написаны на различных языках программирования, таких как SQL, PL/pgSQL, PL/Python, PL/Perl и других, в зависимости от СУБД. 192 | Хранимые процедуры полезны по нескольким причинам: 193 | 194 | 1. **Уменьшение сетевого трафика**: Поскольку хранимые процедуры выполняются на сервере базы данных, они могут уменьшить количество данных, которые должны быть переданы между сервером базы данных и клиентским приложением. 195 | 2. **Улучшение производительности**: Хранимые процедуры могут быть оптимизированы для выполнения на сервере базы данных, что может улучшить производительность. 196 | 3. **Сокрытие сложности**: Хранимые процедуры могут использоваться для сокрытия сложности запросов и бизнес-логики от клиентских приложений. 197 | 4. **Повторное использование кода**: Хранимые процедуры могут быть повторно использованы в различных частях приложения или различных приложениях. 198 | 5. **Улучшение безопасности**: Хранимые процедуры могут использоваться для ограничения доступа к данным и обеспечения безопасности. 199 | 200 | Хранимые процедуры - это мощный инструмент, который может быть использован для улучшения производительности, безопасности и обслуживания базы данных. 201 | 202 | ## Сталкивался с дэдлоками? Что это такое и когда происходит? Допустим у тебя update батча, то есть в транзакции апдейтишь 1000 строк и у тебя множество параллельных транзакций и может быть пересечение данных, как не словить дэдлок? 203 | 204 | Дэдлок в базах данных - это ситуация, когда две или более транзакций взаимно блокируют друг друга, каждая ожидая, пока другая освободит ресурс. Это может произойти, когда транзакции конкурируют за доступ к одним и тем же строкам данных. 205 | 206 | Вот несколько способов избежать дедлоков: 207 | 208 | 1. **Упорядочивание доступа к ресурсам**: Если все транзакции всегда запрашивают доступ к ресурсам в одном и том же порядке, дедлоки не могут произойти. 209 | 2. **Избегание долгих транзакций**: Чем дольше транзакция держит блокировку, тем больше вероятность дедлока. По возможности старайтесь делать транзакции как можно короче. 210 | 3. **Использование тайм-аутов**: Можно установить тайм-аут на блокировку ресурса. Если транзакция не может получить блокировку в течение определенного времени, она откатывается, освобождая свои ресурсы и предотвращая дедлок. 211 | 4. **Использование механизмов обнаружения дедлоков**: Большинство СУБД, включая PostgreSQL, имеют встроенные механизмы обнаружения дедлоков. Когда обнаруживается дедлок, одна из транзакций автоматически откатывается, освобождая свои блокировки и разрешая другим транзакциям продолжить работу. 212 | 213 | В случае с обновлением батча, если есть вероятность пересечения данных между транзакциями, одним из подходов может быть упорядочивание обновлений по какому-то критерию (например, по ID строки), чтобы уменьшить вероятность дедлока. 214 | 215 | ## Каким образом выявили узкое место в SQL? (ППО) 216 | 217 | В SQL есть команда `EXPLAIN PLAN`, которая показывает, как СУБД планирует выполнить запрос. Это может помочь выявить, какие части запроса занимают больше всего времени, и выявить возможные проблемы, такие как отсутствие индексов или неэффективные операции соединения. 218 | 219 | Помните, что оптимизация SQL - это итеративный процесс, который может потребовать многократного тестирования и настройки. 220 | 221 | ## У нас есть запрос с JOIN, но не по id и не по PRIMARY KEY. Есть индекс, но в запросе он не используется. Почему? 222 | 223 | Система управления базами данных (СУБД) определяет, какие индексы использовать при выполнении запроса, на основе статистической информации о данных. Если СУБД решает, что использование индекса не будет эффективным, она может выбрать другой план выполнения запроса. 224 | 225 | Вот несколько возможных причин, почему индекс может не использоваться в запросе с JOIN: 226 | 227 | 1. **Низкая выборочность**: Если индексированное поле содержит много дубликатов, индекс может быть неэффективным для ускорения запроса. Это обычно происходит, когда поле имеет низкую "выборочность" (т.е., большое количество строк имеют одно и то же значение). 228 | 2. **Неправильный тип соединения**: Некоторые типы соединений (например, OUTER JOIN) могут препятствовать использованию индексов. 229 | 3. **Неправильный порядок столбцов в индексе**: Если индекс состоит из нескольких столбцов, порядок этих столбцов в индексе имеет значение. Если столбец, используемый в JOIN, не является первым в индексе, индекс может не использоваться. 230 | 4. **Неправильное использование функций или операторов**: Если в условии JOIN используются функции или операторы, которые не могут использовать индекс, индекс может не использоваться. 231 | 232 | Для определения, почему индекс не используется, вы можете использовать команду `EXPLAIN PLAN` (или аналогичный инструмент в вашей СУБД), чтобы увидеть, как СУБД планирует выполнить запрос. 233 | 234 | ## В качестве идентификатора что лучше bigint или uuid? Когда uuid может нам помочь? 235 | 236 | Выбор между `bigint` и `uuid` для идентификатора зависит от конкретных требований вашего приложения. 237 | 238 | **`bigint`** обычно используется, когда вам нужен простой, упорядоченный идентификатор. Он занимает меньше места (8 байтов) и обычно быстрее для операций сравнения и сортировки. Однако, если вам нужно объединять данные из нескольких баз данных или таблиц, могут возникнуть проблемы с конфликтами идентификаторов. 239 | 240 | **`uuid`** представляет собой универсальный уникальный идентификатор, который генерируется таким образом, что вероятность его дублирования крайне мала. Это делает `uuid` идеальным для ситуаций, когда вам нужно гарантировать уникальность идентификатора во всем мире, например, при репликации данных между несколькими базами данных или при создании распределенных систем. Однако `uuid` занимает больше места (16 байтов) и может быть медленнее для операций сравнения и сортировки. 241 | 242 | В общем, если вам нужна глобальная уникальность идентификатора, `uuid` может быть хорошим выбором. Если же вам нужен простой, эффективный идентификатор и вы уверены, что конфликты идентификаторов не будут проблемой, `bigint` может быть более подходящим. 243 | 244 | ## Что такое партиционирование(Секционирование)? Когда его стоит применять? 245 | 246 | Партиционирование в базах данных — это процесс разделения больших таблиц на более мелкие фрагменты, называемые разделами или партициями, с целью повышения производительности, улучшения управляемости и обеспечения более эффективного использования ресурсов хранения. Партиционирование может применяться как для горизонтального, так и для вертикального разделения данных. Горизонтальное партиционирование относится к разделению данных на основе строк или диапазонов значений ключа, тогда как вертикальное партиционирование относится к разделению данных на основе столбцов. 247 | 248 | ### Основные принципы партиционирования: 249 | 1. **Разделение данных**: Таблица разделяется на отдельные сегменты данных, которые могут быть обрабатываемыми независимо друг от друга. Каждый сегмент содержит свой поднабор строк из исходной таблицы. 250 | 2. **Ключ разделения**: Для определения, в какую партицию должна попасть каждая строка, используется ключ разделения. Это может быть определенный столбец или набор столбцов, по которым данные распределяются между различными партициями. 251 | 3. **Прозрачный доступ**: Хотя данные разделены на различные фрагменты, для пользователя или приложения это должно быть незаметно. Пользователь должен иметь возможность выполнять запросы к таблице, как если бы она была одним целым. 252 | 4. **Управление и обслуживание**: Партиционирование упрощает управление большими объемами данных, поскольку позволяет выполнить административные задачи, такие как добавление и удаление данных, в отдельных частях таблицы, а не на всей таблице целиком. 253 | 5. **Улучшение производительности**: Партиционирование может повысить производительность запросов, поскольку запросы могут быть направлены только к определенным разделам, что уменьшает объем данных, обрабатываемых при выполнении запроса. 254 | 255 | ### Партиционирование стоит применить в следующих случаях: 256 | 1. **Работа с большими объемами данных**: Когда объем данных в таблице становится очень большим, партиционирование может помочь управлять этими данными, улучшая производительность запросов и упрощая администрирование. 257 | 2. **Регулярное удаление старых данных**: Если ваше приложение регулярно удаляет старые данные, партиционирование позволяет удалить эти данные из отдельных партиций, что может быть более эффективным, чем удаление данных из целой таблицы. 258 | 3. **Повышение производительности запросов**: Партиционирование может улучшить производительность запросов, направляя запросы только к определенным партициям, уменьшая объем данных, обрабатываемых при выполнении запроса. 259 | 4. **Управление обновлением и вставками данных**: Если ваше приложение часто выполняет операции вставки и обновления данных, партиционирование может помочь улучшить производительность этих операций, разделяя данные на более мелкие фрагменты. 260 | 5. **Горизонтальное масштабирование**: Партиционирование может быть использовано в качестве стратегии горизонтального масштабирования, позволяя распределить данные по разным узлам или серверам для балансировки нагрузки и увеличения производительности. 261 | 262 | -------------------------------------------------------------------------------- /content/4.infrastructure/docker.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Docker 3 | sidebar_position: 3 4 | --- 5 | 6 | ## Чем контейнеризация отличается от виртуализации? 7 | 8 | Контейнеризация - это технология, позволяющая упаковывать приложения и их зависимости в изолированные контейнеры. Контейнеры работают на уровне операционной системы и используют ее ядро. Виртуализация же позволяет запускать несколько виртуальных машин на одном физическом сервере. Виртуальные машины имеют собственное ядро и работают на уровне гипервизора. 9 | 10 | ## Какие преимущества контейнеризации? 11 | 12 | - **Изоляция**: контейнеры изолированы друг от друга и от хост-системы. 13 | - **Легковесность**: контейнеры используют общее ядро операционной системы, что делает их легче и быстрее виртуальных машин. 14 | - **Портабельность**: контейнеры могут быть запущены на любой системе, поддерживающей контейнеризацию. 15 | - **Масштабируемость**: контейнеры могут быть легко масштабированы горизонтально и вертикально. 16 | - **Гибкость**: контейнеры могут быть легко созданы, удалены и обновлены. 17 | 18 | ## Какие инструменты для контейнеризации вы знаете? 19 | 20 | - **Docker**: самый популярный инструмент для контейнеризации приложений. 21 | - **Podman**: альтернатива Docker, которая не требует демона. 22 | - **LXC/LXD**: система контейнеров на уровне операционной системы. 23 | - **rkt**: инструмент от CoreOS для запуска контейнеров. 24 | - **containerd**: компонент Docker, отвечающий за запуск контейнеров. 25 | 26 | ## Как создать Docker-контейнер? 27 | 28 | 1. Создайте Dockerfile, описывающий, как собрать контейнер. 29 | 2. Соберите контейнер с помощью команды `docker build`. 30 | 3. Запустите контейнер с помощью команды `docker run`. 31 | 32 | Пример Dockerfile: 33 | 34 | ```Dockerfile 35 | # Используем базовый образ 36 | FROM ubuntu:latest 37 | 38 | # Устанавливаем необходимые пакеты 39 | RUN apt-get update && apt-get install -y nginx 40 | 41 | # Копируем файлы в контейнер 42 | COPY index.html /var/www/html/ 43 | 44 | # Открываем порт 45 | EXPOSE 80 46 | 47 | # Запускаем приложение 48 | CMD ["nginx", "-g", "daemon off;"] 49 | ``` 50 | 51 | ## Какие команды Docker вы знаете? 52 | 53 | - `docker build`: собрать контейнер из Dockerfile. 54 | - `docker run`: запустить контейнер. 55 | - `docker ps`: показать запущенные контейнеры. 56 | - `docker images`: показать доступные образы. 57 | - `docker exec`: выполнить команду внутри контейнера. 58 | - `docker stop`: остановить контейнер. 59 | - `docker rm`: удалить контейнер. 60 | 61 | ## Какие типы сетей Docker вы знаете? 62 | 63 | - **bridge**: сеть по умолчанию, используемая для связи контейнеров на одном хосте. 64 | - **host**: контейнер использует сеть хоста. 65 | - **overlay**: сеть, используемая для связи контейнеров на разных хостах. 66 | - **macvlan**: сеть, позволяющая контейнерам иметь собственный MAC-адрес. 67 | 68 | ## Какие технологии используются в Docker? 69 | 70 | - **Namespaces**: изолируют процессы, сеть, файловую систему и другие ресурсы. 71 | - **Control groups (cgroups)**: ограничивают ресурсы, такие как CPU, память и дисковое пространство. 72 | - **Union file systems**: позволяют объединять несколько файловых систем в одну. 73 | - **Container format**: определяет структуру и формат контейнера. 74 | 75 | ## Какие проблемы могут возникнуть при использовании Docker? 76 | 77 | - **Безопасность**: уязвимости в контейнерах и образах. 78 | - **Масштабирование**: сложности с управлением большим количеством контейнеров. 79 | - **Сеть**: проблемы с настройкой сети и связью между контейнерами. 80 | - **Хранение данных**: необходимость сохранения данных между запусками контейнеров. 81 | - **Мониторинг и логирование**: сложности с мониторингом и логированием контейнеров. 82 | 83 | ## Какие best practices для Docker вы знаете? 84 | 85 | - **Используйте официальные образы**: проверенные и безопасные образы. 86 | - **Минимизируйте размер образа**: удаляйте ненужные файлы и зависимости. 87 | - **Используйте .dockerignore**: исключайте ненужные файлы из контекста сборки. 88 | - **Ограничьте привилегии**: используйте `USER` и `RUN` с `--no-cache`. 89 | - **Используйте Docker Compose**: для управления множеством контейнеров. 90 | - **Мониторьте ресурсы**: используйте инструменты мониторинга и логирования. 91 | 92 | ## Какие инструменты для управления Docker вы знаете? 93 | 94 | - **Docker Compose**: для управления множеством контейнеров. 95 | - **Docker Swarm**: для управления кластером Docker-хостов. 96 | - **Kubernetes**: для управления контейнерами и микросервисами. 97 | - **Portainer**: веб-интерфейс для управления Docker. 98 | - **Rancher**: платформа для управления контейнерами и Kubernetes. -------------------------------------------------------------------------------- /content/4.infrastructure/git.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Git 3 | sidebar_position: 4 4 | --- 5 | 6 | ## Чем Rebase отличается от Merge? 7 | 8 | Rebase и Merge - это два способа объединения изменений из одной ветки в другую. 9 | 10 | ### Rebase 11 | 12 | Rebase переносит коммиты из одной ветки в другую. При этом история коммитов становится линейной, так как коммиты из ветки, в которую происходит ребейз, добавляются к коммитам ветки, из которой происходит ребейз. Это позволяет избежать конфликтов слияния, так как изменения применяются последовательно. 13 | 14 | ### Merge 15 | 16 | Merge создает новый коммит, который объединяет изменения из двух веток. При этом история коммитов остается нелинейной, так как в истории появляется новый коммит слияния. Это может привести к конфликтам слияния, так как изменения объединяются в один коммит. 17 | 18 | ### Когда использовать Rebase? 19 | 20 | - Когда нужно обновить ветку относительно другой ветки. 21 | - Когда нужно избежать конфликтов слияния. 22 | - Когда нужно поддерживать линейную историю коммитов. 23 | 24 | ### Когда использовать Merge? 25 | 26 | - Когда нужно объединить изменения из двух веток. 27 | - Когда не важно сохранение линейной истории коммитов. 28 | - Когда необходимо сохранить историю коммитов ветки. 29 | 30 | ## Как создать новую ветку в Git? 31 | 32 | Чтобы создать новую ветку в Git, выполните команду `git checkout -b `. Эта команда создаст новую ветку с именем `` и переключит вас на нее. 33 | 34 | Пример: 35 | 36 | ```bash 37 | git checkout -b feature-branch 38 | ``` 39 | 40 | ## Как переключиться на другую ветку в Git? 41 | 42 | Чтобы переключиться на другую ветку в Git, выполните команду `git checkout `. Эта команда переключит вас на ветку с именем ``. 43 | 44 | Пример: 45 | 46 | ```bash 47 | git checkout main 48 | ``` 49 | 50 | ## Как объединить изменения из одной ветки в другую? 51 | 52 | Чтобы объединить изменения из одной ветки в другую в Git, вы можете использовать команду `git merge `. Эта команда создаст новый коммит, который объединит изменения из ветки `` в текущую ветку. 53 | 54 | Пример: 55 | 56 | ```bash 57 | git merge feature-branch 58 | ``` 59 | 60 | ## Что такое cherry-pick в Git? 61 | 62 | Cherry-pick - это команда Git, которая позволяет применить изменения из одного коммита в другую ветку. Это полезно, когда нужно применить только определенные изменения из одной ветки в другую. 63 | 64 | Пример: 65 | 66 | ```bash 67 | git cherry-pick 68 | ``` 69 | 70 | ## Как отменить последний коммит в Git? 71 | 72 | Чтобы отменить последний коммит в Git, выполните команду `git reset HEAD~1`. Эта команда отменит последний коммит и оставит изменения в рабочем каталоге. 73 | 74 | Примечание: Эта команда не удаляет изменения, сделанные в последнем коммите, а только отменяет коммит. 75 | 76 | ## Как отменить изменения в файле в Git? 77 | 78 | Чтобы отменить изменения в файле в Git, выполните команду `git checkout -- `. Эта команда отменит изменения в файле и вернет его к состоянию последнего коммита. 79 | 80 | Пример: 81 | 82 | ```bash 83 | git checkout -- index.html 84 | ``` 85 | 86 | ## Что такое git flow? 87 | 88 | Git Flow — это модель ветвления и процесс управления версиями в системе контроля версий Git, разработанные Винсентом Дриссеном. Эта модель предназначена для упрощения работы с параллельными ветками, облегчения интеграции изменений и повышения надежности выпускаемых релизов. Git Flow предоставляет набор правил и практик для организации работы над проектами. 89 | 90 | Основные концепции Git Flow: 91 | 92 | 1. **Ветвление**: 93 | 94 | - **Main (или Master)**: основная ветка, содержащая стабильные и готовые к выпуску версии продукта. 95 | - **Develop**: основная ветка разработки, в которую интегрируются все изменения перед их выпуском в main. 96 | - **Feature branches**: ветки для разработки новых функциональностей. Они создаются от develop и после завершения работы мержатся обратно в develop. 97 | - **Release branches**: ветки для подготовки релизов. Они создаются от develop, когда проект готов к выпуску новой версии. В этой ветке можно производить окончательное тестирование и исправление багов. После завершения работы эта ветка мержится в main и develop. 98 | - **Hotfix branches**: ветки для экстренного исправления багов в стабильных версиях. Они создаются от main и после исправления мержатся обратно в main и develop. 99 | 100 | 2. **Процесс**: 101 | - **Начало работы**: создается feature-ветка от develop. 102 | - **Разработка**: ведется разработка в feature-ветке. 103 | - **Интеграция**: после завершения разработки feature-ветка мержится обратно в develop. 104 | - **Подготовка к релизу**: создается release-ветка от develop, в которой проводится окончательное тестирование и полировка. 105 | - **Выпуск релиза**: release-ветка мержится в main и develop. В main добавляется тег с номером версии. 106 | - **Экстренные исправления**: создается hotfix-ветка от main, вносится исправление, и ветка мержится обратно в main и develop. 107 | 108 | Git Flow помогает структурировать процесс разработки, делает его более прозрачным и управляемым, особенно при работе в команде. Для упрощения работы с этой моделью существуют инструменты, такие как расширение для Git под названием `git-flow`, которое автоматизирует многие операции. 109 | -------------------------------------------------------------------------------- /content/4.infrastructure/kubernetes.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Kubernetes (k8s) 3 | sidebar_position: 5 4 | --- 5 | 6 | ## Что такое Kubernetes 7 | 8 | Kubernetes (k8s) - это платформа для автоматизации развертывания, масштабирования и управления контейнеризированными приложениями. Kubernetes позволяет управлять контейнерами на нескольких хостах, обеспечивая автоматическое масштабирование, балансировку нагрузки, самовосстановление и многое другое. 9 | 10 | ## Какие компоненты входят в Kubernetes 11 | 12 | Основные компоненты Kubernetes: 13 | - **Master Node**: управляющий узел, который управляет кластером Kubernetes. 14 | - **Worker Node**: рабочий узел, на котором запускаются контейнеры. 15 | - **Pod**: минимальная единица развертывания в Kubernetes, содержащая один или несколько контейнеров. 16 | - **ReplicaSet**: контроллер, который обеспечивает желаемое количество запущенных копий Pod. 17 | - **Deployment**: контроллер, который управляет ReplicaSet и обеспечивает обновление и масштабирование приложений. 18 | - **Service**: абстракция, которая определяет доступ к набору Pod. 19 | - **Volume**: механизм для постоянного хранения данных в Kubernetes. 20 | - **Namespace**: механизм для организации и изоляции ресурсов в Kubernetes. 21 | 22 | ## Какие преимущества Kubernetes 23 | 24 | - **Масштабируемость**: Kubernetes позволяет легко масштабировать приложения горизонтально и вертикально. 25 | - **Отказоустойчивость**: Kubernetes обеспечивает автоматическое восстановление после сбоев. 26 | - **Самообслуживание**: Kubernetes позволяет разработчикам самостоятельно развертывать и управлять приложениями. 27 | - **Гибкость**: Kubernetes поддерживает различные типы приложений и инструменты. 28 | - **Открытость**: Kubernetes является open-source проектом и имеет активное сообщество разработчиков. 29 | 30 | ## Какие инструменты для управления Kubernetes 31 | 32 | - **kubectl**: официальный инструмент командной строки для управления Kubernetes. 33 | - **Helm**: пакетный менеджер для Kubernetes, который упрощает установку и управление приложениями. 34 | - **kubeadm**: инструмент для развертывания кластера Kubernetes. 35 | - **kops**: инструмент для развертывания кластера Kubernetes в AWS. 36 | - **kubespray**: инструмент для развертывания кластера Kubernetes с использованием Ansible. 37 | - **kubectx**: инструмент для управления контекстами Kubernetes. 38 | - **kubens**: инструмент для управления пространствами имен Kubernetes. 39 | 40 | ## Как создать Pod в Kubernetes 41 | 42 | 1. Создайте файл `pod.yaml`, описывающий Pod. 43 | 2. Примените файл с помощью команды `kubectl apply -f pod.yaml`. 44 | 45 | Пример `pod.yaml`: 46 | 47 | ```yaml 48 | apiVersion: v1 49 | kind: Pod 50 | metadata: 51 | name: nginx 52 | spec: 53 | containers: 54 | - name: nginx 55 | image: nginx:latest 56 | ports: 57 | - containerPort: 80 58 | ``` 59 | 60 | ## Как масштабировать приложение в Kubernetes 61 | 62 | 1. Создайте файл `deployment.yaml`, описывающий Deployment. 63 | 2. Примените файл с помощью команды `kubectl apply -f deployment.yaml`. 64 | 3. Измените количество реплик в Deployment с помощью команды `kubectl scale deployment --replicas=`. 65 | 4. Kubernetes автоматически создаст или удалит Pod в соответствии с желаемым количеством реплик. 66 | 67 | Пример `deployment.yaml`: 68 | 69 | ```yaml 70 | apiVersion: apps/v1 71 | kind: Deployment 72 | metadata: 73 | name: nginx-deployment 74 | spec: 75 | replicas: 3 76 | selector: 77 | matchLabels: 78 | app: nginx 79 | template: 80 | metadata: 81 | labels: 82 | app: nginx 83 | spec: 84 | containers: 85 | - name: nginx 86 | image: nginx:latest 87 | ports: 88 | - containerPort: 80 89 | ``` 90 | 91 | ## Каковы способы обеспечения безопасности API в Kubernetes 92 | 93 | - **RBAC (Role-Based Access Control)**: механизм управления доступом на основе ролей. 94 | - **Network Policies**: механизм управления сетевым доступом к Pod. 95 | - **Pod Security Policies**: механизм управления безопасностью Pod. 96 | - **Service Accounts**: механизм управления доступом к API Kubernetes. 97 | 98 | ## Зачем использует Kube-apiserver? 99 | 100 | `kube-apiserver` - это компонент Kubernetes, который предоставляет API для управления кластером Kubernetes. Он обрабатывает запросы от различных клиентов, таких как `kubectl`, и взаимодействует с другими компонентами кластера для управления ресурсами. 101 | 102 | ## Расскажите, как вы будете запускать приложение в Kubernetes, если из инструментов у вас только kubectl? 103 | 104 | Для запуска приложения в Kubernetes необходимо упаковать его в контейнер, создать Deployment для запуска контейнера в виде набора реплик (подов), настроить сервис LoadBalancer для доступа к приложению из интернета, и создать Ingress для маршрутизации трафика до приложения 105 | 106 | 1. Упаковать приложение в контейнер и загрузить его в Docker Registry. 107 | 2. Создать Deployment, описывающий контейнер с приложением. 108 | 3. Применить Deployment с помощью команды `kubectl apply -f deployment.yaml`. 109 | 4. Создать Service, описывающий доступ к Deployment. 110 | 5. Применить Service с помощью команды `kubectl apply -f service.yaml`. 111 | 6. Создать Ingress, описывающий маршрутизацию трафика до Service. 112 | 7. Применить Ingress с помощью команды `kubectl apply -f ingress.yaml`. 113 | 114 | ## Какие типы сервисов в Kubernetes вы знаете? 115 | 116 | - **ClusterIP**: сервис с внутренним IP-адресом, доступный только внутри кластера. 117 | - **NodePort**: сервис с открытым портом на каждом узле кластера. 118 | - **LoadBalancer**: сервис с облачным балансировщиком нагрузки. 119 | - **ExternalName**: сервис с внешним DNS-именем. 120 | -------------------------------------------------------------------------------- /content/4.infrastructure/redis.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Redis 3 | sidebar_position: 6 4 | --- 5 | 6 | ## Что такое Redis? 7 | Redis (Remote Dictionary Server) — это открытое программное обеспечение, реализующее структуру данных типа ключ-значение в памяти, используемое как база данных, кэш и брокер сообщений. 8 | 9 | ## Объясните функцию репликации Redis 10 | Репликация Redis — это процесс копирования данных из одного Redis-сервера на другой. Она позволяет создать копию данных для повышения отказоустойчивости и масштабируемости. 11 | 12 | ## Какие типы данных поддерживает Redis? 13 | Redis поддерживает различные типы данных: 14 | - **Строки**: последовательность байтов. 15 | - **Списки**: упорядоченный список строк. 16 | - **Множества**: коллекция уникальных элементов. 17 | - **Хэши**: коллекция полей и значений. 18 | - **Сортированные множества**: множество уникальных элементов с ассоциированными значениями. 19 | 20 | ## В чем разница между Memcached и Redis? 21 | 22 | Redis: 23 | - Поддерживает различные типы данных. 24 | - Поддерживает репликацию данных. 25 | - Поддерживает сохранение данных на диск. 26 | - Поддерживает транзакции. 27 | - Поддерживает публикацию/подписку. 28 | 29 | Memcached: 30 | - Поддерживает только хранение данных в виде ключ-значение. 31 | - Не поддерживает репликацию данных. 32 | - Не поддерживает сохранение данных на диск. 33 | - Не поддерживает транзакции. 34 | - Не поддерживает публикацию/подписку. 35 | 36 | ## Каковы ограничения Redis? 37 | 38 | Ограничения Redis: 39 | - **Ограниченный объем памяти**: Redis хранит все данные в памяти, поэтому объем данных ограничен объемом доступной памяти. 40 | - **Отсутствие встроенной шифрования**: Redis не предоставляет встроенных средств шифрования данных. 41 | - **Отсутствие механизма управления доступом**: Redis не имеет встроенного механизма управления доступом к данным. 42 | - **Отсутствие механизма сжатия данных**: Redis не предоставляет встроенного механизма сжатия данных. 43 | 44 | ## Как повысить надежность Redis? 45 | Для повышения надежности Redis можно использовать различные стратегии, включая репликацию данных, использование fsync() для синхронизации данных с диском и разделение данных между несколькими серверами. 46 | 47 | -------------------------------------------------------------------------------- /content/4.infrastructure/Брокеры сообщений/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Брокеры сообщений", 3 | "position": 1, 4 | "link": { 5 | "type": "generated-index" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /content/4.infrastructure/Брокеры сообщений/kafka.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Apache Kafka 3 | sidebar_position: 2 4 | --- 5 | 6 | ## Что такое Consumer Group в Apache Kafka? 7 | 8 | Consumer Group в Apache Kafka - это группа консьюмеров, которые читают данные из топиков. Каждый консьюмер в группе читает данные из отдельной партиции и обрабатывает их. Consumer Group обеспечивает масштабируемость и отказоустойчивость при чтении данных из топиков. Каждый консьюмер в группе читает данные из отдельной партиции, что позволяет распределить нагрузку на кластер. Если один из консьюмеров выходит из строя, другие консьюмеры в группе могут продолжить чтение данных без прерываний. 9 | 10 | ```mermaid 11 | graph LR 12 | A[Consumer Group] --> B[Consumer 1] 13 | A --> C[Consumer 2] 14 | A --> D[Consumer 3] 15 | B --> E[Topic Partition 1] 16 | C --> F[Topic Partition 2] 17 | D --> G[Topic Partition 3] 18 | E --> H[Topic] 19 | F --> H 20 | G --> H 21 | ``` 22 | 23 | ## Что такое оффсет в Apache Kafka? 24 | 25 | Оффсет (offset) в Apache Kafka - это уникальный идентификатор для каждой записи в партиции. Оффсет используется для отслеживания прогресса чтения данных из топиков. Каждый консьюмер в Kafka хранит оффсет для каждой партиции, чтобы знать, какие данные уже были прочитаны. Консьюмеры используют оффсеты для управления прогрессом чтения данных и обеспечения надежности. Если консьюмер выходит из строя или перезапускается, он может продолжить чтение данных с последнего оффсета, который он запомнил. 26 | -------------------------------------------------------------------------------- /content/4.infrastructure/Брокеры сообщений/overall.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Общие вопросы 3 | sidebar_position: 1 4 | --- 5 | 6 | ## Exactly Once 7 | Exactly Once означает, что каждое сообщение будет обработано ровно один раз. Это достигается путем гарантии, что каждое сообщение будет доставлено и обработано ровно один раз, без дубликатов и без пропусков. Для достижения этой гарантии необходимо использовать транзакции и идемпотентные операции. В контексте Kafka, это может быть достигнуто с помощью Kafka Streams или Kafka Connect, которые поддерживают транзакции и идемпотентные операции. 8 | 9 | ## At Least Once 10 | At Least Once означает, что каждое сообщение будет обработано по крайней мере один раз. Это гарантирует, что каждое сообщение будет обработано, но может быть обработано более одного раза в случае сбоев или повторных доставок. Это обычно достигается путем повторной доставки сообщений в случае сбоев. В Kafka, это может быть достигнуто с помощью настройки enable.idempotence для производителей и processing.guarantee для Kafka Streams. 11 | 12 | ## At Most Once 13 | At Most Once означает, что каждое сообщение будет обработано не более одного раза. Это гарантирует, что каждое сообщение будет обработано не более одного раза, но может быть пропущено в случае сбоев. Это обычно достигается путем отключения повторной доставки сообщений и использования неидемпотентных операций. В Kafka, это может быть достигнуто с помощью настройки enable.idempotence для производителей и processing.guarantee для Kafka Streams, но с осторожностью, так как это может привести к потере сообщений. 14 | 15 | ## Что такое Push и Pull модель поведения брокеров сообщений? 16 | 17 | Push и Pull модели поведения брокеров сообщений определяют способ доставки сообщений от брокера к клиенту. 18 | 19 | - **Push модель**: В Push модели брокер активно отправляет сообщения клиенту, когда они становятся доступными. Клиент не запрашивает сообщения у брокера, а брокер отправляет их клиенту по мере появления. Push модель обеспечивает низкую задержку и высокую производительность, так как сообщения доставляются немедленно. 20 | 21 | - **Pull модель**: В Pull модели клиент запрашивает сообщения у брокера, когда он готов их обработать. Клиент опрашивает брокера и получает сообщения, которые были отправлены в момент запроса. Pull модель обеспечивает гибкость и контроль над процессом получения сообщений, так как клиент сам решает, когда и какие сообщения получать. 22 | 23 | Push и Pull модели могут использоваться в различных системах обмена сообщениями в зависимости от требований к производительности и надежности. 24 | 25 | ## Что такое Pub/Sub модель обмена сообщениями? 26 | 27 | Pub/Sub (Publish/Subscribe) модель обмена сообщениями - это архитектурный шаблон, который позволяет отправителям (публикаторам) и получателям (подписчикам) обмениваться сообщениями через посредника (брокера). 28 | 29 | - **Публикаторы (Publishers)**: Публикаторы отправляют сообщения в темы (топики) брокера. Темы - это категории сообщений, которые группируются по схожим характеристикам или темам. Публикаторы не знают, кто будет получать сообщения, они просто отправляют их в темы. 30 | 31 | - **Подписчики (Subscribers)**: Подписчики получают сообщения из тем брокера, на которые они подписаны. Подписчики могут подписываться на несколько тем и получать сообщения, которые соответствуют их интересам. Подписчики не знают, откуда именно приходят сообщения, они просто получают их из тем. 32 | 33 | - **Брокер (Broker)**: Брокер - это посредник между публикаторами и подписчиками. Брокер принимает сообщения от публикаторов и отправляет их подписчикам в соответствии с их подписками. Брокер обеспечивает масштабируемость, надежность и гарантии доставки сообщений между публикаторами и подписчиками. 34 | 35 | Pub/Sub модель обмена сообщениями широко используется в распределенных системах для обмена сообщениями между компонентами и обеспечения асинхронной связи. 36 | -------------------------------------------------------------------------------- /content/5.systemdesign/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Системный дизайн", 3 | "position": 6, 4 | "link": { 5 | "type": "generated-index", 6 | "description": "Вопросы с собеседований, разбитые по темам" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /content/5.systemdesign/data-management.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Управление данными 3 | sidebar_position: 3 4 | --- 5 | 6 | ## CQRS 7 | 8 | CQRS (Command Query Responsibility Segregation) — это шаблон проектирования, который разделяет операции над данными на две категории: 9 | 10 | 1. Команды: 11 | - Изменяют состояние системы. 12 | 13 | Примеры: "Создать заказ", "Обновить товар", "Удалить пользователя". 14 | 15 | 2. Запросы: 16 | - Не изменяют состояние системы. 17 | - Возвращают данные для отображения или обработки. 18 | 19 | Примеры: "Получить список товаров", "Просмотреть детали заказа", "Найти пользователя по имени". 20 | 21 | ### Преимущества: 22 | - Повышение производительности: 23 | - Отдельные модели для чтения и записи оптимизированы для своих задач. 24 | - Запросы не блокируют команды и наоборот. 25 | - Улучшенная масштабируемость: 26 | - Модели можно масштабировать независимо друг от друга. 27 | - Легче добавить горизонтальное масштабирование для чтения. 28 | - Упрощенная разработка: 29 | - Четкое разделение обязанностей между моделями. 30 | - Повышение читабельности и сопровождаемости кода. 31 | - Повышение безопасности: 32 | - Разграничение доступа к моделям чтения и записи. 33 | - Снижение риска случайного изменения данных. 34 | 35 | ### Недостатки 36 | Увеличенная сложность: 37 | - Требуется проектировать и поддерживать две модели. 38 | - Возможна дубликация кода. 39 | - Необходимость согласованности: 40 | - Нужно синхронизировать данные между моделями при обновлениях. 41 | - Возможны проблемы с "непоследовательным чтением". 42 | 43 | ## Event Sourcing 44 | 45 | Event Sourcing - это шаблон проектирования, в котором все изменения состояния программного приложения фиксируются как последовательность событий в журнале append-only. Это означает, что события добавляются в журнал по мере их возникновения, и существующие записи никогда не изменяются. 46 | 47 | Вот некоторые ключевые концепции Event Sourcing: 48 | 49 | - **События (Events)**: События - это атомарные единицы изменений, которые произошли в системе. Каждое событие представляет собой факт, произошедший в определенный момент времени, и содержит данные, необходимые для понимания этого факта. Например, событием может быть "Заказ создан" или "Товары добавлены в корзину". 50 | - **Хранилище событий (Event Store)**: Хранилище событий - это специализированная база данных, предназначенная для неизменяемого хранения последовательности событий. В отличие от традиционных баз данных, хранилище событий не перезаписывает существующие данные. Это позволяет просматривать всю историю изменений системы. 51 | - **Проекции (Projections)**: Проекции - это механизм, который используется для создания представлений данных из последовательности событий. Поскольку сырые данные событий могут быть громоздкими для запросов, проекции материализуют данные таким образом, чтобы их можно было эффективно читать. 52 | 53 | Существуют различные типы проекций, которые могут использоваться для разных целей, например, для отображения текущего состояния сущности или для создания аналитических отчетов. 54 | 55 | ### Преимущества 56 | - **Аудит-трейл (Audit Trail)**: Поскольку все события сохраняются неизменными, Event Sourcing обеспечивает полный аудит-трейл всех изменений в системе. Это может быть полезно для отладки, обеспечения соответствия требованиям и анализа бизнес-процессов. 57 | - **Воспроизводимость (Reproducibility)**: С помощью последовательности событий можно воспроизвести состояние системы на любой момент времени. Это позволяет отслеживать ошибки, тестировать различные сценарии и повторно запускать вычисления. 58 | - **Масштабируемость (Scalability)**: Event Sourcing хорошо подходит для распределенных систем, поскольку события могут легко публиковаться и подписываться различными компонентами системы. 59 | 60 | ### Недостатки 61 | - **Сложность (Complexity)**: Реализация Event Sourcing может быть более сложной, чем традиционные подходы к управлению данными. Необходимо тщательно продумывать, как представлять события, создавать и обновлять проекции. 62 | - **Запросы (Queries)**: Запросы к данным в системе Event Sourcing могут быть менее эффективными, чем запросы к традиционной базе данных. Это связано с тем, что для получения текущего состояния сущности может потребоваться реконструкция из последовательности событий. 63 | 64 | ## Sharding / Partitioning 65 | 66 | Шардинг и партицирование – это два метода масштабирования баз данных, которые позволяют распределять данные по нескольким серверам. 67 | 68 | Шардинг (от англ. shard – осколок) – это горизонтальное масштабирование, при котором данные одной таблицы разделяются на несколько частей, называемых шардами. 69 | 70 | Партицирование (от англ. partition – раздел) – это метод, при котором данные одной таблицы разделяются на несколько логических частей, называемых партициями. 71 | 72 | ### В чем же разница? 73 | 74 | Шардинг – это более низкоуровневый метод, который работает на уровне физических серверов. При шардинге данные распределяются по разным серверам, и каждый сервер хранит только часть данных. 75 | 76 | Партицирование – это более высокоуровневый метод, который работает на уровне логических разделов. При партицировании данные одной таблицы разделяются на несколько логических частей, но эти части могут храниться на одном сервере. 77 | 78 | ### Преимущества шардинга 79 | - **Горизонтальное масштабирование**: Шардинг позволяет масштабировать базу данных по горизонтали, добавляя новые серверы. 80 | - **Улучшение производительности**: Шардинг может улучшить производительность за счет распределения нагрузки между несколькими серверами. 81 | - **Повышение доступности**: Шардинг может повысить доступность базы данных, поскольку при выходе из строя одного сервера остальные серверы будут продолжать работать. 82 | 83 | ### Недостатки шардинга 84 | - **Сложность**: Шардинг может быть более сложным в реализации и управлении, чем партицирование. 85 | - **Необходимость репликации**: Для обеспечения доступности данных при шардинге необходимо реплицировать данные на несколько серверов. 86 | 87 | ### Преимущества партиционирования 88 | - **Простота**: Партицирование проще в реализации и управлении, чем шардинг. 89 | - **Улучшение производительности**: Партицирование может улучшить производительность за счет разделения данных на логические части. 90 | - **Повышение доступности**: Партицирование может повысить доступность базы данных, поскольку при выходе из строя одной части данных остальные части будут продолжать работать. 91 | 92 | ### Недостатки партиционирования 93 | - **Ограниченная масштабируемость**: Партицирование не позволяет масштабировать базу данных по горизонтали так же эффективно, как шардинг. 94 | - **Сложность запросов**: Запросы к данным, которые разделены на несколько партиций, могут быть более сложными. -------------------------------------------------------------------------------- /content/5.systemdesign/load-balancing.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Load Balancer 3 | sidebar_position: 2 4 | --- 5 | 6 | ## Что такое балансировщик нагрузки? 7 | 8 | Балансировщик нагрузки (Load Balancer) – это аппаратное или программное устройство, которое распределяет трафик между несколькими серверами. 9 | 10 | Это позволяет: 11 | - **Увеличить производительность**: Распределяя нагрузку между серверами, балансировщик нагрузки помогает избежать перегрузки одного сервера и обеспечивает более быстрое время отклика для пользователей. 12 | - **Повысить надежность**: Если один сервер выходит из строя, балансировщик нагрузки может перенаправить трафик на другие серверы, что обеспечивает бесперебойную работу приложения. 13 | - **Обеспечить масштабируемость**: Балансировщик нагрузки позволяет легко добавлять новые серверы в пул серверов по мере роста нагрузки. 14 | 15 | ## Какие типы балансировщиков нагрузки существуют? 16 | 17 | Существует два основных типа балансировщиков нагрузки: 18 | - **Балансировщики нагрузки на сетевом уровне**: Эти балансировщики работают на 4-м транспортном уровне OSI. Они распределяют трафик на основе IP-адреса, порта или MAC-адреса. 19 | - **Балансировщики нагрузки на прикладном уровне**: Эти балансировщики работают на 7-м прикладном уровне OSI. Они могут распределять трафик на основе более сложных критериев, таких как URL-адрес, тип запроса или содержимое HTTP-заголовка. 20 | 21 | ## Какие алгоритмы балансировки нагрузки существуют? 22 | 23 | 1. Round Robin (Круговой метод) 24 | - Описание: Запросы распределяются по серверам в циклическом порядке. 25 | - Преимущества: Простота реализации, не требует информации о состоянии серверов. 26 | - Недостатки: Не учитывает нагрузку на серверы, может привести к перегрузке некоторых серверов. 27 | 28 | 2. Weighted Round Robin (Взвешенный круговой метод) 29 | - Описание: Запросы распределяются по серверам с учетом их веса. Вес может отражать производительность сервера, количество доступных ресурсов или другие факторы. 30 | - Преимущества: Более равномерно распределяет нагрузку по серверам, чем Round Robin. 31 | - Недостатки: Требует ручной настройки весов серверов. 32 | 33 | 3. IP Hash (Хеширование IP) 34 | - Описание: Запросы распределяются по серверам на основе хеш-функции от IP-адреса клиента. 35 | - Преимущества: Обеспечивает равномерное распределение нагрузки, гарантирует, что все запросы от одного клиента будут направляться на один и тот же сервер. 36 | - Недостатки: Может привести к перегрузке серверов с низким хеш-кодом. 37 | 38 | 4. Least Connections (Наименьшее количество подключений) 39 | - Описание: Запросы направляются на сервер с наименьшим количеством активных подключений. 40 | - Преимущества: Динамически распределяет нагрузку по серверам, учитывая их текущую загрузку. 41 | - Недостатки: Может привести к тому, что некоторые серверы будут постоянно недогружены, а другие перегружены. 42 | 43 | 5. Least Response Time (Наименьшее время отклика) 44 | - Описание: Запросы направляются на сервер с наименьшим временем отклика. 45 | - Преимущества: Обеспечивает минимальное время отклика для пользователей. 46 | - Недостатки: Может быть неточным, если время отклика серверов постоянно меняется. 47 | 48 | 6. Least Loaded (Наименьшая загрузка) 49 | - Описание: Запросы направляются на сервер с наименьшей загрузкой. 50 | - Преимущества: Обеспечивает равномерное распределение нагрузки по серверам. 51 | - Недостатки: Требует точной информации о загрузке серверов. 52 | 53 | 7. Resource-based (На основе ресурсов) 54 | - Описание: Запросы распределяются по серверам с учетом доступных ресурсов, таких как CPU, RAM, disk space. 55 | - Преимущества: Обеспечивает оптимальное использование ресурсов серверов. 56 | - Недостатки: Требует точной информации о доступных ресурсах серверов. 57 | 58 | ## Какие популярные решения для балансировщиков нагрузки? 59 | 60 | Существует множество решений для балансировщиков нагрузки, как коммерческих, так и с открытым исходным кодом. Вот несколько примеров: 61 | 62 | Коммерческие: 63 | - F5 BIG-IP: https://www.f5.com/products/big-ip-services 64 | - Citrix ADC: https://docs.netscaler.com/en-us/citrix-adc.html 65 | - A10 Thunder: https://www.a10networks.com/products/thunder-adc/ 66 | - Radware Alteon: https://www.radware.com/products/alteon/ 67 | - Barracuda Load Balancer: https://www.barracuda.com/products/application-protection/load-balancer 68 | 69 | С открытым исходным кодом: 70 | - HAProxy: https://www.haproxy.org/ 71 | - Nginx: https://www.nginx.com/ 72 | - LVS: https://www.nasdaq.com/market-activity/stocks/lvs/option-chain 73 | - Keepalived: https://www.keepalived.org/ 74 | 75 | ## Какие недостатки использования балансировщиков нагрузки? 76 | 77 | Использование балансировщиков нагрузки имеет несколько недостатков, таких как: 78 | - **Дополнительная сложность**: Балансировщик нагрузки добавляет дополнительный уровень сложности в инфраструктуру. 79 | - **Дополнительные расходы**: Балансировщики нагрузки могут быть дорогими, особенно для высокопроизводительных решений. 80 | - **Единая точка отказа**: Балансировщик нагрузки может стать единой точкой отказа, если он выходит из строя. 81 | 82 | ## Как включить Round Robin в nginx? 83 | 84 | Для включения Round Robin в nginx, добавьте следующую конфигурацию в блок `http`: 85 | 86 | ```nginx 87 | upstream backend { 88 | server backend1.example.com; 89 | server backend2.example.com; 90 | server backend3.example.com; 91 | } 92 | 93 | server { 94 | listen 80; 95 | server_name example.com; 96 | 97 | location / { 98 | proxy_pass http://backend; 99 | } 100 | } 101 | ``` 102 | 103 | В этом примере: 104 | - `upstream backend` определяет группу серверов, на которые будет распределяться трафик. 105 | - `server backend1.example.com` определяет адрес первого сервера. 106 | - `proxy_pass http://backend` указывает nginx использовать Round Robin для распределения трафика между серверами в группе `backend`. -------------------------------------------------------------------------------- /content/5.systemdesign/microservices.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Микросервисы 3 | sidebar_position: 1 4 | --- 5 | 6 | ## Что такое микросервисы? 7 | 8 | Микросервисная архитектура – это подход к разработке ПО, где приложение разбивается на множество независимых, слабосвязанных микросервисов. Каждый сервис выполняет определенную функцию и автономно работает в своем процессе. 9 | 10 | ```mermaid 11 | graph LR 12 | A(Клиент/Пользователь) --> B(API-шлюз) 13 | B(API-шлюз) --> C(Сервис 1) 14 | C(Сервис 1) --> D(База данных) 15 | B(API-шлюз) --> E(Сервис 2) 16 | E(Сервис 2) --> F(База данных) 17 | ``` 18 | 19 | ### Преимущества 20 | - Масштабируемость: Легко добавлять, удалять или масштабировать отдельные сервисы. 21 | - Независимость: Разработка и развертывание каждого сервиса происходят независимо. 22 | - Надежность: Отказ одного сервиса не влияет на работу других. 23 | - Простота обслуживания: Легче найти и исправить ошибки в небольших сервисах. 24 | 25 | ### Недостатки 26 | - Сложность разработки: Требует более сложного проектирования и управления. 27 | - Сложность тестирования: Нужно тестировать не только каждый сервис, но и их взаимодействие. 28 | - Увеличение накладных расходов: Больше сервисов – больше сетевых запросов, больше инфраструктуры. 29 | 30 | ----- 31 | 32 | ## Какие паттерны проектирования используются в микросервисной архитектуре? 33 | 34 | - API Gateway: Этот паттерн используется для маршрутизации запросов к соответствующим микросервисам. API Gateway действует как единая точка входа в систему, обеспечивая аутентификацию, авторизацию, логирование, мониторинг и обработку ошибок. 35 | - Service Discovery: Сервисы в микросервисной архитектуре могут динамически добавляться или удаляться. Service Discovery позволяет сервисам находить друг друга без необходимости вручную настраивать сетевые адреса. 36 | - Circuit Breaker: Этот паттерн предотвращает запросы к сервису, который уже не отвечает, предотвращая таким образом каскадные сбои в системе. 37 | - CQRS (Command Query Responsibility Segregation): Разделение операций чтения и записи данных на разные модели позволяет оптимизировать производительность и масштабируемость системы. 38 | - Event Sourcing: Вместо хранения текущего состояния системы, сохраняется последовательность событий, которые приводят к этому состоянию. Это позволяет легко - восстанавливать состояние системы и анализировать историю изменений. 39 | - Saga Pattern: Используется для управления распределенными транзакциями, позволяя выполнять последовательности локальных транзакций, каждая из которых может быть отменена, если что-то пойдет не так. 40 | 41 | ## Чем микросервисы отличаются от монолитной архитектуры? 42 | 43 | 1. Структура приложения 44 | - Монолитная архитектура: Приложение разрабатывается как единое целое, где все компоненты тесно связаны и работают вместе в рамках одного процесса. Это может быть как одно приложение, так и несколько взаимосвязанных приложений, работающих в рамках одного проекта. 45 | - Микросервисная архитектура: Приложение разбивается на множество независимых сервисов, каждый из которых выполняет свою функцию и может быть разработан, развернут и масштабирован независимо от остальных. 46 | 2. Масштабирование 47 | - Монолитная архитектура: Масштабирование обычно происходит путем добавления больше ресурсов (например, CPU, память) к одному экземпляру приложения. Это может быть неэффективно, если только одна часть приложения требует больше ресурсов. 48 | - Микросервисная архитектура: Масштабирование происходит на уровне отдельных сервисов, что позволяет оптимизировать использование ресурсов и улучшить производительность. 49 | 3. Разработка и развертывание 50 | - Монолитная архитектура: Изменения в любой части приложения требуют пересборки и перезапуска всего приложения, что может быть затратно по времени и ресурсам. 51 | - Микросервисная архитектура: Изменения в одном сервисе не влияют на другие, что позволяет быстро и независимо разрабатывать, тестировать и развертывать отдельные сервисы. 52 | 4. Технологический стек 53 | - Монолитная архитектура: Обычно использует один технологический стек для всего приложения, что может ограничивать выбор технологий. 54 | - Микросервисная архитектура: Позволяет использовать разные технологии для разных сервисов, что может улучшить производительность и гибкость системы. 55 | 5. Управление данными 56 | - Монолитная архитектура: Данные обычно хранятся в одной базе данных, что может привести к проблемам с производительностью и масштабируемостью. 57 | - Микросервисная архитектура: Каждый сервис может иметь свою собственную базу данных, что позволяет оптимизировать структуру данных под конкретные потребности каждого сервиса. 58 | 6. Отказоустойчивость 59 | - Монолитная архитектура: Сбой в одной части приложения может привести к сбою всего приложения. 60 | - Микросервисная архитектура: Отдельные сервисы могут быть изолированы друг от друга, что позволяет системе продолжать работать даже при сбоях в некоторых сервисах. 61 | 7. Сложность 62 | - Монолитная архитектура: Обычно проще в разработке, тестировании и развертывании из-за единой кодовой базы и технологического стека. 63 | - Микросервисная архитектура: Вводит дополнительную сложность из-за необходимости управления множеством сервисов, обеспечения их взаимодействия и координации. -------------------------------------------------------------------------------- /content/6.patterns/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Паттерны", 3 | "position": 6, 4 | "link": { 5 | "type": "generated-index", 6 | "description": "Вопросы с собеседований, разбитые по темам" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /content/6.patterns/solid.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: SOLID 3 | sidebar_position: 1 4 | --- 5 | 6 | ## S - Принцип единственной ответственности (Single Responsibility Principle) 7 | 8 | Каждый класс или модуль должен иметь одну и только одну причину для изменения. То есть, он должен решать только одну задачу. 9 | 10 | ```go title="Плохой пример" 11 | type User struct { 12 | Name string 13 | Email string 14 | } 15 | 16 | func (u *User) Save() { 17 | // сохраняет пользователя в базу данных 18 | } 19 | 20 | func (u *User) SendEmail() { 21 | // отправляет email пользователю 22 | } 23 | ``` 24 | 25 | ```go title="Хороший пример" 26 | type User struct { 27 | Name string 28 | Email string 29 | } 30 | 31 | type UserRepository struct {} 32 | 33 | func (r *UserRepository) Save(u User) { 34 | // сохраняет пользователя в базу данных 35 | } 36 | 37 | type EmailService struct {} 38 | 39 | func (e *EmailService) SendEmail(u User) { 40 | // отправляет email пользователю 41 | } 42 | ``` 43 | 44 | ## O - Принцип открытости/закрытости (Open/Closed Principle) 45 | 46 | Программные сущности должны быть открыты для расширения, но закрыты для модификации. 47 | 48 | ```go title="Плохой пример" 49 | // добавление новых форм требует изменения существующего кода. 50 | type Shape struct {} 51 | 52 | func (s *Shape) Area(shapeType string, dimensions ...float64) float64 { 53 | if shapeType == "circle" { 54 | return 3.14 * dimensions[0] * dimensions[0] 55 | } else if shapeType == "rectangle" { 56 | return dimensions[0] * dimensions[1] 57 | } 58 | return 0 59 | } 60 | ``` 61 | 62 | ```go title="Хороший пример" 63 | // использование интерфейсов для расширения. 64 | type Shape interface { 65 | Area() float64 66 | } 67 | 68 | type Circle struct { 69 | Radius float64 70 | } 71 | 72 | func (c Circle) Area() float64 { 73 | return 3.14 * c.Radius * c.Radius 74 | } 75 | 76 | type Rectangle struct { 77 | Width, Height float64 78 | } 79 | 80 | func (r Rectangle) Area() float64 { 81 | return r.Width * r.Height 82 | } 83 | ``` 84 | 85 | ## L - Принцип подстановки Барбары Лисков (Liskov Substitution Principle) 86 | 87 | Наследующие классы должны дополнять, а не изменять функциональность базовых классов. То есть, если S является подтипом T, объекты типа T могут быть заменены объектами типа S без изменения желаемых свойств программы. 88 | 89 | ```go title="Плохой пример" 90 | // наследующий класс изменяет поведение базового класса. 91 | type Bird struct {} 92 | 93 | func (b Bird) Fly() string { 94 | return "I can fly" 95 | } 96 | 97 | type Ostrich struct { 98 | Bird 99 | } 100 | 101 | func (o Ostrich) Fly() string { 102 | return "I can't fly" 103 | } 104 | ``` 105 | 106 | ```go title="Хороший пример" 107 | // интерфейсы для различных типов поведения. 108 | type Flyer interface { 109 | Fly() string 110 | } 111 | 112 | type Sparrow struct {} 113 | 114 | func (s Sparrow) Fly() string { 115 | return "I can fly" 116 | } 117 | 118 | type Ostrich struct {} 119 | 120 | func (o Ostrich) Run() string { 121 | return "I can run fast" 122 | } 123 | ``` 124 | 125 | ## I - Принцип разделения интерфейса (Interface Segregation Principle) 126 | 127 | Клиенты не должны зависеть от интерфейсов, которые они не используют. То есть, лучше иметь несколько специализированных интерфейсов, чем один общий. 128 | 129 | ```go title="Плохой пример" 130 | // один интерфейс с методами, которые не используются всеми реализациями. 131 | type Worker interface { 132 | Work() 133 | Eat() 134 | } 135 | 136 | type Human struct {} 137 | 138 | func (h Human) Work() { 139 | // человек работает 140 | } 141 | 142 | func (h Human) Eat() { 143 | // человек ест 144 | } 145 | 146 | type Robot struct {} 147 | 148 | func (r Robot) Work() { 149 | // робот работает 150 | } 151 | 152 | func (r Robot) Eat() { 153 | // робот не ест 154 | } 155 | ``` 156 | 157 | ```go title="Хороший пример" 158 | // разделение интерфейсов. 159 | type Worker interface { 160 | Work() 161 | } 162 | 163 | type Eater interface { 164 | Eat() 165 | } 166 | 167 | type Human struct {} 168 | 169 | func (h Human) Work() { 170 | // человек работает 171 | } 172 | 173 | func (h Human) Eat() { 174 | // человек ест 175 | } 176 | 177 | type Robot struct {} 178 | 179 | func (r Robot) Work() { 180 | // робот работает 181 | } 182 | ``` 183 | 184 | ## D - Принцип инверсии зависимостей (Dependency Inversion Principle) 185 | 186 | Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций. 187 | 188 | ```go title="Плохой пример" 189 | // высокоуровневый модуль зависит от низкоуровневого. 190 | type LightBulb struct {} 191 | 192 | func (lb LightBulb) TurnOn() { 193 | // включить лампочку 194 | } 195 | 196 | func (lb LightBulb) TurnOff() { 197 | // выключить лампочку 198 | } 199 | 200 | type Switch struct { 201 | Bulb LightBulb 202 | } 203 | 204 | func (s Switch) Operate() { 205 | s.Bulb.TurnOn() 206 | } 207 | ``` 208 | 209 | ```go title="Хороший пример" 210 | // высокоуровневый модуль зависит от абстракции. 211 | type Switchable interface { 212 | TurnOn() 213 | TurnOff() 214 | } 215 | 216 | type LightBulb struct {} 217 | 218 | func (lb LightBulb) TurnOn() { 219 | // включить лампочку 220 | } 221 | 222 | func (lb LightBulb) TurnOff() { 223 | // выключить лампочку 224 | } 225 | 226 | type Switch struct { 227 | Device Switchable 228 | } 229 | 230 | func (s Switch) Operate() { 231 | s.Device.TurnOn() 232 | } 233 | ``` 234 | -------------------------------------------------------------------------------- /content/7.systems/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Операционные системы", 3 | "position": 5, 4 | "link": { 5 | "type": "generated-index", 6 | "description": "Вопросы с собеседований, разбитые по темам" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /content/7.systems/linux/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Вопросы по Linux", 3 | "position": 1, 4 | "link": { 5 | "type": "generated-index", 6 | "description": "Вопросы с собеседований, разбитые по темам" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /content/7.systems/linux/filesystem.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Файловая система 3 | sidebar_position: 2 4 | description: Вопросы связанные с файловой системой в Linux 5 | --- 6 | 7 | ## Что такое inode? 8 | 9 | Inode (индексный узел) — это структура данных, которая хранит метаданные о файле или каталоге, такие как права доступа, владелец, группа, размер, время создания, изменения и доступа, количество ссылок, указатели на блоки данных и т.д. Inode не хранит имя файла или каталога, а только его уникальный номер (inode number). Имя файла или каталога хранится в каталоге, который содержит записи, связывающие имя с номером inode. Таким образом, inode позволяет работать с файлами и каталогами по их номерам, а не по именам. 10 | 11 | Источники: 12 | - [Selectel](https://selectel.ru/blog/directory-structure-linux/) 13 | - [Serverspace](https://serverspace.ru/support/help/struktura-fajlovoj-sistemy-linux/) 14 | 15 | ## Чем отличается hard link от symbolic link? Что будет с файлом, если удалить hard link? symlink? 16 | 17 | Hard link (жесткая ссылка) и symbolic link (символическая ссылка) — это два способа создания ссылок на файлы или каталоги в Linux. Они имеют следующие отличия: 18 | 19 | - Hard link — это файл, который имеет тот же номер inode, что и оригинальный файл, и указывает на те же блоки данных. Hard link не хранит имя оригинального файла, а только его номер inode. Hard link можно создать только внутри одной файловой системы и только для обычных файлов, а не для каталога. 20 | - Symbolic link — это файл, который хранит путь к оригинальному файлу или каталогу, а не его номер inode. 21 | Symbolic link имеет свой собственный номер inode и блоки данных. Symbolic link можно создать для любого типа файла или каталога и в разных файловых системах. 22 | 23 | Если удалить hard link, то файл не будет удален, пока не будет удалена последняя жесткая ссылка на него. Количество жестких ссылок на файл можно узнать по счетчику в выводе команды ls -l. Если удалить symbolic link, то файл не будет затронут, так как символическая ссылка не влияет на счетчик жестких ссылок. 24 | 25 | Источники: 26 | - [rtfm](https://rtfm.co.ua/ru/unix-chto-takoe-symlink-hardlink-i-inode/) 27 | - [Stackoverflow](https://ru.stackoverflow.com/questions/536236/%d0%a1%d0%b8%d0%bc%d0%b2%d0%be%d0%bb%d1%8c%d0%bd%d1%8b%d0%b5-%d1%81%d1%81%d1%8b%d0%bb%d0%ba%d0%b8-%d0%b8-%d0%ba%d0%be%d0%bc%d0%b0%d0%bd%d0%b4%d0%b0-ln) 28 | 29 | ## Что означают разрешения 0764 для файла? 30 | 31 | Разрешения 0764 для файла означают, что владелец файла имеет права на чтение, запись и выполнение файла, группа, к которой принадлежит файл, имеет права на чтение и запись файла, а остальные пользователи имеют только право на чтение файла. 32 | 33 | Разрешения для файла или папки в Linux обычно представляются в восьмеричном или символьном формате. В восьмеричном формате каждая цифра от 0 до 7 соответствует комбинации разрешений на чтение (r), запись (w) и выполнение (x). Например, 7 означает rwx, 6 означает rw-, 4 означает r–, и т.д. 34 | 35 | Первая цифра в восьмеричном разрешении указывает на права владельца, вторая — на права группы, а третья — на права остальных. 36 | 37 | В символьном формате разрешения представляются тремя группами из трех символов, например, rwxrw-r–. Каждая группа соответствует одной из цифр в восьмеричном формате. Первая группа указывает на права владельца, вторая — на права группы, а третья — на права остальных. -------------------------------------------------------------------------------- /content/7.systems/linux/overall.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Общее 3 | sidebar_position: 1 4 | description: Общие вопросы по Linux 5 | --- 6 | 7 | ## Назовите 10 трехбуквенных команд в Линуксе 8 | 1. `pwd` - путь к текущему рабочему каталогу 9 | 2. `cat` - вывод содержимого файла в командной строке 10 | 3. `tar` - команда для архивирования нескольких файлов 11 | 4. `top` - эквивалент диспетчера задач в Windows 12 | 5. `man` - подсказки по командам, например: `man tail` покажет инструкцию к команде `tail`. 13 | 6. `zip` - команда для сжатия ваших файлов 14 | 7. `who` - информация о вошедших пользователях 15 | 8. `ssh` - соединение с удаленным сервером по протоколу SSH. 16 | 9. `scp` - копирует файлы между удаленным и локальным серверами по протоколу SSH. 17 | 10. `sar` - собирает информацию о использовании ресурсов системы, таких как процессор, память, сеть и диски, и сохраняет ее для последующего анализа. 18 | 19 | ## Как отлистать процессы 20 | Для того, чтобы отобразить список процессов в Linux, вы можете использовать различные команды, такие как ps, top, htop, pidof и другие. Каждая из этих команд имеет свои параметры и опции, которые позволяют фильтровать, сортировать и управлять процессами. Вот несколько примеров: 21 | 22 | - `ps` — выводит информацию о выбранных процессах. Вы можете использовать разные ключи, например, `ps -e` для вывода всех процессов, `ps -u username` для вывода процессов определенного пользователя, `ps -C command` для вывода процессов с определенным именем и т.д. 23 | - `top` — выводит динамически обновляемую информацию о процессах и ресурсах системы. Вы можете нажимать разные клавиши, чтобы изменять режим отображения, например, P для сортировки по использованию ЦП, M для сортировки по использованию памяти, k для убийства процесса по PID и т.д. 24 | - `htop` — улучшенная версия `top`, которая имеет более удобный интерфейс и больше возможностей. Вы можете использовать мышь для выбора и управления процессами, а также нажимать разные клавиши, например, F3 для поиска по процессам, F9 для убийства процесса, F4 для фильтрации по имени и т.д. 25 | - `pidof` — выводит идентификаторы процессов (PID) по имени команды. Например, `pidof sshd` выведет PID всех процессов `sshd`. 26 | 27 | ## Что такое proc-fs? sys-fs? 28 | 29 | proc-fs и sys-fs это две виртуальные файловые системы, которые используются в Linux для предоставления информации о процессах и системных параметрах. Они не хранятся на физическом диске, а генерируются ядром при обращении к ним. 30 | 31 | proc-fs обычно монтируется на /proc и содержит каталоги, соответствующие каждому процессу, а также файлы, содержащие общую информацию о системе, такую как версия ядра, загрузка ЦП, память и т.д. proc-fs позволяет получать и изменять различные атрибуты процессов, такие как приоритет, ограничения, сигналы и т.д 32 | 33 | sys-fs обычно монтируется на /sys и содержит информацию о доступном оборудовании, драйверах, подсистемах и настройках ядра. sys-fs позволяет управлять некоторыми параметрами ядра, такими как частота ЦП, состояние питания, горячая замена устройств и т.д 34 | 35 | Источники: 36 | - [Habr](https://habr.com/ru/companies/otus/articles/446614/) 37 | - [Wikipedia](https://ru.wikipedia.org/wiki/Procfs) 38 | 39 | ## Что такое initrd? 40 | Initrd (сокращение от англ. Initial RAM Disk, диск в оперативной памяти для начальной инициализации) — это временная файловая система, которая используется ядром Linux при начальной загрузке. Initrd обычно используется для начальной инициализации перед монтированием «настоящих» файловых систем. Initrd позволяет загружать необходимые модули ядра для работы с дисками, файловыми системами, сетью и другими подсистемами, которые нужны для доступа к корневому разделу. 41 | 42 | Initrd может быть создан с помощью различных утилит, таких как mkinitrd, dracut, genkernel и других. Initrd обычно хранится в сжатом виде рядом с ядром и загружается загрузчиком, таким как GRUB, LILO, SYSLINUX и т.д. После загрузки initrd, ядро запускает специальный скрипт, который выполняет необходимые действия для подключения корневого раздела и перехода к нему. 43 | 44 | Источники: 45 | - [Wikipedia](https://ru.wikipedia.org/wiki/Initrd) 46 | 47 | ## Назови команды, которые покажут на каких портах какие работают приложения, мониторинг сетевых соединений 48 | 49 | netstat - (Network Statistics) — это классическая утилита, присутствующая в большинстве дистрибутивов Linux, которая позволяет отображать различную информацию о сетевых соединениях, табле маршрутизации, статистике интерфейсов и многое другое. Для того чтобы увидеть список активных соединений и слушающих портов вместе с названиями приложений, используйте команду: netstat -tulnp 50 | ss - (Socket Statistics) - замена netstat, предоставляющая более быстрый и более детализированный вывод информации о сокетах. Эта утилита также позволяет отображать различную статистику сетевых соединений. Пример команды для просмотра активных соединений и слушающих портов: ss -tulnp 51 | lsof - (List Open Files) — это еще одна мощная утилита, которая не только может показывать открытые файлы, но и отображать информацию о сетевых соединениях (все сокеты технически являются файлами в UNIX и Linux). Команда дает возможность увидеть, какие порты используются процессами. Пример команды, позволяющей увидеть приложения и их открытые порты: lsof -i -nP 52 | Дополнение(telnet) - помощью telnet можно проверить, открыт ли порт и доступен ли сетевой сервис на устройстве без установки специализированного клиентского ПО. При работе telnet пытается установить соединение с указанным хостом (или IP-адресом) на конкретном порту. Если порт открыт и на нем работает сервис, который может принимать входящие соединения, telnet установит соединение, позволяя пользователю взаимодействовать с этим сервисом. 53 | Пример использования telnet для проверки доступности веб-сервера на порту 80: telnet example.com 80 )) 54 | 55 | -------------------------------------------------------------------------------- /content/7.systems/linux/signals.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Сигналы, процессы 3 | sidebar_position: 2 4 | description: Вопросы связанные с сигналами и процессами в Linux 5 | --- 6 | 7 | ## Для чего используется команда kill? 8 | 9 | Команда kill позволяет завершить процесс по его идентификатору (PID). Чтобы узнать PID процесса, можно использовать команду ps или top. Команда kill посылает процессу сигнал, который может быть разным в зависимости от параметра. По умолчанию это сигнал TERM, который просит процесс завершиться. Если процесс не реагирует на сигнал TERM, можно использовать сигнал KILL, который принудительно убивает процесс. 10 | 11 | Например, чтобы убить процесс с PID 1234, можно выполнить команду: 12 | ``` 13 | $: kill 1234 14 | # или 15 | $: kill -TERM 1234 16 | ``` 17 | 18 | Если процесс не завершается, можно выполнить команду: 19 | ``` 20 | $: kill -KILL 1234 21 | # или 22 | $: kill -9 1234 23 | ``` 24 | 25 | Источники: 26 | - [Selectel](https://selectel.ru/blog/tutorials/kill-and-killall-commands-in-linux/) 27 | - [Pingvinus](https://pingvinus.ru/note/ps-kill-killall) 28 | 29 | ## Чем отличаются сигналы, какие можно перехватить 30 | 31 | Сигналы — это способ обмена информацией между процессами в Linux. Сигналы могут быть отправлены ядром, другими процессами или пользователем с помощью команды kill. Сигналы могут быть разными по своему назначению и эффекту. Например, сигнал SIGINT прерывает выполнение процесса, сигнал SIGKILL убивает процесс без возможности обработки, сигнал SIGUSR1 и SIGUSR2 предназначены для пользовательских целей. 32 | 33 | Процессы могут перехватывать и обрабатывать некоторые сигналы, используя функцию signal или sigaction. Однако не все сигналы можно перехватить. Сигналы SIGKILL и SIGSTOP не могут быть перехвачены, игнорированы или заблокированы, так как они предназначены для надежного управления процессами. Также некоторые сигналы могут быть недоступны в зависимости от версии ядра или архитектуры системы. 34 | 35 | ## Что такое zombie и orphan? 36 | 37 | zombie — это процесс, который завершил свою работу, но еще не освободил ресурсы, такие как PID, статус и память. Zombie ожидает, что его родительский процесс прочитает его статус с помощью системного вызова wait. Если родительский процесс не делает этого, zombie остается в таблице процессов и занимает место. Zombie не потребляет ЦП и не может быть убит сигналами. Чтобы избавиться от zombie, нужно убить его родителя или перезагрузить систему. 38 | 39 | Orphan — это процесс, чей родительский процесс завершился, но не освободил его. Orphan продолжает работать, но теперь подчиняется процессу init, который становится его новым родителем. Init периодически вызывает wait, чтобы освободить сирот от их статусов. Orphans не являются проблемой для системы, так как они не засоряют таблицу процессов. Однако orphans могут потреблять ЦП, память и другие ресурсы, которые могут быть необходимы другим процессам. Чтобы избавиться от orphans, можно убить их сигналами или завершить их работу 40 | 41 | ## Когда процесс может не реагировать на SIGKILL? 42 | 43 | - Процесс является процессом-зомби, который уже завершился, но не освободил свои ресурсы. Зомби не может принимать сигналы и ожидает, что его родительский процесс считает его статус с помощью системного вызова wait. 44 | - Процесс находится в состоянии блокировки, когда он ожидает ввода-вывода или другого события, которое не наступило. Процесс не может завершиться, пока операционная система не вернет его в нормальное состояние. 45 | - Процесс является процессом init, который не получает от операционной системы сигналов, которые он не хочет обрабатывать. Процесс init может игнорировать SIGKILL, так как он является особым процессом, который отвечает за запуск и завершение других процессов. 46 | 47 | Источники: 48 | - [OpenNET](https://www.opennet.ru/docs/RUS/linux_parallel/node10.html) 49 | --------------------------------------------------------------------------------