所有由罗佳(博主)发布的文章

INSERT ON DUPLICATE KEY UPDATE导致自增字段不连续

今天做一个自用小图库的时候发现保存的tag_id没一会就已经上万了,但是实际的tag数量才只有600个左右,找了一会儿发现是`ON DUPLICATE KEY UPDATE` 导致的。

简单来说就是由于mysql在执行语句之前不知道它会进入insert还是update模式,为了一致性只能先取一个自增id,不然如果进了insert模式就没有id用了,就是这样导致即使该语句实际上进入的是update模式也会导致自增id+1。

网上有找到修改`innodb_autoinc_lock_mode`来解决的方法,但是我改成0了之后依然会导致进入update模式后自增+1,不知道为什么。

还有的说在程序里先select,再决定是insert还是update,但这样并发会导致问题,如果select和后续步骤之间有别的请求插入了满足条件的条目,就会导致错误。

方案1

我暂时用了这样一个笨办法,关掉自增,然后手动设置id为最大值+1,其中`tag_id` 和`name` 都是`unique` 字段

INSERT INTO `tags` ( `tag_id`, `name`, `type` ) 
VALUES (( SELECT id FROM ( SELECT max( tag_id )+ 1 AS id FROM tags ) AS tmp), 'valueA', 'valueB') 
ON DUPLICATE KEY UPDATE `type`=VALUES(`type`)

 

由于insert里不能直接select被insert的表,所以需要在select外面再套一个select,变成两层子查询。

但这样做也有一个很明显的缺陷,因为用的不是数据库自带的自增,于是自然也就没有`LAST_INSERT_ID` ,所以如果需要获取刚刚插入数据的id的话,就得再进行一次查询。

好在可以根据affectedRows判断此语句发生了更新还是插入,如果影响的行数是2的话那就是更新,如果是1就是插入了一条新数据。

注意,这个方法只适用于单个事务依次执行,如果是并发会造成死锁!

方案2

这个问题只在InnoDB会出现,所以如果对并发要求不高的话可以直接改成MyISAM引擎解决问题。

 

如果有什么别的好办法希望可以给我留言,谢谢。

第一次做破解软件

今天是国庆假期最后一天,我本来打算今天装个win11然后补完赛马娘第一季的,结果还没睡醒就被公司的人打电话来叫醒说一个软件显示注册过期了,这软件还特别重要,必须要可用。

关键是前任网管并没有给我交接这个软件安装人员的相关信息,软件里关于界面也没留联系方式,软件说明书文档里写着注册码要问物业或机器销售方要,我就是物业,机器不知道是谁装的,于是我只能远程把软件拉回自己电脑上开始尝试破解。

一个上午没有任何进展,一直被ida稀烂的文字编码拖着后退,字符串列表完全找不到关键词。你说它同样是UTF-16的字符,有的就能显示有的就是乱码,再加上这软件有的中文是GB系编码,有的是UTF-16LE编码,光是找注册相关代码的地址就找了一下午,IDA都换了4个版本。

最后编码问题还是靠vscode解决的,在vscode里直接用UTF-16LE编码打开程序文件,找到里面需要的中文字符串,再新建一个文档保存成UTF-16LE,再用hexeditor插件打开来,再用字节码搜索字符串在程序里的地址,再找引用这个地址的代码段,再改汇编测试。

IDA这东西往程序里patch还不能连续patch,连着改会把程序改坏,坏了好几次我才发现不是我改的汇编有问题,于是又写了个bat每次patch前一键把程序复原,再patch测试。

一直折腾到现在现学现做用了15个小时终于破解好了,写这篇日志的时候已经是第二天1:30了,有计划的一天完全浪费了,头发也没来得及理,番也没看,win11也没装,自己的事啥也没做,淦。

程序无法监听端口,但端口未被占用

之前写过一个情况,是由于程序权限不足而导致无法监听小端口号,但是今天我又遇到一个奇怪的情况。

