Nicksxs's Blog

What hurts more, the pain of hard work or the pain of regret?

我跟LD一直在用的是比较古早iPhone7 plus,我是之前有另外买个安卓机作为主力,主要是觉得安卓经常可以换新,苹果用久了特别是存储空间比较贵,占满空间以后也会比较慢,LD一直说没啥用就一个手机用到现在,算起来应该有接近9年左右了,前阵子终于被我说动了买了个iPhone17,主要也是觉得这一代标准版配置很均衡,处理器是正代的,存储也有256了,高刷屏也有。
就是这个换机还是比安卓换机麻烦一些,首先就是想iTunes做个整机备份
这里是第一个比较麻烦的点,默认的iTunes备份是不支持自己设置备份文件的存储路径的,对于windows来说,特别是家里的老win7,可能版本比较早
并且也没有提前判断下C盘的剩余空间,对于C盘只有128G的,刚好手机也是128G且已经用的差不多了的情况,是比较尴尬的,因为它默认也是存在C盘里
这里主要两个方法,一个是提前判断好手机的存储大小,清理好C盘空间,不过有一点倒是iTunes备份比如因为盘满了,它下次不会从头再备份,而且会继续上一次的,所以比较满了再清理之后,继续备份还是可以延续上一次写入的文件继续写的,不用删除上一次失败的
另一个就是做个软连接,用管理员权限打开cmd命令行

1
mklink/j "C:\Users\你的用户名\AppData\Roaming\Apple Computer\MobileSync" "D:\iTunesBackup"

这里mklink前面的一个参数是默认的备份存储路径,后者是要修改的路径,这里的意思是前面的路径是苹果会去写入,做了软链接后前面的就是个快捷方式,后面的才是实际的存储位置,但是需要自己去用户目录下看下这个文件夹是否存在,已经存在的话要把原来的重命名成其他的或者删除(确认没用的话)
另外这是比较老的iTunes跟windows版本,如果是Win10,并且是在微软商店里下载的话可能路径是其他的
类似于
C:\Users\你的用户名\AppleMobileSync这样
这样就能改到我们存储空间比较大的盘,这是第一块,里面还有个小插曲,或者说也是蛮重要的,因为我其实不想把iTunes备份好的整个恢复到新iPhone上,它并没有类似于很多安卓手机会给你选择,比如只要恢复相册联系人这些,相当于会把整个iPhone再拷贝到新的上,这样有些比如很久不用的app,或者一些app的缓存啥的也会带过去;只是做一个以防万一,但是这里还有个问题是,iPhone通过iTunes备份,貌似如果选择不加密备份,是不会把整个iPhone都备份,而是把非安全相关的做个备份,比如一些浏览器存储的密码这些都不会,所以这里也是个注意点
第二部分是iCloud的使用,LD的习惯比较好,一些截图或者临时拍的照片都会即时删掉,所以照片占用的空间也比较少,正好iCloud也够用,所以在前面iTunes备份比较慢,经过盘空间失败,还有个比较头大的就是在iPhone通过无线网做迁移的时候,也是无法选择想要同步迁移的内容,这样也导致了时间非常久,从一开始的2小时变成导了一下午反而变成了11小时这种比较离谱的情况,所以最后选择了相册这些通过iCloud同步,其他的app反正本来就打算重新装,微信有自己的迁移同步工具;这里有个注意点iCloud会在手机存储空间不足的情况下将本地的照片换成像素比较低的占用空间比较小的照片
结果后一种方式整个都非常快,iCloud的下载比如相册也会是先下载缩略图,看起来就是一下就出来了照片,另外就是像短信跟联系人,这个都需要自己在新iPhone的iCloud设置里开启同步到此iPhone才会同步过来

最近在研究headscale的多局域网互通,但是因为设备和网络知识不过关,所以还没完全走通,不过对于mac设备的路由和linux的路由这些知识稍微多了一点
在mac上可以通过

