数据存储格式

行、列存储

行存储和列存储用于描述数据组织方式,通常,行存储存取单位是整条记录,列存储存储单位是数据记录的一列。

姓名 年龄 省份
Li 25 山东
Zhao 23 北京
Zhang 22 辽宁

以上面数据为例,基于行存储,数据可能组织如下。

1
2
3
Li,25,山东
Zhao,23,北京
Zhang,22,辽宁

基于列存储,数据可能组织如下。

1
2
3
Li,Zhao,Zhang
25,23,22
山东,北京,辽宁

两者各有优劣,对比如下。

对比项 行存储 列存储
格式 按行存储 按列,每一列单独存储
粒度 记录 记录单列
索引 需要额外索引提高效率 数据即索引
读取 读取整条记录,可能存在冗余读取 按列多次读取,性能可能会更差
写入 一次写入,数据完整性更容易保证 按列多次写入,性能可能会更差
性能 建立索引、物化视图、冗余读写消耗大量资源 无需索引、视图
扩展 随着数据量增加为维持性能需要大量膨胀数据库 可横向扩展
应用 OLTP OLAP
全量操作 全量更新、聚合字段效率差 粒度更细,效率较高
压缩 压缩率较低 每一列数据同质,无二义性,且顺序存储,可达到较高压缩率

关系型数据库一般采用行存储,存储结构化数据,随着数据量增加,要提升性能需要付出很大的代价。列存储数据库一般具备可伸缩、可分区的特点,可以高效实现数据序列化、存储、检索。

数据库示例

行存储 列存储
MySQL HBase
MongoDB

参考

Docker应用

影音娱乐

  • Plex
  • jellyfin

办公下载

  • FileBrowser
  • Krusader (binhex-krusader)
  • Nextcloud
  • Transmission
  • 可道云
  • 迅雷远程
  • 蚂蚁笔记

Koel

docker command

1
2
3
4
5
6
7
8
9
10
11
12
docker run -d --name koel \
-p 2070:80 \
-e DB_CONNECTION=mysql \
-e DB_HOST=192.168.1.3 \
-e DB_DATABASE=koel \
-e DB_USERNAME=koel \
-e DB_PASSWORD=password \
-v /mnt/user/music:/music \
-v /mnt/user/appdata/koel/koel.env:/var/www/html/.env \
-v /mnt/user/appdata/koel/covers:/var/www/html/public/img/covers \
-v /mnt/user/appdata/koel/search_index:/var/www/html/storage/search-indexes \
phanan/koel

koel.env

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
44
45
46
47
48
49
50
51
52
53
54
55
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
APP_NAME=Koel
APP_ENV=production
APP_DEBUG=false
APP_URL=http://koel.domain.com

# A comma-separated list of (Koel server) hostnames accepted to access Koel.
# Leave this empty to allow access to Koel with any hostname.
# Example: localhost,192.168.0.1,yourdomain.com
TRUSTED_HOSTS=

# A random 32-char string. You can leave this empty if use php artisan koel:init.
APP_KEY=

# Database connection name, which corresponds to the database driver.
# Possible values are:
# mysql (MySQL/MariaDB - default)
# pgsql (PostgreSQL)
# sqlsrv (Microsoft SQL Server)
# sqlite-persistent (Local sqlite file)
# IMPORTANT: This value must present for `artisan koel:init` command to work.
DB_CONNECTION=mysql

DB_HOST=192.168.1.3
DB_PORT=
DB_DATABASE=koel
DB_USERNAME=koel
DB_PASSWORD=password

# Some providers (e.g. Heroku) provide a "database URL" instead separated config values, which
# you can use here instead.
DATABASE_URL=

# The absolute path to the root CA bundle if you're connecting to the MySQL database via SSL.
MYSQL_ATTR_SSL_CA=


# The storage driver. Valid values are:
# local: Store files on the server's local filesystem.
# sftp: Store files on an SFTP server.
# s3: Store files on Amazon S3 or a S3-compatible service (e.g. Cloudflare R2 or DigitalOcean Spaces). Koel Plus only.
# dropbox: Store files on Dropbox. Koel Plus only.
STORAGE_DRIVER=local


# The ABSOLUTE path to your media. This value can always be changed later via the web interface.
# Required if you're using the local file system to store your media (STORAGE_DRIVER=local).
MEDIA_PATH=


# S3 or S3-compatible service settings. Required if you're using S3 to store your media (STORAGE_DRIVER=s3).
# Remember to set CORS policy to allow access from your Koel's domain (or "*").
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
# For Cloudflare R2, set this to "auto". For S3 and other services, set this to the region of your bucket.
AWS_REGION=
AWS_ENDPOINT=
AWS_BUCKET=


# Dropbox settings. Required if you're using Dropbox to store your media (STORAGE_DRIVER=dropbox)
# Follow these steps to have these values filled:
# 1. Create a Dropbox app at https://www.dropbox.com/developers/apps
# 2. Run `php artisan koel:setup-dropbox` from the CLI and follow the instructions.
DROPBOX_APP_KEY=
DROPBOX_APP_SECRET=
DROPBOX_REFRESH_TOKEN=


# SFTP settings. Required if you're using SFTP to store your media (STORAGE_DRIVER=sftp).
SFTP_HOST=
SFTP_PORT=

# The absolute path of the directory to store the media files on the SFTP server.
# Make sure the directory exists and is writable by the SFTP user.
SFTP_ROOT=

# You can use either a username/password pair…
SFTP_USERNAME=
SFTP_PASSWORD=

# …or private key authentication:
SFTP_PRIVATE_KEY=
SFTP_PASSPHRASE=


# By default, Koel ignores dot files and folders. This greatly improves performance if your media
# root have folders like .git or .cache. If by any chance your media files are under a dot folder,
# set the following setting to false.
IGNORE_DOT_FILES=true


# The maximum scan time, in seconds. Increase this if you have a huge library.
# Note: This setting doesn't have effect when scanning via koel:sync.
APP_MAX_SCAN_TIME=600


# The memory limit, in MB, used by the scanning process.
# For example, if you want to set a memory limit of 2048MB, enter "2048" (without
# quotes) here.
MEMORY_LIMIT=


# The streaming method.
# Can be either 'php' (default), 'x-sendfile', or 'x-accel-redirect'
# See https://docs.koel.dev/usage/streaming for more information.
# Note: This setting doesn't have effect if the media needs transcoding (e.g. FLAC).

# It's HIGHLY recommended to use 'x-sendfile' or 'x-accel-redirect' if
# you plan to use the Koel mobile apps.