调试node.js后端的时候需要监听3000端口,前几天都是好的,但是刚才启动的时候报了`bind EACCES` 错误,查看端口并没有被使用,程序在别的电脑上就正常。整来整去还是不行,于是开始在网上找有没有类似的情况,竟然让我找到了。

在一个搜索结果里出现列Hyper-V字样立刻吸引了我的注意,因为我昨天由于一些软件无法启动,排查问题的时候打开了Windows Sandbox,这是微软基于Hyper-V做的windows沙盒。

看了看内容应该大差不差找到原因了,启用Hyper-V会导致系统保留一些端口,这些端口不能被别的程序使用,而我用的3000正好在里面,于是就出现了今天这种端口明明没有被别的程序使用,但就是不能监听的情况。

解决方法原文在这里

简单描述一下就是在启用Hyper-V之前要用netsh设置一下你需要的端口列表,确保启用Hyper-V之后这些端口不会被保留。

netsh int ipv4 add excludedportrange protocol=tcp startport=3000 numberofports=1

然后再启用Hyper-V就可以了。

要注意的是在Hyper-V或者Sandbox启用的情况下进行设置不会直接生效,必须先禁用再启用才行。

相关资料:

2022/3/1 更新

今天发现一个更方便的方法,不需要先关闭hyper-v服务再开,方案在这里

首先重新分配一个端口段,只要我们需要监听的端口不在里面即可

“`shell

netsh int ipv4 set dynamicport tcp start=49152 num=16384

“`

然后编辑注册表禁用端口排除(但是使用`netsh int ipv4 show excludedportrange protocol=tcp` 可以看到还是有排除的端口,只不过变成了前一条命令设置的范围)

“`shell

reg add HKLM\SYSTEM\CurrentControlSet\Services\hns\State /v EnableExcludedPortRange /d 0 /f

“`

然后重启

重要提示:设置排除端口时你必须设置足够多的端口(几千到几万个)才能保证需要对外发起连接的程序能正常工作,因为发起连接需要占用一个端口,如果你设置的排除端口不够多,那么端口资源很快就会被耗尽,之后发起的连接就会出现addr in use之类的报错。

BlackGlory不见了

Glory已经很久没有更新blog了,前些天我还在曾经的群里@了他询问情况,但是没有回复。

昨天我发现blog已经打不开了,友链里的第一条已经灰了,不知道是不是出什么事了,其实我们也有很长时间没有联系过了。

我自认为BlackGlory在我学习Web前后端的阶段起到了很重要的作用,虽然不知道他是怎么想的。

我会一直把Glory的友链放在第一个。

==========2021/9/15===========

Glory回复我了,他没事,就是博客在重做,然后出了点问题

[Nginx]特殊后缀路径不走反代地址

一个很迷惑的问题整了我一晚上,我在服务器上新加了个网站,用nginx反代到一个后端,但是因为是复制的另一个有静态文件的网站的配置来改的,忘了删nginx的root配置了,于是这个本该是单纯反代的配置就有了一个静态文件输出的功能.

在我发现问题把root选项去掉了之后,它依然保留了静态文件输出的功能,而且文件路径改到默认静态文件目录了,我甚至可以打开nginx的默认index.html,不管我怎么改,一些特定后缀的地址都会被nginx拦截下来当作静态文件去找,然后返回404,根本不pass到后端去。

在我翻了一圈配置文件看到底是哪里出问题的时候,发现了在一个expires.conf里定义了这么两个规则

location ~ .*\.(js|css|html)$ {
	expires 10d;
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|mp4|mp3)$ {
	expires 30d;
}

所有出问题的后缀都是这里面定义的后缀,当我把这几行全都注释掉了之后重启了nginx,这个问题竟然就解决了。

但更奇怪的地方是,在这个出问题的站的配置上根本就没有include这个配置文件,我到现在还是没搞清到底是咋回事。

总之 It Works!

Oculus Quest2

这是去年买的东西了,昨天翻以前拍的照片的时候才想起来这东西的开箱图一直忘了发。