1
netstat -nr

查看路由表
命令参数的解析是

1
2
3
4
5
 -n    Show network addresses as numbers (normally netstat interprets addresses and attempts to display them symbolically).  This option may be used with any
of the display formats.
-r Show the routing tables. Use with -a to show protocol-cloned routes. When -s is also present, show routing statistics instead. When -l is also
present, netstat assumes more columns are there and the maximum transmission unit. More detailed information about the route metrics are displayed with
-ll for TCP round trip times -lll for all metrics. Use the -z flags to display only entries with non-zero RTT values. (“mtu”) are also displayed.

比如我的路由表里

1
2
3
4
5
Routing tables

Internet:
Destination Gateway Flags Netif Expire
default 192.168.xx.1 UGScg en0

这个表示是默认路由,走的网关转发,
这里的Gateway就是网关地址,一般是我们的路由器地址
Flags的意思是
U表示Up,表示路由是可用的,
G表示是Gateway,表示通过网关转发
S表示是Static,是静态路由,非动态生成,
c表示是Cloned,由父路由自动克隆生成
g表示是Global,全局的
比较全的

1
2
3
4
5
6
7
8
U = Up
G = Gateway
S = Static, e.g., default route added at boot time
H = Host-specific
C = Generate new (host-specific) routes on use
L = Valid link-layer (MAC) address
c = Cloned route
R = Reject route, known but unreachable route

表示我们的请求都是经过这个网关转发的,通过它把数据往外网或者局域网内其他设备发
至于这个Netif表示 network interface
就是网络接口也可以理解为网卡
比如我这里的en0,一般指物理网卡(通常是 Wi-Fi)
另外还可以通过

1
route get default

查看默认路由设置
可以这么理解网络连接过程

1
2
3
4
5
6
7
8
9
应用程序

路由表

netif(网络接口)

物理网卡 / 虚拟隧道

网络

原来的设置就是比如我可以在网关设备配置好tailscale节点,
然后让局域网内的设备都通过这个网关设备连接tailscale的网络,
对于非自建headscale应该就是subnet互联这样的逻辑
但是对于headscale中,可能因为我的网关设备是n1刷的openwrt,
对应的tailscale版本比较新,一直出现比较奇怪的问题
比如获取不到ipv4的地址,
启动tailscale就直接把本地局域网的地址全都走tailscale0虚拟网卡路由了
导致ipv4地址直接都不通了,
还得找下原因,可能是tailscale的客户端版本太高了,
一开始连接不上headscale的http地址,
高版本必须得用https才行,
结果就是用caddy套了一层https的壳
能连接上了又发现ipv4地址没了
而且还会出现一些很奇怪的问题
比如会出现局域网内的wifi也断连
是否是出现了局域网内的二层广播风暴或者arp冲突
应该是二层环路问题比较大
这一块还有待后续的研究

我们在使用mysql的索引的时候一般会使用explain来查看执行计划,用来分析索引使用情况等
但是经常我们也会质疑,为啥没有用预期的索引,反而使用了另一个或者甚至没使用
这样我们就可以开启optimizer trace来看看具体优化器是怎么处理的

1
2
3
4
SET optimizer_trace='enabled=on';
SET optimizer_trace_limit=10;
set optimizer_trace_offset=-10;
set end_markers_in_json=on;

我们可以用这几个命令来开启
比如我们执行一个最简单的 do 1+1;
通过查询

1
SELECT query,trace FROM information_schema.OPTIMIZER_TRACE;

就可以看到优化器的trace

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
{
"steps": [
{
"join_preparation": {
"select#": 1,
"steps": [
{
"expanded_query": "/* select#1 */ select (1 + 1) AS `1+1`"
}
] /* steps */
} /* join_preparation */
},
{
"join_optimization": {
"select#": 1,
"steps": [
] /* steps */
} /* join_optimization */
},
{
"join_execution": {
"select#": 1,
"steps": [
] /* steps */
} /* join_execution */
}
] /* steps */
}