STREAMING_METHOD=php


# Full text search driver.
# Koel supports all drivers supported by Laravel (see https://laravel.com/docs/9.x/scout).
# Available drivers: 'tntsearch' (default), 'database', 'algolia' or 'meilisearch'.
# For Algolia or MeiliSearch, you need to provide the corresponding credentials.
SCOUT_DRIVER=tntsearch
ALGOLIA_APP_ID=
ALGOLIA_SECRET=
MEILISEARCH_HOST=
MEILISEARCH_KEY=


# Last.fm API can be used to fetch artist and album information, as well as to
# allow users to connect to their Last.fm account and scrobble.
# To integrate Koel with Last.fm, create an API account at
# https://www.last.fm/api/account/create and set the credentials here.
# Consult Koel's doc for more information.
LASTFM_API_KEY=
LASTFM_API_SECRET=


# Spotify API can be used to fetch artist and album images.
# To integrate Koel with Spotify, create a Spotify application at
# https://developer.spotify.com/dashboard/applications and set the credentials here.
# Consult Koel's docs for more information.
SPOTIFY_CLIENT_ID=
SPOTIFY_CLIENT_SECRET=


# To integrate Koel with YouTube, set the API key here.
# See https://docs.koel.dev/service-integrations#youtube for more information.
YOUTUBE_API_KEY=


# You can also configure Koel to use a CDN to serve the media files.
# This url must be mapped to the home URL of your Koel's installation.
# No trailing slash.
CDN_URL=


# To transcode FLAC to MP3 and stream it on the fly, make sure the following settings are sane.
# If you don't want to transcode FLAC (i.e. to stream it as-is), set this to false.
TRANSCODE_FLAC=false

# The full path of ffmpeg binary.
FFMPEG_PATH=/usr/local/bin/ffmpeg


# The bit rate of the output mp3 stream. Higher value results in better quality,
# but slower streaming and more bandwidth.
OUTPUT_BIT_RATE=128


# Whether to allow song downloading.
# Note that if you're downloading more than one song, Koel will zip them up
# using PHP's ZipArchive. So if the module isn't available in the current
# environment, such a download will (silently) fail.
ALLOW_DOWNLOAD=true


# Whether to create a backup of a song when deleting it from the filesystem.
BACKUP_ON_DELETE=true


# If using SSO, set the providers details here. Koel will automatically enable SSO if these values are set.
# Create an OAuth client and get these values from https://console.developers.google.com/apis/credentials
SSO_GOOGLE_CLIENT_ID=
SSO_GOOGLE_CLIENT_SECRET=
# The domain that users must belong to in order to be able to log in.
SSO_GOOGLE_HOSTED_DOMAIN=your.koel.domain


# Koel can be configured to authenticate users via a reverse proxy.
# To enable this feature, set PROXY_AUTH_ENABLED to true and provide the necessary configuration below.
PROXY_AUTH_ENABLED=false
# The header name that contains the unique identifier for the user
PROXY_AUTH_USER_HEADER=remote-user
# The header name that contains the user's preferred, humanly-readable name
PROXY_AUTH_PREFERRED_NAME_HEADER=remote-preferred-name
# A comma-separated list of allowed proxy IPs or CIDRs, for example, 10.10.1.0/24 or 2001:0db8:/32
# If empty, NO requests will be allowed (which means proxy authentication is disabled).
PROXY_AUTH_ALLOW_LIST=


# Sync logs can be found under storage/logs/. Valid options are:
# all: Log everything (errored-, skipped-, and successfully processed file).
# error: Log errors only. This is the default.
SYNC_LOG_LEVEL=error


# Koel attempts to detect if your website uses HTTPS and generates secure URLs accordingly.
# If this attempt fails for any reason, you can force it by setting this value to true.
FORCE_HTTPS=


# Pusher configuration, for interesting features such as remote controlling.
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=


# The following settings are for Koel to send emails, for example to send user invitations and reset passwords.
MAIL_FROM_ADDRESS=email@domain.com
MAIL_FROM_NAME="${APP_NAME}"
MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null


SQS_PUBLIC_KEY=
SQS_SECRET_KEY=
SQS_QUEUE_PREFIX=
SQS_QUEUE_NAME=
SQS_QUEUE_REGION=


# The variables below are Laravel-specific.
# You can change them if you know what you're doing. Otherwise, just leave them as-is.
BROADCAST_DRIVER=log
CACHE_DRIVER=file
FILESYSTEM_DISK=local
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120

启动后进终端运行。

1
2
3
4
$ php artisan key:generate # 生成 APP_KEY
# 如果需要重新设置数据库地址,运行如下命令
# NOTE: 可选
$ php artisan koel:init # 忽略前端编译错误

忽略前端编译问题。

最后,账号密码。

1
2
3
4
# Username
admin@koel.dev
# Password
KoelIsCool

systemd使用

添加服务

  • 创建可执行服务程序
  • 创建 Unit File
  • 复制 Unit File 至 /etc/systemd/system
  • 服务启 / 停

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# [required] 1. 创建可执行服务程序
## File: test_service.sh
DATE=`date '+%Y-%m-%d %H:%M:%S'`
echo "Example service started at ${DATE}" | systemd-cat -p info

while :
do
echo "Looping...";
sleep 30;
done

# [optional] 2. 复制程序至 /usr/bin
$ sudo cp test_service.sh /usr/bin/test_service.sh
$ sudo chmod +x /usr/bin/test_service.sh

# [required] 3. 创建 Unit File
## File: myservice.service
[Unit]
Description=Example systemd service.

[Service]
Type=simple
ExecStart=/bin/bash /usr/bin/test_service.sh

[Install]
WantedBy=multi-user.target

# [required] 4. 复制 Unit File 至 /etc/systemd/system
$ sudo cp myservice.service /etc/systemd/system/myservice.service
$ sudo chmod 644 /etc/systemd/system/myservice.service

服务启停

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 启动 
$ sudo systemctl start myservice

# 查看状态
$ sudo systemctl status myservice

# 停止
$ sudo systemctl stop myservice

# 重启
$ sudo systemctl restart myservice

# 开机启动
$ sudo systemctl enable myservice

Neovim

配置

使用 LazyVim 进行配置。

1
git clone https://github.com/LazyVim/starter ~/.config/nvim

安装字体(可选)

如果是通过 ssh 远程登录,需要在本地机器安装,并设置 terminal font。

1
2
wget https://github.com/ryanoasis/nerd-fonts/releases/download/v3.2.1/JetBrainsMono.zip
unzip JetBrainsMono.zip -d ~/.local/share/fonts