这个一体式VR头显当时特别多宣传,最近几天好像又出现了一波宣传,主要是确实也不错,于是只体验过一次谷歌的辣鸡DayDream的我没管住自己的手,就下单了。我是淘宝代购的,来的是亚马逊的箱子。

这个一体式VR的好处就是既可以用里面自带的魔改quest安德猴系统直接运行安卓普通app和安卓vr app,也可以连电脑上玩电脑vr游戏,我买的时候还需要有线连接电脑,无线串流需要买第三方App,现在官方已经支持WiFi6无线串流了,延迟要求不高的话WiFi5也可以。

继续阅读Oculus Quest2

[ffmpeg]转码后音画不同步

音画不同步多半是在vfr(可变帧率)和cfr(固定帧率)之间转换的时候视频帧时间戳没有更新导致的,不知道为啥ffmpeg会默认以这种一定会导致错误的方式工作,但我们手动加入参数后就可以修正这个问题。

如果是一个vfr视频,ffmpeg默认会转换成cfr,如果不希望它转换的话,则在前置参数的位置加一个`-vsync passthrough` ,视频帧率模式就不会被转换了,这样只要原视频音画是同步的,那转出来音画肯定也是同步的,因为时间戳完全没有变。

【佳佳拆解】SurfaceBook换电池

这是一个月前的事情了,到现在突然想起来拍的照片还没发,另外这次拆解是翻车的,外屏爆了,然后寄出去换外屏,对方又不知道弄了点啥东西在我内屏里,最后软磨硬盘俩星期才给我重换了个屏,不过他给我的屏侧面看偏光很严重,远不如我原来的屏,大概不是原装的。耳机线也被我切断了,换了一个。

以下是拆解正文。

继续阅读【佳佳拆解】SurfaceBook换电池

[MySQL]This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled

MySQL中创建函数的时候如果出现了这个错误,表示创建语句中缺少错误中所示的3个函数行为声明,只要根据需求加在Begin前面就可以了。

如果你不确定,那就需要加`DETERMINISTIC` ,如果这个函数不会去操作数据库就写`NO SQL` (不是完全不能有SQL语句),如果需要读取数据就加`READS SQL DATA` 。

比如

CREATE DEFINER=`root`@`%` FUNCTION `poi`() RETURNS int
    NO SQL
BEGIN
	SELECT 1+1 into @r;
	return @r;
END

[Node.js]解决‘gbk’ codec can’t decode byte 0x80 in position

每次换一个环境执行npm i都会碰到这样的问题,记录一下解决方法,

方法1:手动去报错的文件里指定编码为UTF-8,我自己第一次解决这个问题就是用的这个办法,刚刚想找找看有没有别的解决方法的时候发现别人也有去手动改源码的,放个参考链接

简单地说就是找到报错的那行,给open函数加个`encoding=’utf-8’` 参数就可以解决问题,但这样属于改了人家文件,更新后还是会变回去的,所以现在我不这样做了。

方法2:去控制面板的区域设置里到“管理”标签页下,“更改系统区域设置”,把“Beta版:使用Unicode UTF-8提供全球语言支持(U)”勾上,然后重启一下,python就会默认用utf-8来读文件了。

这个方法会改变整个系统的默认代码页,最主要的影响是会导致使用非Unicode的程序乱码甚至崩溃,还有在gbk设置时以ansi保存的含有中文的bat脚本会乱码。如果影响不大的话倒是可以让系统一直保持在这个区域选项上,影响某些软件的正常使用的话在执行完需要的编译任务后还得改回去。在这种模式下可以使用“locale emulator”这样的软件以Chinese模式启动程序,以解决程序的乱码和崩溃问题。

方法3:我没试过,理论上应该可行,从python本体入手,把它的默认编码设置成utf-8,参考这里的方案2。这个没用

[MYSQL]删除JSON数组中特定数字

这问题折腾了我一下午,首先是mysql中没有现成的方法来寻找数组中数字的位置,然后是谷歌百度bing用中文和英文搜索都没找到可用又不烂的办法,爆栈网有一个高票答案,但是似乎并不对,JSON_SEARCH只能用来搜索字符串在json中的位置,数字不支持,不知道他是怎么得到结果的。