我们可以再查一个真实点的
比如

1
SELECT * from students WHERE class = 10;

trace 就长这样

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
{
"steps": [
{
"join_preparation": {
"select#": 1,
"steps": [
{
"expanded_query": "/* select#1 */ select `students`.`id` AS `id`,`students`.`name` AS `name`,`students`.`age` AS `age`,`students`.`class` AS `class`,`students`.`created_at` AS `created_at`,`students`.`updated_at` AS `updated_at` from `students` where (`students`.`class` = 10)"
}
] /* steps */
} /* join_preparation */
},
{
"join_optimization": {
"select#": 1,
"steps": [
{
"condition_processing": {
"condition": "WHERE",
"original_condition": "(`students`.`class` = 10)",
"steps": [
{
"transformation": "equality_propagation",
"resulting_condition": "multiple equal(10, `students`.`class`)"
},
{
"transformation": "constant_propagation",
"resulting_condition": "multiple equal(10, `students`.`class`)"
},
{
"transformation": "trivial_condition_removal",
"resulting_condition": "multiple equal(10, `students`.`class`)"
}
] /* steps */
} /* condition_processing */
},
{
"substitute_generated_columns": {
} /* substitute_generated_columns */
},
{
"table_dependencies": [
{
"table": "`students`",
"row_may_be_null": false,
"map_bit": 0,
"depends_on_map_bits": [
] /* depends_on_map_bits */
}
] /* table_dependencies */
},
{
"ref_optimizer_key_uses": [
{
"table": "`students`",
"field": "class",
"equals": "10",
"null_rejecting": false
}
] /* ref_optimizer_key_uses */
},
{
"rows_estimation": [
{
"table": "`students`",
"range_analysis": {
"table_scan": {
"rows": 107,
"cost": 24.5
} /* table_scan */,
"potential_range_indexes": [
{
"index": "PRIMARY",
"usable": false,
"cause": "not_applicable"
},
{
"index": "idx_class",
"usable": true,
"key_parts": [
"class",
"id"
] /* key_parts */
}
] /* potential_range_indexes */,
"setup_range_conditions": [
] /* setup_range_conditions */,
"group_index_range": {
"chosen": false,
"cause": "not_group_by_or_distinct"
} /* group_index_range */,
"analyzing_range_alternatives": {
"range_scan_alternatives": [
{
"index": "idx_class",
"ranges": [
"10 <= class <= 10"
] /* ranges */,
"index_dives_for_eq_ranges": true,
"rowid_ordered": true,
"using_mrr": false,
"index_only": false,
"rows": 5,
"cost": 7.01,
"chosen": true
}
] /* range_scan_alternatives */,
"analyzing_roworder_intersect": {
"usable": false,
"cause": "too_few_roworder_scans"
} /* analyzing_roworder_intersect */
} /* analyzing_range_alternatives */,
"chosen_range_access_summary": {
"range_access_plan": {
"type": "range_scan",
"index": "idx_class",
"rows": 5,
"ranges": [
"10 <= class <= 10"
] /* ranges */
} /* range_access_plan */,
"rows_for_plan": 5,
"cost_for_plan": 7.01,
"chosen": true
} /* chosen_range_access_summary */
} /* range_analysis */
}
] /* rows_estimation */
},
{
"considered_execution_plans": [
{
"plan_prefix": [
] /* plan_prefix */,
"table": "`students`",
"best_access_path": {
"considered_access_paths": [
{
"access_type": "ref",
"index": "idx_class",
"rows": 5,
"cost": 4,
"chosen": true
},
{
"access_type": "range",
"range_details": {
"used_index": "idx_class"
} /* range_details */,
"chosen": false,
"cause": "heuristic_index_cheaper"
}
] /* considered_access_paths */
} /* best_access_path */,
"condition_filtering_pct": 100,
"rows_for_plan": 5,
"cost_for_plan": 4,
"chosen": true
}
] /* considered_execution_plans */
},
{
"attaching_conditions_to_tables": {
"original_condition": "(`students`.`class` = 10)",
"attached_conditions_computation": [
] /* attached_conditions_computation */,
"attached_conditions_summary": [
{
"table": "`students`",
"attached": null
}
] /* attached_conditions_summary */
} /* attaching_conditions_to_tables */
},
{
"refine_plan": [
{
"table": "`students`"
}
] /* refine_plan */
}
] /* steps */
} /* join_optimization */
},
{
"join_execution": {
"select#": 1,
"steps": [
] /* steps */
} /* join_execution */
}
] /* steps */
}