ubuntu

1
2
sudo apt install fontconfig
fc-cache -fv

概念

Buffer

NeoVim 以 Buffer List 的方式管理打开的文件,每个打开的文件是一个 Buffer,并且有唯一的 id(:ls 可查看打开的文件列表及 id)。

编辑

. 命令

重复上次的修改。

缩进

1
2
3
4
5
>    # 缩进当前行
>n # 缩进接下来的 n+1 行
>G # 缩进到文件末尾

< <n <G # 反向操作

跳转

1
2
3
4
5
gd    # 跳转到上一个编辑的位置,可跨文件
gr # 跳转到使用位置
% # 跳转到成对符号的另一侧, 比如 {}, [], ()
[{ # 跳转到代码块的开始位置
}] # 跳转到代码块的结束位置

移动

1
Ctrl+[,j/k # Ctrl+[ 后快速按 j/k 键可以移动当前行上移/下移一行,注意必须要快;此外 Ctrl+[ 后会进入 command 模式

Leader Key

默认是 \ ,如果使用 LazyVim,会被替换为 <Space> 空格键。

Vim

模式

  • Normal Mode
    • iao 进入 Insert Mode
      • A 在行尾插入
    • : 进入 Command Line Mode
    • vV 进入 Visual Mode;v 按字符选择,V 按行选择
    • Operator-Pending Mode,操作符待决模式,改模式是指 Noraml 模式下输入了操作符但未完成整个操作时的状态
      • 可以按 Esc 中止该状态
  • Insert Mode
  • EscC+[ 进入 Normal Mode
  • Command Line Mode
  • Visual Mode
    • C+v 进入按块选择

命令

操作 = 操作符 + 动作命令

operation = {operator} + {motion}

特殊命令

  • . 重复上次操作
  • ; 重复上次 f 命令的查找
  • , 上次 f 的反方向查找
  • * 查找当前光标所在单词

复合命令

复合命令 等效命令 解释
C c$ 删除从当前位置到行尾的内容,并进入插入模式
s cl 删除当前光标所在字符,并进入插入模式
S ^c 删除当前行,并进入插入模式
I ^i 将光标移动到行首,并进入插入模式
A $a 将光标移动到行尾,并进入插入模式
o A<CR> 在下方新开一行,并进入插入模式
O ko 在上方新开一行,并进入插入模式

操作符命令

命令 用途
c 修改
d 删除
y 复制到寄存器
g~ 反转大小写
gu 转换为小写
gU 转换为大写
> 增加缩进
< 减少缩进
= 自动缩进
! 使用外部程序过滤{motion} 所跨越的行

可重复的操作及回退方式

目的 操作 重复 回退
修改 {edit} . u
行内查找下一个字符 f{char}/t{char} ; ,
行内查找上一个字符 F{char}/T{char} ; ,
文档内查找下一处匹配 /pattern<CR> n N
文档内查找上一处匹配 ?pattern<CR> n N
行内替换 :s/target/replacement & u
执行一系列修改 qx{changes}q @x u

跳转

1
2
3
4
5
b   # 移动光标到单词开头,不包含标点符号
B # 移动光标到单词开头,包含标点符号
e # 移动光标到单词结尾,不包含标点符号
E # 移动光标到单词结尾,包含标点符号
g{e/E} # 移动光标到上一个单词的尾部

Tips

  • 当一个操作符命令被连续调用两次,会作用于当前行,比如 >>yy

快捷键

VIM

1
2
3
4
5
6
7
8
9
10
11
12
13
vib / viB # 选中当前/下一个 () {} 内的内容,b: (), B: {}
cib / ciB # 移除当前/下一个 () {} 内的内容,b: (), B: {}

gf # 打开文件
gx # 打开链接

m{markname} # 创建一个标签
'{markname} # 跳转到标签
m+shift+{markname} # 创建一个标签(跨文件)
'+shift+{markname} # 跳转到标签(跨文件)

J # 合并当前行和下一行,空格分割
gJ # 合并当前行和下一行,无空格分割

LazyVim

1
2
3
<space>-e      # 打开 NeoTree
<ctrl>-o # 上一个编辑的位置
<ctrl>-i # 下一个编辑的位置,<ctrl>-o 的逆操作

Buffer

1
2
3
4
5
6
7
bd            # 删除当前 buffer
bn # 下一个 buffer
bn{number} # 下面的第 {number} 个
bp # 前一个
bp{number} # 前面的第 {number} 个
b# # 切换到最近激活的 buffer
ls # 列出所有 buffer

NeoTree

1
2
3
4
?       # 打开帮助框
a # 添加文件/文件夹,添加文件夹则以 '/' 结尾,否则添加文件
H # 显式/折叠隐藏文件
. # 设置选定的目录为 root dir

使用

多行编辑

1
2
3
4
5
6
7
8
9
10
# 行首添加
<ctrl>-v # 开启矩阵选择
I # 开始编辑
Esc # 退出编辑,编辑对多行生效

# 行位添加
<ctrl>-v
$ # shift + 4
A # shift + a
Esc

代码折叠

1
z{?}

终端

滚动

进入 Normal 模式

在 Neovim 终端界面中,按 Ctrl+\ + Ctrl+N(快速连续按键)切换到 Normal 模式,之后可使用以下操作:

  • j/k:逐行上下滚动
  • Ctrl+u/Ctrl+d:半屏滚动
  • gg/G:跳转到顶部/底部

退出 Normal 模式

  • i

插件

Telescope

Preview 窗口

  • <C-b>/<C-f> 可以滚动 Preview 窗口

常见问题

图标显示异常

修改 terminal font,以 iterm2 为例。字体可从 nerd fonts 下载。

image-20240922102745830

无法复制到系统粘贴板

场景是,登录到远程机器,并使用 nvim 编译,无法复制选中的文本,解决方案是使用快捷键,下面是 Max OS 下的快捷键操作,Windows/Linux 下可尝试探索。

1
2
<option + 拖动鼠标> : 选中连续文本,可跨行
<option + command + 拖动鼠标> : 选中矩形区域,可跨行

选中后,再按系统的复制快捷键即可(或右键弹出菜单、选择复制)。

FZF 搜索窗口无法复制寄存器内容

核心思想是针对 fzf 窗口,自定义快捷键,调用 getreg() 来获取寄存器内容,搜索到使用 getreg(nr2char(getchar())) 命令,但是不行。在 init.lua 文件中添加任意一个方案的内容,在搜索时使用快捷键 Ctrl + V 即可粘贴寄存器内容。

方案一,使用 nvim 的 lua api 命令创建。

1
2
3
4
5
6
7
8
local autogrp = vim.api.nvim_create_augroup("FZF", { clear = true })
vim.api.nvim_create_autocmd("FileType", {
pattern = "fzf",
group = autogrp,
callback = function()
vim.api.nvim_set_keymap("t", "<C-r>", "getreg()", { noremap = true, expr = true, silent = true })
end,
})

方案二,使用 vim.cmd

1
2
3
vim.cmd([[
autocmd! FileType fzf tnoremap <expr> <C-r> getreg()
]])

Lazy.nvim

Mason

  • mason 仅安装 LSP Server,最终还是要使用 lspconfig 插件来完成 LSP

唤出

1
:Mason

image-20241227184840062

image-20241227184849311

修改 Mason 配置后,可手动唤出 Mason 面板安装。

分屏

1
2
<space> -> <shift> + \ : 水平分屏
<space> -> - : 垂直分屏

切换分屏。

1
2
3
4
<ctrl> + H/左 : 左
<ctrl> + L/右 : 右
<ctrl> + J/下 : 下
<ctrl> + K/上 : 上

快捷键冲突检查

1
:checkhealth which-key

拷贝到系统剪贴板

1
:"+y

配置 V 模式下复制快捷键,修改 lua/config/keymaps.lua

1
2
3
4
5
6
-- 可视模式下 <leader>y 复制到系统剪贴板
vim.keymap.set("v", "<leader>y", '"+y', {
noremap = true, -- 非递归映射
silent = true, -- 静默执行
desc = "Copy to system clipboard", -- 可选的描述
})

Iterm2 额外配置

image-20250317171736221

问题排查

1
2
:LspInfo               # 打印 Lsp 信息,用于排查
:setlocal indentexpr? # 查看 indent 信息

日志

1
~/.local/state/nvim

初始化后有报错

1
nvim --headless "+Lazy! sync" +qa

语言

Golang

添加当前函数测试代码

1
2
3
4
5
6
7
// 为当前函数添加测试代码
:GoAddTest

// 运行当前测试函数
:GoTestFunc
:GoTestFunc -v // 显示测试输出日志
// 测试日志弹窗显示, 如果想要再查看, 打开 Notification Hitory (leader-n)

nginx配置

命令

1
2
3
4
5
# 检查配置文件
$ sudo nginx -t

# 重新加载配置
$ sudo nginx -s reload

配置

Centos

centos 下 nginx 配置默认是在 /etc/nginx/nginx.conf/etc/nginx/conf.d/

开启目录访问

1
2
3
4
5
6
7
# 在配置文件的 server 配置的 location 配置中添加 autoindex on
location / {
autoindex on;
autoindex_exact_size off;
autoindex_format html;
autoindex_localtime on;
}

配置服务

upstream

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
upstream [us-name] {
server 127.0.0.1:[port];
}

server {
listen 80;
server_name example.com;
# auth_basic "Auth Message";
# auth_basic_user_file /etc/apache2/.htpasswd;
client_max_body_size 100m;

# rewrite ^/(.*) https://example.com/$1 permanent;

location / {
proxy_pass http://[us-name];
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

# enable websocket
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

认证

Basic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 1. 安装 apache2-utils (Debian, Ubuntu) / httpd-tools (RHEL/CentOS/Oracle Linux)
# 2. 创建认证文件
$ sudo mkdir /etc/apache2
## 创建
$ sudo htpasswd -c /etc/apache2/.htpasswd user
## 添加,移除 -c 参数
$ sudo htpasswd /etc/apache2/.htpasswd another-user

# 3. 配置
## 按路径配置
server {
...
location /api {
auth_basic "Administrator’s Area";
auth_basic_user_file /etc/apache2/.htpasswd;
}
}
## 全局配置
server {
...
auth_basic "Administrator’s Area";
auth_basic_user_file /etc/apache2/.htpasswd;

location /api {
...
}
}

异常

修改 root 后 403

可能原因

  • 没有目录访问权限
  • 目录没有指定的默认文件(比如,index.html),同时又未开启 autoindex

修改 root

1
2
3
4
5
6
7
8
# /etc/nginx/nginx.conf
http {
...
server {
...
root /home/wii/www;
}
}

查看权限

1
2
3
4
5
6
$ namei -om /home/wii/www
f: /home/wii/www
dr-xr-xr-x root root /
drwxr-xr-x root root home
drwx------ wii wii wii
drwxrwxr-x wii wii www

查看 nginx 启动时使用的用户

1
2
3
4
5
6
7
$ ps aux | grep nginx
wii 9364 0.0 0.0 112692 2012 pts/1 R+ 15:54 0:00 grep --color=auto nginx
root 11219 0.0 0.0 39308 956 ? Ss 14:58 0:00 nginx: master process /usr/sbin/nginx
nginx 11220 0.0 0.0 41796 4364 ? S 14:58 0:00 nginx: worker process
nginx 11221 0.0 0.0 41796 3824 ? S 14:58 0:00 nginx: worker process
nginx 11222 0.0 0.0 41796 4168 ? S 14:58 0:00 nginx: worker process
nginx 11223 0.0 0.0 41796 4092 ? S 14:58 0:00 nginx: worker process

可以看到,nginx 使用 nginx 启动,且没有 /home/wii 的访问权限,可以通过 chomd 设置 /home/wii 的权限,或更换其他路径。

root 更换为 /www

1
2
$ sudo mkdir /www
$ sudo chmod -R 755 /www

基于 consul 动态发现

重新编译 nginx

使用 ginx-upsync-module 模块,下载 nginx 源码,然后把模块代码放在 modules 下,用下面命令编译 nginx。

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/sh
./configure --prefix="`pwd`/build" \
--with-pcre \
--with-stream \
--with-http_v2_module \
--with-http_realip_module \
--with-http_gzip_static_module \
--with-http_addition_module \
--with-http_ssl_module \
--with-http_sub_module \
--add-module="`pwd`/modules/nginx-upsync-module" \
--http-log-path=/logs/nginx/access.log \
--error-log-path=/logs/nginx/error.log

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
upstream main {
server 127.0.0.1:8000;
upsync 127.0.0.1:8500/v1/health/service/echo-service upsync_timeout=6m upsync_interval=500ms upsync_type=consul_health strong_dependency=off;
upsync_dump_path /home/ubuntu/app/nginx/upsync_backend/echo-service.conf;
include /home/ubuntu/app/nginx/upsync_backend/echo-service.conf;
}

server {
listen 8080;

location /api/v1/echo {
proxy_pass http://main;
mirror_request_body on;
mirror /mirror;
}
}

流量复制

mirror 有个坑,nginx 以 subrequest 的方式请求 mirror 地址,只有在所有 subrequest 完成之后才会处理下一个请求。如果用来复制流量给开发环境,那么开发环境的响应时间会作用在线上环境的请求上面。

参考这里这里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
upstream main {
server 192.168.0.10:8000;
}

upstream mirror {
server 192.168.0.11:8000;
}

server {
listen 8080;

location /api/v1/echo {
proxy_pass http://main;
proxy_pass_request_body on;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
mirror_request_body on;
mirror /mirror;
}

location /mirror {
proxy_pass http://mirror$request_uri;
proxy_pass_request_body on;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

附录

默认配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
44
45
46
47
48
49
50
51
52
53
54
55
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
82
83
84
85
86
87
88
89
90
91
##
# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# https://www.nginx.com/resources/wiki/start/
# https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
# https://wiki.debian.org/Nginx/DirectoryStructure
#
# In most cases, administrators will remove this file from sites-enabled/ and
# leave it as reference inside of sites-available where it will continue to be
# updated by the nginx packaging team.
#
# This file will automatically load configuration files provided by other
# applications, such as Drupal or Wordpress. These applications will be made
# available underneath a path with that package name, such as /drupal8.
#
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
##

# Default server configuration
#
server {
listen 80 default_server;
listen [::]:80 default_server;

# SSL configuration
#
# listen 443 ssl default_server;
# listen [::]:443 ssl default_server;
#
# Note: You should disable gzip for SSL traffic.
# See: https://bugs.debian.org/773332
#
# Read up on ssl_ciphers to ensure a secure configuration.
# See: https://bugs.debian.org/765782
#
# Self signed certs generated by the ssl-cert package
# Don't use them in a production server!
#
# include snippets/snakeoil.conf;

root /var/www/html;

# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;

server_name _;

location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}

# pass PHP scripts to FastCGI server
#
#location ~ \.php$ {
# include snippets/fastcgi-php.conf;
#
# # With php-fpm (or other unix sockets):
# fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
# # With php-cgi (or other tcp sockets):
# fastcgi_pass 127.0.0.1:9000;
#}

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}


# Virtual Host configuration for example.com
#
# You can move that to a different file under sites-available/ and symlink that
# to sites-enabled/ to enable it.
#
#server {
# listen 80;
# listen [::]:80;
#
# server_name example.com;
#
# root /var/www/example.com;
# index index.html;
#
# location / {
# try_files $uri $uri/ =404;
# }
#}

大数据相关概念

处理

  • 数据清理
  • 数据集成
    • 将不同来源于格式的数据逻辑上或物理上进行集成的过程

分析

  • 联邦分析
    • 一种数据科学方法实践,用于分析存储在用户本地设备中的原始数据,本地计算然后汇总

查询

  • 联邦查询
    • 对多个不同数据源进行检索进行查询

概念

  • 数据孤岛
    • 物理性
      • 数据在不同部门相互独立存储,独立维护,彼此间相互孤立
    • 逻辑性
      • 不同部门站在自己角度定义数据,使得数据被赋予不同含义,加大了跨部门数据合作的沟通成本
  • 数据仓库,Data Warehouse,DWH
    • 是一个面向主题的、集成的、相对稳定的、反映历史变化的数据集合,用于支持管理决策和信息的全局共享,主要处理历史的、结构化的数据
  • 数据集市
    • 数据仓库的特殊形式,正如数据仓库,数据集市也包含对操作数据的快照,便于用户基于历史趋势与经验进行战略决策。两者关键的区别在于数据集市的创建是在有具体的、预先定义好了的对被选数据分组并配置的需求基础之上的。配置数据集市强调对相关信息的易连接性
    • 通俗讲,数据是专门针对特定用户/团队处理后的,以提高数据易用性
  • 数据湖
    • 数据湖是一个存储企业各种各样原始数据的大型仓库,其中的数据可供存取、处理、分析和传输
    • 可以包括结构化数据(关系数据库数据)、半结构化数据(json,xml等)、非结构化数据(电子邮件,文档)、二进制数据(音视频等)
  • 联机分析处理,Online Analytical Processing,OLAP
  • 联机事务处理,Online Transaction Processing,OLTP
  • 数据库管理系统,Database Management System,DBMS

文章

参考

云原生

定义

云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中,构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式API。

这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段,云原生技术使工程师能够轻松地对系统作出频繁和可预测的重大变更。

技术

不可变基础设施

在传统可变服务器基础架构中,服务器在创建之后会不断更新和修改,包括软件环境、配置等,在这种架构中存在配置漂移、雪花服务器等问题。配置漂移 是指对操作系统的更改不被记录,独特和可变的配置,无法/很难在其他机器复现。雪花服务器 存在配置漂移问题的服务器,很难被复制。凤凰服务器 对配置进行版本管理,减少或解决配置漂移问题。

在不可变基础架构/设施中,对配置进行版本控制,服务器一但部署,不会再手动更改。如需更新配置、软件环境,通过部署版本化的配置解决,或通过部署打包的公共镜像。这种方式,有更高的一致性、可靠性及可预测的部署过程。

参考

c++ - debug, asan

AddressSanitizer (ASan) C/C++ 程序的内存错误检测器,可以检查如下错误。

TL;TR

1
2
3
4
5
# CMAKE_CXX_FLAGS
-g3 -fno-omit-frame-pointer -fno-common -fsanitize=undefined -fsanitize=address -fsanitize-recover=address

# 环境变量
export ASAN_OPTIONS=symbolize=true:halt_on_error=false:abort_on_error=false:disable_coredump=false:unmap_shadow_on_exit=true:disable_core=false:sleep_before_dying=15:fast_unwind_on_fatal=1:log_path=asan.log

指定 Asan Library 路径

1
export LD_LIBRARY_PATH=/path/to/libasan.so.x

注意

  • 部分 IDE 集成分析工具,ASAN_OPTIONS 可能被覆盖,比如 CLion,需要在设置中设置 ASAN_OPTIONS(AddressSanitizer)。

image-20241014111225039

使用

编译选项

1
2
3
4
5
6
-fsanitize=address						# 开启内存越界检测
-fsanitize-recover=address # 内存出错后继续运行, 需配合运行选项 halt_on_error=0
-fno-stack-protector # 去使能栈溢出保护
-fno-omit-frame-pointer # 去使能栈溢出保护
-fno-var-tracking # 默认选项为-fvar-tracking,会导致运行非常慢
-g1 # 表示最小调试信息,通常debug版本用-g即-g2

示例

1
2
ASAN_CFLAGS += -fsanitize=address -fsanitize-recover=address
ASAN_CFLAGS += -fno-stack-protector -fno-omit-frame-pointer -fno-var-tracking -g1

链接选项

1
ASAN_LDFLAGS += -fsanitize=address -g1 # 如果使用gcc链接,此处可忽略

运行选项

ASAN_OPTIONS是Address-Sanitizier的运行选项环境变量。

1
2
3
4
5
6
7
8
9
10
11
halt_on_error=0/1 					# 检测内存错误后继续运行
abort_on_error=0/1 # 遇到错误后调用 abort() 而不是 _exit()
detect_leaks=0/1 # 使能内存泄露检测
malloc_context_size=15 # 内存错误发生时,显示的调用栈层数为15
log_path=asan.log # 内存检查问题日志存放文件路径
suppressions=$SUPP_FILE # 屏蔽打印某些内存错误
symbolize=0/1 # 启用符号化,将错误地址翻译成代码行号
disable_coredump=0/1 # 禁用 core dump
disable_core=0/1 # 禁用 core dump
unmap_shadow_on_exit=1
sleep_before_dying=60

更多

示例

1
2
3
4
5
6
7
# 1
export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer
export ASAN_OPTIONS=halt_on_error=0:use_sigaltstack=0:detect_leaks=1:malloc_context_size=15:log_path=/tmp/asan.log:suppressions=$SUPP_FILE

# 2
export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer
export ASAN_OPTIONS=symbolize=true:halt_on_error=false:abort_on_error=false:disable_coredump=false:unmap_shadow_on_exit=true:disable_core=false:sleep_before_dying=15:log_path=asan_log

cmake 链接

1
2
3
4
#asan 链接sys
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
SET(CMAKE_C_FLAGS_ASAN "-O2 -g -fsanitize=address -fno-omit-frame-pointer" CACHE STRING "Flags used by the C compiler during asan builds." FORCE)
SET(CMAKE_C_FLAGS "-O2 -g -fsanitize=address -fno-omit-frame-pointer -lstdc++ -lasan" CACHE STRING "Flags used by the C compiler during asan builds." FORCE)

Debug

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
==1867==ERROR: AddressSanitizer: heap-use-after-free on address 0x7f69c0e59823 at pc 0x000001f1f50c bp 0x7f69c02624e0 sp 0x7f69c02624d0
...
0x7f69c0e59823 is located 35 bytes inside of 8388608-byte region [0x7f69c0e59800,0x7f69c1659800)
freed by thread T14 here:
...
previously allocated by thread T14 here:
...
SUMMARY: AddressSanitizer: heap-use-after-free /data/zhenkai.sun/ranker/cmake-build-debug/CMakeUnzipPackages/mongo-c-driver-1.19.1/src/libbson/src/bson/bson.c:1993 in bson_init_static
Shadow bytes around the buggy address:
0x0fedb81c32b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0fedb81c32c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0fedb81c32d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0fedb81c32e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0fedb81c32f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0fedb81c3300: fd fd fd fd[fd]fd fd fd fd fd fd fd fd fd fd fd
0x0fedb81c3310: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0fedb81c3320: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0fedb81c3330: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0fedb81c3340: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0fedb81c3350: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==1867==ABORTING

c++ 手册

定义变量

基础操作

字符串

c++

1
2
// 查找
size_type idx = str.find(s); // 未查到: (idx == std::string::npos)

容器

顺序容器

  • [x] 可变数组 vector
  • [x] 双向链表 list
  • [ ] 单向链表 forward_list
  • [ ] 双端队列 deque
  • [ ] 固定数组 array

关联容器

  • [ ] 集合 set、multiset
  • [ ] 映射 map、multimap

容器适配器

  • [ ] 栈 stack
  • [ ] 队列 queue
  • [ ] 优先队列 priority_queue

可变容量数组 vector

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <vector>
// 初始化
std::vector<T> vec;
std::vector<T> vec(5, 0); // 长度 5, 元素值
std::vector<T> vec = {1, 2, 3, 4, 5};
std::vector<T> vec(arr, arr + sizeof(arr) / sizeof(arr[0]));
std::vector<T> vec(container.begin(), container.end());
// 插入
vec.push_back(element);
vec.emplace_back(element);
vec.insert(iterator, element);
// 访问元素
vec.at(index);
vec[index];
vec.front();
vec.back();
// 删除元素
vec.pop_back();
vec.erase(iterator);
vec.erase(start_iterator, end_iterator);
// 大小和容量
vec.resize();

双向链表 list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <list>
// 初始化
std::list<T> lst;
std::list<T> lst = {1, 2, 3, 4, 5};
std::list<T> lst(container.begin(), container.end());
// 插入
lst.push_back(element);
lst.push_front(element);
lst.insert(it, element);
lst.insert(it, n, element); // 在 it 插入 n 个 element
lst.insert(it, begin, end); // 在 it 插入迭代器 [begin, end) 内元素
// 赋值 (清空元素并重置为指定元素)
assign(begin, end);
assign(n, element);
// 访问
l.begin(); // 返回迭代器
l.end(); // 返回迭代器
l.front(); // 返回元素
l.back(); // 返回元素
// 删除
erase(begin, end);
erase(it);
remove(element);
clear();
pop_back();
pop_front();

单向链表 forward_list

1
2
3
4
5
6
7
8
9
10
11
12
#include <forward_list>
// 初始化
std::forward_list<int> fl;
std::forward_list<int> fl {1, 2, 3};
std::forward_list<int> fl(n, element);
std::forward_list<int> fl(begin, end);
// 插入
fl.push_front(element);
fl.insert_after(iter, element);
// 赋值 (清空元素并重置为指定元素)
// 访问
// 删除

unordered_set

1
2
3
4
5
6
7
8
9
// 修改
insert(Key&&)
emplace(Args&&)
erase(const Key&)
merge()
// 查找
iterator find(Key)
size_type count(Key)
bool contains(Key)

基础知识

虚函数

  • 运行时动态绑定
  • 基类使用虚函数表(vtable)存储每个虚函数的地址

c++基础

[toc]

组织

文件

文件

文件名后缀 说明
.c c 源文件
.h c 同文件
.cpp c++ 源文件
.hpp c++ 头文件
.hh c++ 头文件
.hxx c++ 头文件
.h++ c++ 头文件

原则

  • 通常的,.h.c 对应 c 代码,.hpp.cpp 对应 c++ 代码
    • 除此之外的后缀,多是项目规范,保持一惯性即可
  • 保持 .h 文件是 c 兼容的代码(不包含 c++ 代码)
  • 如果想编写 c 和 c++ 的混合代码
    • 可以在 .hpp 中使用 extern "C" 来实现
  • 不要使用 .H.C 后缀,因为部分文件系统不区分大小写,比如 windows,macos 在格式化分区时也有不区分大小写的选项

输入输出

标准输入输出包含:

  • cin
  • cout
  • cerr
  • clog
    • 带缓冲区,常用于写日志数据
1
2
3
4
5
6
// 持续输入
while (cin >> num)
sum += num;

// 读取到类对象
cin >> Item;

基础

变量

初始化

C++ 支持两种初始化方式,复制初始化和直接初始化。

1
2
int val(1024);   // 直接初始化;效率更高
int val = 1024; // 复制初始化

对于内置类型来说,复制初始化和直接初始化几乎没有差别。函数体外定义的内置变量都初始化成0,函数体内定义的内置变量不进行自动初始化。变量在使用前必须被定义,且只允许被定义一次。

声明

为了让多个文件可以访问相同的变量,C++ 区分了声明和定义。声明用于向程序表明变量存在,及其类型和名字;定义用于为变量分配空间,还可对变量进行初始化。

1
2
3
4
extern int i;	 // 声明变量
int i; // 定义变量

extern int i = 1; // 有初始化的声明,可视为定义

数据类型

类型 含义 最小存储空间
bool 布尔型
char 字符 8位
wchar_t 宽字符型 16位
short 短整型 16位
int 整型 16位
long 长整型 32位
float 单精度浮点数 6位有效数字
double 双精度浮点型 10位有效数字
long double 扩展精度浮点型 10位有效数字
1
wchar_t wc = L'a'; // 字符前面加 L 表示宽字符 

类型

数字

字符串

初始化

1
string s(10, '0');

子串

1
2
str.substr(3, 5);    // [3, 8)
str.substr(5); // [5, ~)

布尔

限定

static

1
2
3
4
5
6
7
class A {
public:
static A Instance() { // 实现单例
static A _a; // static 修饰,只会创建一次 A 对象
return _a;
}
};

常量

定义

数据结构

数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 创建
int i[5];
int j[2][2];

// 初始化
int i[] = {1, 2, 3};
int j[][1] = {{1}, {2}, {3}};

// 二维数组初始化
int k[2][2] = {{1, 2}, {3}};
1 2
3 0

// 静态数组
int i[5] = {0}; // 创建静态数组, 并初始化, 没设置的元素被重置为 0
std::cout << sizeof(i) << std::endl; // 20, 5 * 4. 5 个元素, 每个占用 4 字节空间
int b[3][3] = {0}; // 多维数组

// 动态数组
int size = 3;
int *c = new int[size]; // 创建动态数组
std::cout << sizeof(c) << " - " << sizeof(*c) << std::endl; // 8 - 4. 8: 64位机器, 指针大小, 4: int 元素大小
memset(c, 0, size * sizeof(*c)); // 必须要乘 size 才能算出总的占用内存
delete[] c;

列表

1
2
3
// 初始化
vector<int> iv{1, 2, 3} // [1, 2, 3]
vector<int> iv(3, 1); // [1, 1, 1]

集合

映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 初始化
std::map<int, int> m = {
{1, 2},
{2, 3}
};

std::map<std::string, std::vector<int> > mapOfOccur = {
{ "Riti", { 3, 4, 5, 6 } },
{ "Jack", { 1, 2, 3, 5 } }
};

// 遍历
std::map<int, int> m = ...;
for (auto &entry : m) {
std::cout << entry.first << " -> " << entry.second << std::endl;
}

// 插入
map<int, string> mp;
mp.insert(pair<int,string>(1,"aaaaa"));
mp.insert(make_pair<int,string>(2,"bbbbb"));
mp.insert(map<int, string>::value_type(3,"ccccc"));
mp[4] = "ddddd";

std::map<char,int> mp;
mp.emplace('x',100);

// 查找
std::map<char,int>::iterator it = mp.find('x');
if (it != mp.end())
// exists
else
// not exists

// 删除
std::map<char,int>::iterator it = mp.find('x');
if (it != mp.end())
mp.erase(it);

语法

程序结构

注释

运算符

条件控制

循环

判断

函数

特殊成员函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 默认构造函数 (Default constructor)
classname ()
// 非默认函数
explicit classname(type param)
// 拷贝构造函数 (Copy constructor)
classname(const classname &other)
// 赋值构造 (Copy assignment operator)
classname& operator=(const classname &other)
// move 构造 (Move constructor)
classname(classname &&other)
// 赋值 move 构造 (Move assignment operator)
classname& operator=(classname &&other)

// 析构函数 Destructor
~classname()
Function syntax for class MyClass
Default constructor MyClass();
Copy constructor MyClass(const MyClass& other);
Move constructor MyClass(MyClass&& other) noexcept;
Copy assignment operator MyClass& operator=(const MyClass& other);
Move assignment operator MyClass& operator=(MyClass&& other) noexcept;
Destructor ~MyClass();

特性

语法糖

default

default 关键词为类的特殊默认无参函数(构造、析构、拷贝构造、拷贝赋值)提供默认行为。

1
2
3
4
5
6
7
8
9
10
11
class A
{
public:
A() = default;
A(const A&);
A& operator = (const A&);
~A() = default;
};

A::A(const X&) = default; // 拷贝构造函数
A& A::operator= (const A&) = default; // 拷贝赋值操作符

delete

default ,屏蔽默认行为。

1
2
3
4
5
6
7
class A
{
A& operator=(const A&) = delete; // assignment operator disabled
};

A a, b;
a = b; // ERROR: 拷贝赋值操作被禁用

thread_local

lambda

lambda 表达式格式如下。

1
[函数对象参数] (操作符重载函数参数) mutable 或 exception 声明 -> 返回值类型 {函数体}

函数对象参数

参数范围 参数传递方式 备注
没有函数对象参数 - -
= 表达式所有可访问局部变量(包括所在类的 this 对象) 值传递 -
& 表达式所有可访问局部变量(包括所在类的 this 对象) 引用传递 -
this 函数体内可以使用 Lambda 所在类中的成员变量 TBD -
a a 值传递 变量默认为 const,如果需要修改需为函数体添加 mutable 修饰符
&a a 引用传递
a,&b a,b a 为值传递,b 为引用传递 -
=,&a,&b 表达式所有可访问局部变量(包括所在类的 this 对象) a、b 引用传递,其他参数是值传递 -
&,a,b 表达式所有可访问局部变量(包括所在类的 this 对象) a、b 值传递,其他参数是引用传递 -

空指针处理

函数式编程

泛型编程

1
2
3
4
5
6
7
8
9
10
// 从标准输入读取T类型数据
template <typename T>
T r() {
T t;
cin >> t;
return t;
}

// 使用
int x = r<int>();
1
2
3
4
5
6
7
8
9
10
11
12
// 值交换
template <typename T>
void swapT(T& a, T& b) {
a ^= b;
b ^= a;
a ^= b;
}

// 使用
vector<int> iv{1, 2, 3};
swapT(iv[0], iv[2]);
// 1 2 3 -> 3 2 1

不限于类型。

1
2
3
4
5
6
7
8
9
template<unsigned N>
void f() {
std::cout << N << std::endl;
}

int main() {
f<10>();
return 0;
}

类型推演

重用方法

1
2
3
std::is_same<TA, TB>
typeid()
std::is_same_v<TA, TB> #include <variant>

类型读取及定义

1
2
template<auto object, class T=std::decay_t<decltype(*object)>>
int Function();

类型判断

1
2
3
4
5
6
7
8
9
#include <concepts>

template<typename Type>
concept CharTypes = std::is_same<Type, char>::value ||
std::is_same<Type, wchar_t>::value || std::is_same<Type, char8_t>::value ||
std::is_same<Type, char16_t>::value || std::is_same<Type, char32_t>::value;

template<CharTypes T>
class Some{};
1
2
3
4
5
6
7
8
#include <variant>

template<class T>
void f(T t) {
if (std::is_same_v<T, std::string>) {
// DO SOMETHING
}
}

宏定义 define

##

连接形参,忽略前后空白符。

1
2
3
4
5
6
#define Concat(a, b) a##b

int ab = 1, ax = 2, xa = 3;
std::cout << Concat(a, b) << std::endl; // output: 1
std::cout << AppendX(a) << std::endl; // output: 2
std::cout << XAppend(a) << std::endl; // output: 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// not ok
#define select(m, key) m##[#key]
// ok
#define select(m, key) (m)[#key]

//--- .
// not ok
#define select(m, key) m##.##key
#define select(m, key) m.##key
// ok
#define select(m, key) m.key


// 使用
std::map<std::string, std::string> m;
m["a"] = "0";
auto v = select(m, a);

// 在使用宏变量时,外加小括号, 比如 #define add(a, b) (a) + (b)

#@

字符化形参。

#

字符串化形参。

1
2
3
4
5
6
#define ToString(a) #a
std::cout << ToString(abc) << std::endl; // abc

// 拼接
#define ToSV(member) #member##sv
ToSV(time) // 等价于 "time"sv

Parameter pack

Parameter pack

A template parameter pack is a template parameter that accepts zero or more template arguments (non-types, types, or templates). A function parameter pack is a function parameter that accepts zero or more function arguments.

A template with at least one parameter pack is called a variadic template.

包含至少一个参数包的模板称为可变模板。

右值引用&&

C++ 11 引入右值引用主要是为了解决以下几个问题:

  1. 优化复制大对象的性能问题。

在传递一个对象时,如果使用常规的左值引用,就需要进行拷贝构造函数的调用,这会导致复制大对象的时候开销很大。而右值引用可以避免这种情况的发生。因为右值引用本身不会进行对象的拷贝操作,只是将对象所在的内存地址绑定到右值引用上,从而提高代码执行效率。

  1. 实现移动语义,支持转移资源所有权。

在C++11中,新增了std::move函数,可以将一个对象的资源所有权转移到另一个对象中,这就是移动语义。通过将对象的内部数据指针从源对象转移到目标对象,可以避免创建和销毁临时对象,从而提高代码执行效率。而实现移动语义,需要使用右值引用的特性。

总之,右值引用的引入,旨在提高C++代码的性能和效率,支持更加高效的对象传递和资源管理方式,并且为C++编程带来更多的灵活性和扩展性。

其他优化场景。

  • 函数中返回一个临时变量时,编译器会自动调用移动构造函数,并将临时变量的资源所有权移动到函数的返回值中,从而避免进行数据拷贝
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 示例 1
std::string GetString() {
std::string t = "abc";
std::string res = t + "def";
std::cout << "A " << static_cast<void *>(res.data()) << std::endl;
return res; // 函数返回临时变量, 未进行对象拷贝
}

int main() {
auto r = GetString(); // 右值引用赋值给左值变量, 不进行对象拷贝
std::cout << "B " << static_cast<void *>(r.data()) << std::endl;
std::cout << "---" << std::endl;
auto r2 = std::move(GetString());
std::cout << "B " << static_cast<void *>(r2.data()) << std::endl;
return 0;
} /* output
A 0x16eeeb100
B 0x16eeeb100
---
A 0x16eeeb0c0
B 0x16eeeb0d8
*/

注意

  • 对右值调用 std::move 没有作用

其他

常量

1
2
3
// MAX / MIN
INT_MAX
INT_MIN

进阶

C++ 程序质量保障

  • 代码覆盖率(code coverage)
  • 内存检查
    • asan
    • valgrind
  • CPU Profiler

malloc

常用的 malloc 库,及实践。

  • jemalloc
  • tcmalloc
    • 性能要好于 jemalloc
  • mimalloc
    • 偶尔会 core

RAII

RAII(Resource Acquisition Is Initialization,资源获取即初始化)使用局部变量来管理资源,是 C++ 中常用的资源管理方式。

时间

  • time unit
    • std::chrono::microseconds
    • std::chrono::milliseconds
    • std::chrono::second
    • std::chrono::minutes
    • std::chrono::hours
    • std::chrono::days
    • std::chrono::months
    • std::chrono::years
  • clock
    • steady_clock 单调递增时钟
    • system_clock 系统时间时钟
    • high_resolution_clock 高精度时钟
  • time_point
  • duration
  • duration_cast
1
std::chrono::time_point<Clock,Duration>::time_point

sample

1
2
3
4
5
using namespace std::chrono_literals; // 8h, 24m, 15s
// 当前时间
std::chrono::system_clock::now(); // return time_point
// duration
std::chrono::duration(10s); // 10 秒