经过我晚上的继续摸索,翻遍了官方所有json相关的文档,终于让我拼出了一个方案,就是用`JSON_TABLE` 先把数组转成表,然后用where剔除里面不要的数字,再用`JSON_ARRAYAGG`转回json数组,以下是示例代码
“`sql
set @j='[1,2,5,8,9]’;
select JSON_ARRAYAGG(b) from JSON_TABLE(
@j,
“$[*]”
COLUMNS(
b int PATH “$”
)
) as `破mysql连个数组查找方法都没有`
where b<>5;
“`
这样就可以得到结果[1, 2, 8, 9],5被删掉了。

这个方法虽然并不优雅,但也没办法,谁叫MySQL连在数组里找数字的位置都做不到呢。

继续阅读[MYSQL]删除JSON数组中特定数字

[MySQL]对json字段中的数组创建索引

Mysql中在json字段里存放数组时,如果需要快速查找到包含某个数字或字符串的数组,则需要在这个数组上建立索引,其`multi-valued` 索引能满足这种需求。

在已有表的情况下,执行以下语句创建一个数字数组索引:
“`sql
ALTER TABLE 表名 ADD INDEX 索引名((CAST(json字段->’$.指向数组的对象名’ AS UNSIGNED ARRAY)));
“`
这个语句把json字段中的一个数组以无符号数组读出,并对每个数字进行索引,这样要寻找包含某数字的数组时,将使用索引提速。

如果数组内的成员都为字符串,可以这样创建索引(要注意,该表的排序规则需为`utf8mb4_0900_as_cs`,否则会报错):
“`sql
ALTER TABLE 表名 ADD INDEX 索引名((CAST(json字段->’$.指向数组的对象名’ AS CHAR(255) ARRAY)));
“`

接下来使用类似这样的语句查找拥有某个数字的数组所在的条目
“`sql
SELECT * FROM 表名 where 要找的数字 MEMBER OF(json字段->’$.指向数组的对象名’);
“`
或同时查找多个数字(字符串查找同理)
“`sql
SELECT * FROM 表名 where JSON_CONTAINS(json字段->’$.指向数组的对象名’, CAST(‘[1,2,3,4,5]’ AS JSON));
“`
使用了该索引后,作用在相关字段上的`JSON_OVERLAPS` 函数也会使用索引。

参考:https://dev.mysql.com/doc/refman/8.0/en/create-index.html

Certify申请Let’s Encrypt 错误 DNS problem: query timed out looking up CAA for

这是一个多月前开始出现的问题,我想着本来一直都好好的,会不会只是let’s encrypt服务器抽了,说不定过段时间就好了,但是一直到现在都没好,于是就来研究一下这个问题。

标题上这个错误是查询域名的CAA记录超时,我查了一圈CAA是个什么东西,大概就是说要有这么条记录,内容是允许哪个机构颁发什么样的证书。

使用Certify测试时发现在Cloudflare里面会自动添加对应的TXT记录,但是从没自动添加过CAA,既然是必须条目为啥它不添加呢。我在寻找相关问题时别人都是DNS解析服务不支持CAA记录,然后换个服务商解决的,cf又不是不支持。

我怀疑是不是软件问题,于是手动去cf添加了一个CAA记录,这下终于不会出现这个错误了。添加方法如下:

点添加记录按钮添加一个CAA记录,要给哪个域名设置就在名称写哪个域名(根域名写@保存会自动变为完整域名,子域名写子域名),TTL随意,CA域名那里写证书机构地址,比如Let’s Encrypt就写letsencrypt.org,前一个“标记”表示这条记录允许的是上面名称里写的那一个域名还是那个域名通配的子域名。

保存了之后再去Certify中申请证书就可以成功了。

 

如果还遇到了cf返回的错误“Cloudflare DNS API :: An error occurred while sending the request.”,可以试试把设置中cf的验证方式从“邮箱+Global API Key”改成“API Token”。