比如这里的 potential_range_indexes 提示primary主键就是不适用的,当然具体分析还需要更深入的学习。

最近也看到了一篇文章,结合一些实际的经验来看下索引的基数和可选择性,
这个基数指的是啥呢,就是索引我们一直在讲的是要字段值的差异度比较大的那种,因为假如这个字段的所有值都是比如0和1的话,那索引的结构BTree就没办法高效的找到所查询的值,这个基数就是可以作为它差异度大小的一个参考,当然这是一种反过来的说法,理论上应该从写入这个表的数据的逻辑去看,这些字段会出现哪些值以及它的取值范围是怎么样的
那么这个会在mysql中存起来的基数值可以怎么看呢
还是用我之前建的一个简单的表

1
2
3
4
5
6
7
8
9
10
CREATE TABLE `students` (
`id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(64) NOT NULL COMMENT '姓名',
`age` int(11) NOT NULL COMMENT '年纪',
`class` int(11) NOT NULL COMMENT '班级',
`created_at` datetime NOT NULL COMMENT '创建时间',
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_class` (`class`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=608 DEFAULT CHARSET=utf8mb4 COMMENT='学生表';

表里的数据也比较简单

因为升成name和class的时候我都用了一定的规律取余,所以会有一些重复的,
这是我们就可以在information库的查一下这个基数

1
select table_schema,table_name,index_name,cardinality from information_schema.statistics where table_schema='database' and table_name = 'students' order by cardinality desc

database是你的库,可以来这么查询

我的这个表差不多是这样,主键刚好对应的就是数据量,另一个班级的索引就是这个class的数量是一致的
所以这就是个明确的区分度的值,而之前也提到过,如果一个表的某个索引字段他的区分度低,跟全表扫描差不多的话
优化器当然也可能会选择不使用索引,直接使用全表扫描,这样就不用质疑说为啥没用起来索引等等

我们日常在使用github的时候经常会碰到访问比较慢的问题,一方面是github的打开慢,还有就是相关的数据传输很慢,比如从github上clone代码,有时候仓库比较大,经常是clone到一半就卡住了,或者速度几乎跌0了,还有比如是下载一些软件的软件包,特别是这个让人特别眼熟的地址raw.githubusercontent.com,经常是龟速下载,时间一长还经常断了就没法继续下载了
最近看到了这个项目,https://github.com/521xueweihan/GitHub520
我们可以把项目的内容复制下来,追加写入到我们的hosts文件里,但是这种方式不够一劳永逸,而且如果出现了访问缓慢,又去这个仓库下载的话,就变成先有鸡还是先有蛋的问题了,
所以刚好有switchhosts这款软件,它可以新建一个远程模式的hosts配置文件

同时可以设置更新频率,仓库维护着推荐是1小时更新,我觉得其实1天问题也不大,这样也能减轻维护者服务器的压力
还有就是通过crontab任务,比如

1
sudo sed -i "" "/# GitHub520 Host Start/,/# Github520 Host End/d" /etc/hosts && curl https://raw.hellogithub.com/hosts | sudo tee -a /etc/hosts

但是相对来说不太推荐,crontab维护比较原始,出现问题也不好恢复,switchhosts是个不错的选择

0%