From b2501ede68b449c9e8340511370a476aa7c9efaa Mon Sep 17 00:00:00 2001
From: nicksxs
Date: Sun, 28 Aug 2022 22:30:18 +0800
Subject: [PATCH] Site updated: 2022-08-28 22:30:16
---
2017/04/25/rabbitmq-tips/index.html | 4 +-
2019/06/18/openresty/index.html | 4 +-
.../07/关于读书打卡与分享/index.html | 2 +-
2021/07/18/2021-年中总结/index.html | 2 +-
baidusitemap.xml | 38 +-
code/Solution1260.java | 44 +
leancloud_counter_security_urls.json | 2 +-
search.xml | 4616 ++++++++---------
sitemap.xml | 1250 ++---
tags/java/page/3/index.html | 2 +-
10 files changed, 3004 insertions(+), 2960 deletions(-)
create mode 100644 code/Solution1260.java
diff --git a/2017/04/25/rabbitmq-tips/index.html b/2017/04/25/rabbitmq-tips/index.html
index 22e4452401..a6f288b130 100644
--- a/2017/04/25/rabbitmq-tips/index.html
+++ b/2017/04/25/rabbitmq-tips/index.html
@@ -1,4 +1,4 @@
-rabbitmq-tips | Nicksxs's Blog
0%
Theme NexT works best with JavaScript enabled
rabbitmq 介绍 接触了一下rabbitmq,原来在选型的时候是在rabbitmq跟kafka之间做选择,网上搜了一下之后发现kafka的优势在于吞吐量,而rabbitmq相对注重可靠性,因为应用在im上,需要保证消息不能丢失所以就暂时选定rabbitmq, Message Queue的需求由来已久,80年代最早在金融交易中,高盛等公司采用Teknekron公司的产品,当时的Message queuing软件叫做:the information bus(TIB)。 TIB被电信和通讯公司采用,路透社收购了Teknekron公司。之后,IBM开发了MQSeries,微软开发了Microsoft Message Queue(MSMQ)。这些商业MQ供应商的问题是厂商锁定,价格高昂。2001年,Java Message queuing试图解决锁定和交互性的问题,但对应用来说反而更加麻烦了。 RabbitMQ采用Erlang语言开发。Erlang语言由Ericson设计,专门为开发concurrent和distribution系统的一种语言,在电信领域使用广泛。OTP(Open Telecom Platform)作为Erlang语言的一部分,包含了很多基于Erlang开发的中间件/库/工具,如mnesia/SASL,极大方便了Erlang应用的开发。OTP就类似于Python语言中众多的module,用户借助这些module可以很方便的开发应用。 于是2004年,摩根大通和iMatrix开始着手Advanced Message Queuing Protocol (AMQP) 开放标准的开发。2006年,AMQP规范发布。2007年,Rabbit技术公司基于AMQP标准开发的RabbitMQ 1.0 发布。所有主要的编程语言均有与代理接口通讯的客户端库。
简单的使用经验 通俗的理解 这里介绍下其中的一些概念,connection表示和队列服务器的连接,一般情况下是tcp连接, channel表示通道,可以在一个连接上建立多个通道,这里主要是节省了tcp连接握手的成本,exchange可以理解成一个路由器,将消息推送给对应的队列queue,其实是像一个订阅的模式。
集群经验 rabbitmqctl stop这个是关闭rabbitmq,在搭建集群时候先关闭服务,然后使用rabbitmq-server -detached静默启动,这时候使用rabbitmqctl cluster_status查看集群状态,因为还没将节点加入集群,所以只能看到类似
Cluster status of node rabbit@rabbit1 .. .
+rabbitmq-tips | Nicksxs's Blog
0%
Theme NexT works best with JavaScript enabled
rabbitmq 介绍 接触了一下rabbitmq,原来在选型的时候是在rabbitmq跟kafka之间做选择,网上搜了一下之后发现kafka的优势在于吞吐量,而rabbitmq相对注重可靠性,因为应用在im上,需要保证消息不能丢失所以就暂时选定rabbitmq, Message Queue的需求由来已久,80年代最早在金融交易中,高盛等公司采用Teknekron公司的产品,当时的Message queuing软件叫做:the information bus(TIB)。 TIB被电信和通讯公司采用,路透社收购了Teknekron公司。之后,IBM开发了MQSeries,微软开发了Microsoft Message Queue(MSMQ)。这些商业MQ供应商的问题是厂商锁定,价格高昂。2001年,Java Message queuing试图解决锁定和交互性的问题,但对应用来说反而更加麻烦了。 RabbitMQ采用Erlang语言开发。Erlang语言由Ericson设计,专门为开发concurrent和distribution系统的一种语言,在电信领域使用广泛。OTP(Open Telecom Platform)作为Erlang语言的一部分,包含了很多基于Erlang开发的中间件/库/工具,如mnesia/SASL,极大方便了Erlang应用的开发。OTP就类似于Python语言中众多的module,用户借助这些module可以很方便的开发应用。 于是2004年,摩根大通和iMatrix开始着手Advanced Message Queuing Protocol (AMQP) 开放标准的开发。2006年,AMQP规范发布。2007年,Rabbit技术公司基于AMQP标准开发的RabbitMQ 1.0 发布。所有主要的编程语言均有与代理接口通讯的客户端库。
简单的使用经验 通俗的理解 这里介绍下其中的一些概念,connection表示和队列服务器的连接,一般情况下是tcp连接, channel表示通道,可以在一个连接上建立多个通道,这里主要是节省了tcp连接握手的成本,exchange可以理解成一个路由器,将消息推送给对应的队列queue,其实是像一个订阅的模式。
集群经验 rabbitmqctl stop这个是关闭rabbitmq,在搭建集群时候先关闭服务,然后使用rabbitmq-server -detached静默启动,这时候使用rabbitmqctl cluster_status查看集群状态,因为还没将节点加入集群,所以只能看到类似
Cluster status of node rabbit@rabbit1 .. .
[ { nodes,[ { disc,[ rabbit@rabbit1,rabbit@rabbit2,rabbit@rabbit3] } ] } ,
{ running_nodes,[ rabbit@rabbit2,rabbit@rabbit1] } ]
.. .done.然后就可以把当前节点加入集群,
rabbit2$ rabbitmqctl stop_app
@@ -7,4 +7,4 @@ Stopping node rabbit@rabbit2 #这里可以用--ram指定将当前节点作为内存节点加入集群
Clustering node rabbit@rabbit2 with [ rabbit@rabbit1] .. .done.
rabbit2$ rabbitmqctl start_app
-Starting node rabbit@rabbit2 .. .done.其他可以参考官方文档
一些坑 消息丢失 这里碰到过一个坑,对于使用exchange来做消息路由的,会有一个情况,就是在routing_key没被订阅的时候,会将该条找不到路由对应的queue的消息丢掉What happens if we break our contract and send a message with one or four words, like "orange" or "quick.orange.male.rabbit"? Well, these messages won't match any bindings and will be lost.对应链接 ,而当使用空的exchange时,会保留消息,当出现消费者的时候就可以将收到之前生产者所推送的消息对应链接 ,这里就是用了空的exchange。
集群搭建 集群搭建的时候有个erlang vm生成的random cookie,这个是用来做集群之间认证的,相同的cookie才能连接,但是如果通过vim打开复制后在其他几点新建文件写入会多一个换行,导致集群建立是报错,所以这里最好使用scp等传输命令直接传输cookie文件,同时要注意下cookie的文件权限。 另外在集群搭建的时候如果更改过hostname,那么要把rabbitmq的数据库删除,否则启动后会马上挂掉
\ No newline at end of file
+Starting node rabbit@rabbit2 .. .done.其他可以参考官方文档
一些坑 消息丢失 这里碰到过一个坑,对于使用exchange来做消息路由的,会有一个情况,就是在routing_key没被订阅的时候,会将该条找不到路由对应的queue的消息丢掉What happens if we break our contract and send a message with one or four words, like "orange" or "quick.orange.male.rabbit"? Well, these messages won't match any bindings and will be lost.对应链接 ,而当使用空的exchange时,会保留消息,当出现消费者的时候就可以将收到之前生产者所推送的消息对应链接 ,这里就是用了空的exchange。
集群搭建 集群搭建的时候有个erlang vm生成的random cookie,这个是用来做集群之间认证的,相同的cookie才能连接,但是如果通过vim打开复制后在其他几点新建文件写入会多一个换行,导致集群建立是报错,所以这里最好使用scp等传输命令直接传输cookie文件,同时要注意下cookie的文件权限。 另外在集群搭建的时候如果更改过hostname,那么要把rabbitmq的数据库删除,否则启动后会马上挂掉
\ No newline at end of file
diff --git a/2019/06/18/openresty/index.html b/2019/06/18/openresty/index.html
index 63813653ec..29a1f6ea8a 100644
--- a/2019/06/18/openresty/index.html
+++ b/2019/06/18/openresty/index.html
@@ -1,4 +1,4 @@
-openresty | Nicksxs's Blog
0%
Theme NexT works best with JavaScript enabled
目前公司要对一些新的产品功能做灰度测试,因为在后端业务代码层面添加判断比较麻烦,所以想在nginx上做点手脚,就想到了openresty 前后也踩了不少坑,这边先写一点
首先是日志error_log logs/error.log debug; 需要nginx开启日志的debug才能看到日志
使用 lua_code_cache off即可, 另外注意只有使用 content_by_lua_file 才会生效
http {
+openresty | Nicksxs's Blog
0%
Theme NexT works best with JavaScript enabled
目前公司要对一些新的产品功能做灰度测试,因为在后端业务代码层面添加判断比较麻烦,所以想在nginx上做点手脚,就想到了openresty 前后也踩了不少坑,这边先写一点
首先是日志error_log logs/error.log debug; 需要nginx开启日志的debug才能看到日志
使用 lua_code_cache off即可, 另外注意只有使用 content_by_lua_file 才会生效
http {
lua_code_cache off;
}
@@ -20,4 +20,4 @@ location ~ *local t = json. decode ( str)
if t then
ngx. say ( " --> " , type ( t) )
-end cjson.safe包会在解析失败的时候返回nil
还有一个是redis链接时如果host使用的是域名的话会提示“failed to connect: no resolver defined to resolve “redis.xxxxxx.com””,这里需要使用nginx的resolver指令,resolver 8.8.8.8 valid=3600s;
还有一点补充下 就是业务在使用redis的时候使用了db的特性,所以在lua访问redis的时候也需要执行db,这里lua的redis库也支持了这个特性,可以使用instance:select(config:get(‘db’))来切换db
性能优化tips建议是尽量少使用阶段钩子,例如content_by_lua_file,*_by_lua
发现一个不错的openresty站点地址
\ No newline at end of file
+end cjson.safe包会在解析失败的时候返回nil
还有一个是redis链接时如果host使用的是域名的话会提示“failed to connect: no resolver defined to resolve “redis.xxxxxx.com””,这里需要使用nginx的resolver指令,resolver 8.8.8.8 valid=3600s;
还有一点补充下 就是业务在使用redis的时候使用了db的特性,所以在lua访问redis的时候也需要执行db,这里lua的redis库也支持了这个特性,可以使用instance:select(config:get(‘db’))来切换db
性能优化tips建议是尽量少使用阶段钩子,例如content_by_lua_file,*_by_lua
发现一个不错的openresty站点地址
\ No newline at end of file
diff --git a/2021/02/07/关于读书打卡与分享/index.html b/2021/02/07/关于读书打卡与分享/index.html
index 7a08246aaa..9a9aac0a81 100644
--- a/2021/02/07/关于读书打卡与分享/index.html
+++ b/2021/02/07/关于读书打卡与分享/index.html
@@ -1 +1 @@
-关于读书打卡与分享 | Nicksxs's Blog
0%
Theme NexT works best with JavaScript enabled
最近群里大佬发起了一个读书打卡活动,需要每天读一会书,在群里打卡分享感悟,争取一个月能读完一本书,说实话一天十分钟的读书时间倒是问题不大,不过每天都要打卡,而且一个月要读完一本书,其实难度还是有点大的,不过也想试试看。 之前某某老大给自己立了个 flag,说要读一百本书,这对我来说挺难实现的,一则我也不喜欢书只读一小半,二则感觉对于喜欢看的内容范围还是比较有限制,可能也算是比较矫情,不爱追热门的各类东西,因为往往会有一些跟大众不一致的观点看法,显得格格不入。所以还是这个打卡活动可能会比较适合我,书是人类进步的阶梯。 到现在是打卡了三天了,读的主要是白岩松的《幸福了吗》,对于白岩松,我们这一代人是比较熟悉,并且整体印象比较不错的一个央视主持人,从《焦点访谈》开始,到疫情期间的各类一线节目,可能对我来说是个三观比较正,敢于说一些真话的主持人,这中间其实是有个空档期,没怎么看电视,也不太关注了,只是在疫情期间的节目,还是一如既往地给人一种可靠的感觉,正好有一次偶然微信读书推荐了白岩松的这本书,就看了一部分,正好这次继续往下看,因为相对来讲不会很晦涩,并且从这位知名央视主持人的角度分享他的过往和想法看法,还是比较有意思的。 从对汶川地震,08 年奥运等往事的回忆和一些细节的呈现,也让我了解比较多当时所不知道的,特别是汶川地震,那时的我还在读高中,真的是看着电视,作为“猛男”都忍不住泪目了,共和国之殇,多难兴邦,但是这对于当事人来说,都是一场醒不过来的噩梦。 然后是对于足球的热爱,其实光这个就能掰扯很多,因为我不爱足球,只爱篮球,其中原因有的没的也挺多可以说的,但是看了他的书,才能比较深入的了解一个足球迷,对足球,对中国足球,对世界杯,对阿根廷的感情。 接下去还是想能继续坚持下去,加油!
\ No newline at end of file
+关于读书打卡与分享 | Nicksxs's Blog
0%
Theme NexT works best with JavaScript enabled
最近群里大佬发起了一个读书打卡活动,需要每天读一会书,在群里打卡分享感悟,争取一个月能读完一本书,说实话一天十分钟的读书时间倒是问题不大,不过每天都要打卡,而且一个月要读完一本书,其实难度还是有点大的,不过也想试试看。 之前某某老大给自己立了个 flag,说要读一百本书,这对我来说挺难实现的,一则我也不喜欢书只读一小半,二则感觉对于喜欢看的内容范围还是比较有限制,可能也算是比较矫情,不爱追热门的各类东西,因为往往会有一些跟大众不一致的观点看法,显得格格不入。所以还是这个打卡活动可能会比较适合我,书是人类进步的阶梯。 到现在是打卡了三天了,读的主要是白岩松的《幸福了吗》,对于白岩松,我们这一代人是比较熟悉,并且整体印象比较不错的一个央视主持人,从《焦点访谈》开始,到疫情期间的各类一线节目,可能对我来说是个三观比较正,敢于说一些真话的主持人,这中间其实是有个空档期,没怎么看电视,也不太关注了,只是在疫情期间的节目,还是一如既往地给人一种可靠的感觉,正好有一次偶然微信读书推荐了白岩松的这本书,就看了一部分,正好这次继续往下看,因为相对来讲不会很晦涩,并且从这位知名央视主持人的角度分享他的过往和想法看法,还是比较有意思的。 从对汶川地震,08 年奥运等往事的回忆和一些细节的呈现,也让我了解比较多当时所不知道的,特别是汶川地震,那时的我还在读高中,真的是看着电视,作为“猛男”都忍不住泪目了,共和国之殇,多难兴邦,但是这对于当事人来说,都是一场醒不过来的噩梦。 然后是对于足球的热爱,其实光这个就能掰扯很多,因为我不爱足球,只爱篮球,其中原因有的没的也挺多可以说的,但是看了他的书,才能比较深入的了解一个足球迷,对足球,对中国足球,对世界杯,对阿根廷的感情。 接下去还是想能继续坚持下去,加油!
\ No newline at end of file
diff --git a/2021/07/18/2021-年中总结/index.html b/2021/07/18/2021-年中总结/index.html
index 1d4c9e8b5d..8a9bd3f31e 100644
--- a/2021/07/18/2021-年中总结/index.html
+++ b/2021/07/18/2021-年中总结/index.html
@@ -1 +1 @@
-2021 年中总结 | Nicksxs's Blog
0%
Theme NexT works best with JavaScript enabled
又到半年总结时,第一次写总结类型的文章感觉挺好写的,但是后面总觉得这过去的一段时间所做的事情,能力上的成长低于预期,但是是需要总结下,找找问题,顺便展望下未来。
这一年做的最让自己满意的应该就是看了一些书,由折腾群洋总发起的读书打卡活动,到目前为止已经读完了这几本书,《cUrl 必知必会》,《古董局中局 1》,《古董局中局 2》,《算法图解》,《每天 5 分钟玩转 Kubernetes》《幸福了吗?》《高可用可伸缩微服务架构:基于 Dubbo、Spring Cloud和 Service Mesh》《Rust 权威指南》后面可以写个专题说说看的这些书,虽然每天打卡如果时间安排不好,并且看的书像 rust 这样比较难的话还是会有点小焦虑,不过也是个调整过程,一方面可以在白天就抽空看一会,然后也不必要每次都看很大一章,注重吸收。
技术上的成长的话,有一些比较小的长进吧,对于一些之前忽视的 synchronized,ThreadLocal 和 AQS 等知识点做了下查漏补缺了,然后多了解了一些 Java 垃圾回收的内容,但是在实操上还是比较欠缺,成型的技术方案,架构上所谓的优化也比较少,一些想法也还有考虑不周全的地方,还需要多花时间和心思去学习加强,特别是在目前已经有的基础上如何做系统深层次的优化,既不要是鸡毛蒜皮的,也不能出现一些不可接受的问题和故障,这是个很重要的课题,需要好好学习,后面考虑定一些周期性目标,两个月左右能有一些成果和总结。
另外一部分是自己的服务,因为 ucloud 的机器太贵就没续费了,所以都迁移到腾讯云的小机器上了,顺便折腾了一点点 traefik,但是还很不熟练,不太习惯这一套,一方面是 docker 还不习惯,这也加重了对这套环境的不适应,还是习惯裸机部署,另一方面就是 k8s 了,家里的机器还没虚拟化,没有很好的条件可以做实验,这也是读书打卡的一个没做好的点,整体的学习效果受限于深度和实操,后面是看都是用 traefik,也找到了一篇文章可以 traefik 转发到裸机应用,因为主仓库用的是裸机的 gogs。
还有就是运动减肥上,唉,这又是很大的一个痛点,基本没效果,只是还算稳定,昨天看到一个视频说还需要力量训练来增肌,以此可以提升基础代谢,打算往这个方向尝试下,因为今天没有疫情限制了,在 6 月底完成了 200 公里的跑步小目标,只是有些膝盖跟大腿根外侧不适,抽空得去看下医生,后面打算每天也能做点卷腹跟俯卧撑。
下半年还希望能继续多看看书,比很多网上各种乱七八糟的文章会好很多,结合豆瓣评分,找一些评价高一些的文章,但也不是说分稍低点的就不行,有些也看人是不是适合,一般 6 分以上评价比较多的就可以试试。
\ No newline at end of file
+2021 年中总结 | Nicksxs's Blog
0%
Theme NexT works best with JavaScript enabled
又到半年总结时,第一次写总结类型的文章感觉挺好写的,但是后面总觉得这过去的一段时间所做的事情,能力上的成长低于预期,但是是需要总结下,找找问题,顺便展望下未来。
这一年做的最让自己满意的应该就是看了一些书,由折腾群洋总发起的读书打卡活动,到目前为止已经读完了这几本书,《cUrl 必知必会》,《古董局中局 1》,《古董局中局 2》,《算法图解》,《每天 5 分钟玩转 Kubernetes》《幸福了吗?》《高可用可伸缩微服务架构:基于 Dubbo、Spring Cloud和 Service Mesh》《Rust 权威指南》后面可以写个专题说说看的这些书,虽然每天打卡如果时间安排不好,并且看的书像 rust 这样比较难的话还是会有点小焦虑,不过也是个调整过程,一方面可以在白天就抽空看一会,然后也不必要每次都看很大一章,注重吸收。
技术上的成长的话,有一些比较小的长进吧,对于一些之前忽视的 synchronized,ThreadLocal 和 AQS 等知识点做了下查漏补缺了,然后多了解了一些 Java 垃圾回收的内容,但是在实操上还是比较欠缺,成型的技术方案,架构上所谓的优化也比较少,一些想法也还有考虑不周全的地方,还需要多花时间和心思去学习加强,特别是在目前已经有的基础上如何做系统深层次的优化,既不要是鸡毛蒜皮的,也不能出现一些不可接受的问题和故障,这是个很重要的课题,需要好好学习,后面考虑定一些周期性目标,两个月左右能有一些成果和总结。
另外一部分是自己的服务,因为 ucloud 的机器太贵就没续费了,所以都迁移到腾讯云的小机器上了,顺便折腾了一点点 traefik,但是还很不熟练,不太习惯这一套,一方面是 docker 还不习惯,这也加重了对这套环境的不适应,还是习惯裸机部署,另一方面就是 k8s 了,家里的机器还没虚拟化,没有很好的条件可以做实验,这也是读书打卡的一个没做好的点,整体的学习效果受限于深度和实操,后面是看都是用 traefik,也找到了一篇文章可以 traefik 转发到裸机应用,因为主仓库用的是裸机的 gogs。
还有就是运动减肥上,唉,这又是很大的一个痛点,基本没效果,只是还算稳定,昨天看到一个视频说还需要力量训练来增肌,以此可以提升基础代谢,打算往这个方向尝试下,因为今天没有疫情限制了,在 6 月底完成了 200 公里的跑步小目标,只是有些膝盖跟大腿根外侧不适,抽空得去看下医生,后面打算每天也能做点卷腹跟俯卧撑。
下半年还希望能继续多看看书,比很多网上各种乱七八糟的文章会好很多,结合豆瓣评分,找一些评价高一些的文章,但也不是说分稍低点的就不行,有些也看人是不是适合,一般 6 分以上评价比较多的就可以试试。
\ No newline at end of file
diff --git a/baidusitemap.xml b/baidusitemap.xml
index 526fbe8d8d..9ec58509bf 100644
--- a/baidusitemap.xml
+++ b/baidusitemap.xml
@@ -89,11 +89,11 @@
2022-06-11
- https://nicksxs.me/2021/09/04/%E8%81%8A%E4%B8%80%E4%B8%8B-RocketMQ-%E7%9A%84%E6%B6%88%E6%81%AF%E5%AD%98%E5%82%A8/
+ https://nicksxs.me/2021/10/03/%E8%81%8A%E4%B8%80%E4%B8%8B-RocketMQ-%E7%9A%84%E6%B6%88%E6%81%AF%E5%AD%98%E5%82%A8%E4%B8%89/
2022-06-11
- https://nicksxs.me/2021/10/03/%E8%81%8A%E4%B8%80%E4%B8%8B-RocketMQ-%E7%9A%84%E6%B6%88%E6%81%AF%E5%AD%98%E5%82%A8%E4%B8%89/
+ https://nicksxs.me/2021/12/12/%E8%81%8A%E8%81%8A-Sharding-Jdbc-%E7%9A%84%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8/
2022-06-11
@@ -101,7 +101,7 @@
2022-06-11
- https://nicksxs.me/2021/12/12/%E8%81%8A%E8%81%8A-Sharding-Jdbc-%E7%9A%84%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8/
+ https://nicksxs.me/2021/09/04/%E8%81%8A%E4%B8%80%E4%B8%8B-RocketMQ-%E7%9A%84%E6%B6%88%E6%81%AF%E5%AD%98%E5%82%A8/
2022-06-11
@@ -189,11 +189,11 @@
2022-06-11
- https://nicksxs.me/2021/09/26/%E8%81%8A%E4%B8%80%E4%B8%8B-SpringBoot-%E4%B8%AD%E5%8A%A8%E6%80%81%E5%88%87%E6%8D%A2%E6%95%B0%E6%8D%AE%E6%BA%90%E7%9A%84%E6%96%B9%E6%B3%95/
+ https://nicksxs.me/2021/09/19/%E8%81%8A%E4%B8%80%E4%B8%8B-SpringBoot-%E4%B8%AD%E4%BD%BF%E7%94%A8%E7%9A%84-cglib-%E4%BD%9C%E4%B8%BA%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E4%B8%AD%E7%9A%84%E4%B8%80%E4%B8%AA%E6%B3%A8%E6%84%8F%E7%82%B9/
2022-06-11
- https://nicksxs.me/2021/09/19/%E8%81%8A%E4%B8%80%E4%B8%8B-SpringBoot-%E4%B8%AD%E4%BD%BF%E7%94%A8%E7%9A%84-cglib-%E4%BD%9C%E4%B8%BA%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E4%B8%AD%E7%9A%84%E4%B8%80%E4%B8%AA%E6%B3%A8%E6%84%8F%E7%82%B9/
+ https://nicksxs.me/2021/09/26/%E8%81%8A%E4%B8%80%E4%B8%8B-SpringBoot-%E4%B8%AD%E5%8A%A8%E6%80%81%E5%88%87%E6%8D%A2%E6%95%B0%E6%8D%AE%E6%BA%90%E7%9A%84%E6%96%B9%E6%B3%95/
2022-06-11
@@ -584,6 +584,10 @@
https://nicksxs.me/2014/12/24/MFC%20%E6%A8%A1%E6%80%81%E5%AF%B9%E8%AF%9D%E6%A1%86/
2020-01-12
+
+ https://nicksxs.me/2019/12/10/Redis-Part-1/
+ 2020-01-12
+
https://nicksxs.me/2015/03/11/Reverse-Bits/
2020-01-12
@@ -601,19 +605,15 @@
2020-01-12
- https://nicksxs.me/2016/08/14/docker-mysql-cluster/
- 2020-01-12
-
-
- https://nicksxs.me/2016/10/11/minimum-size-subarray-sum-209/
+ https://nicksxs.me/2016/09/29/binary-watch/
2020-01-12
- https://nicksxs.me/2019/12/10/Redis-Part-1/
+ https://nicksxs.me/2016/08/14/docker-mysql-cluster/
2020-01-12
- https://nicksxs.me/2016/09/29/binary-watch/
+ https://nicksxs.me/2016/10/11/minimum-size-subarray-sum-209/
2020-01-12
@@ -625,15 +625,15 @@
2020-01-12
- https://nicksxs.me/2020/01/10/redis%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%BB%8B%E7%BB%8D%E4%B8%89/
+ https://nicksxs.me/2019/12/26/redis%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%BB%8B%E7%BB%8D/
2020-01-12
- https://nicksxs.me/2020/01/04/redis%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%BB%8B%E7%BB%8D%E4%BA%8C/
+ https://nicksxs.me/2020/01/10/redis%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%BB%8B%E7%BB%8D%E4%B8%89/
2020-01-12
- https://nicksxs.me/2019/12/26/redis%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%BB%8B%E7%BB%8D/
+ https://nicksxs.me/2020/01/04/redis%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%BB%8B%E7%BB%8D%E4%BA%8C/
2020-01-12
@@ -656,6 +656,10 @@
https://nicksxs.me/2015/01/04/Path-Sum/
2020-01-12
+
+ https://nicksxs.me/2015/06/22/invert-binary-tree/
+ 2020-01-12
+
https://nicksxs.me/2014/12/23/my-new-post/
2020-01-12
@@ -680,10 +684,6 @@
https://nicksxs.me/2016/07/13/swoole-websocket-test/
2020-01-12
-
- https://nicksxs.me/2015/06/22/invert-binary-tree/
- 2020-01-12
-
https://nicksxs.me/2019/12/21/%E8%81%8A%E8%81%8AJava%E4%B8%AD%E7%9A%84%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/
2019-12-22
diff --git a/code/Solution1260.java b/code/Solution1260.java
new file mode 100644
index 0000000000..1e6b86229e
--- /dev/null
+++ b/code/Solution1260.java
@@ -0,0 +1,44 @@
+public class Solution1260 {
+
+ public List> shiftGrid(int[][] grid, int k) {
+ // 行数
+ int m = grid.length;
+ // 列数
+ int n = grid[0].length;
+ // 偏移值,取下模
+ k = k % (m * n);
+ // 反向取下数量,因为我打算直接从头填充新的矩阵
+ /*
+ * 比如
+ * 1 2 3
+ * 4 5 6
+ * 7 8 9
+ * 需要变成
+ * 9 1 2
+ * 3 4 5
+ * 6 7 8
+ * 就要从 9 开始填充
+ */
+ int reverseK = m * n - k;
+ List> matrix = new ArrayList<>();
+ // 这类就是两层循环
+ for (int i = 0; i < m; i++) {
+ List line = new ArrayList<>();
+ for (int j = 0; j < n; j++) {
+ // 数量会随着循环迭代增长, 确认是第几个
+ int currentNum = reverseK + i * n + (j + 1);
+ // 这里处理下到达矩阵末尾后减掉 m * n
+ if (currentNum > m * n) {
+ currentNum -= m * n;
+ }
+ // 根据矩阵列数 n 算出在原来矩阵的位置
+ int last = (currentNum - 1) % n;
+ int passLine = (currentNum - 1) / n;
+
+ line.add(grid[passLine][last]);
+ }
+ matrix.add(line);
+ }
+ return matrix;
+ }
+}
diff --git a/leancloud_counter_security_urls.json b/leancloud_counter_security_urls.json
index 81ecb2fb96..16c0a7dae6 100644
--- a/leancloud_counter_security_urls.json
+++ b/leancloud_counter_security_urls.json
@@ -1 +1 @@
-[{"title":"村上春树《1Q84》读后感","url":"/2019/12/18/1Q84读后感/"},{"title":"2020 年终总结","url":"/2021/03/31/2020-年终总结/"},{"title":"2020年中总结","url":"/2020/07/11/2020年中总结/"},{"title":"2021 年终总结","url":"/2022/01/22/2021-年终总结/"},{"title":"34_Search_for_a_Range","url":"/2016/08/14/34-Search-for-a-Range/"},{"title":"AQS篇二 之 Condition 浅析笔记","url":"/2021/02/21/AQS-之-Condition-浅析笔记/"},{"title":"AQS篇一","url":"/2021/02/14/AQS篇一/"},{"title":"AbstractQueuedSynchronizer","url":"/2019/09/23/AbstractQueuedSynchronizer/"},{"title":"add-two-number","url":"/2015/04/14/Add-Two-Number/"},{"title":"Apollo 的 value 注解是怎么自动更新的","url":"/2020/11/01/Apollo-的-value-注解是怎么自动更新的/"},{"title":"Comparator使用小记","url":"/2020/04/05/Comparator使用小记/"},{"title":"Clone Graph Part I","url":"/2014/12/30/Clone-Graph-Part-I/"},{"title":"Disruptor 系列一","url":"/2022/02/13/Disruptor-系列一/"},{"title":"Disruptor 系列二","url":"/2022/02/27/Disruptor-系列二/"},{"title":"Dubbo 使用的几个记忆点","url":"/2022/04/02/Dubbo-使用的几个记忆点/"},{"title":"Filter, Interceptor, Aop, 啥, 啥, 啥? 这些都是啥?","url":"/2020/08/22/Filter-Intercepter-Aop-啥-啥-啥-这些都是啥/"},{"title":"G1收集器概述","url":"/2020/02/09/G1收集器概述/"},{"title":"JVM源码分析之G1垃圾收集器分析一","url":"/2019/12/07/JVM-G1-Part-1/"},{"title":"Leetcode 021 合并两个有序链表 ( Merge Two Sorted Lists ) 题解分析","url":"/2021/10/07/Leetcode-021-合并两个有序链表-Merge-Two-Sorted-Lists-题解分析/"},{"title":"Leetcode 028 实现 strStr() ( Implement strStr() ) 题解分析","url":"/2021/10/31/Leetcode-028-实现-strStr-Implement-strStr-题解分析/"},{"title":"Leetcode 053 最大子序和 ( Maximum Subarray ) 题解分析","url":"/2021/11/28/Leetcode-053-最大子序和-Maximum-Subarray-题解分析/"},{"title":"Leetcode 104 二叉树的最大深度(Maximum Depth of Binary Tree) 题解分析","url":"/2020/10/25/Leetcode-104-二叉树的最大深度-Maximum-Depth-of-Binary-Tree-题解分析/"},{"title":"Leetcode 105 从前序与中序遍历序列构造二叉树(Construct Binary Tree from Preorder and Inorder Traversal) 题解分析","url":"/2020/12/13/Leetcode-105-从前序与中序遍历序列构造二叉树-Construct-Binary-Tree-from-Preorder-and-Inorder-Traversal-题解分析/"},{"title":"Leetcode 1115 交替打印 FooBar ( Print FooBar Alternately *Medium* ) 题解分析","url":"/2022/05/01/Leetcode-1115-交替打印-FooBar-Print-FooBar-Alternately-Medium-题解分析/"},{"title":"Leetcode 121 买卖股票的最佳时机(Best Time to Buy and Sell Stock) 题解分析","url":"/2021/03/14/Leetcode-121-买卖股票的最佳时机-Best-Time-to-Buy-and-Sell-Stock-题解分析/"},{"title":"Leetcode 124 二叉树中的最大路径和(Binary Tree Maximum Path Sum) 题解分析","url":"/2021/01/24/Leetcode-124-二叉树中的最大路径和-Binary-Tree-Maximum-Path-Sum-题解分析/"},{"title":"Leetcode 1260 二维网格迁移 ( Shift 2D Grid *Easy* ) 题解分析","url":"/2022/07/22/Leetcode-1260-二维网格迁移-Shift-2D-Grid-Easy-题解分析/"},{"title":"Leetcode 155 最小栈(Min Stack) 题解分析","url":"/2020/12/06/Leetcode-155-最小栈-Min-Stack-题解分析/"},{"title":"Leetcode 16 最接近的三数之和 ( 3Sum Closest *Medium* ) 题解分析","url":"/2022/08/06/Leetcode-16-最接近的三数之和-3Sum-Closest-Medium-题解分析/"},{"title":"Leetcode 160 相交链表(intersection-of-two-linked-lists) 题解分析","url":"/2021/01/10/Leetcode-160-相交链表-intersection-of-two-linked-lists-题解分析/"},{"title":"Leetcode 2 Add Two Numbers 题解分析","url":"/2020/10/11/Leetcode-2-Add-Two-Numbers-题解分析/"},{"title":"Leetcode 20 有效的括号 ( Valid Parentheses *Easy* ) 题解分析","url":"/2022/07/02/Leetcode-20-有效的括号-Valid-Parentheses-Easy-题解分析/"},{"title":"Leetcode 234 回文链表(Palindrome Linked List) 题解分析","url":"/2020/11/15/Leetcode-234-回文联表-Palindrome-Linked-List-题解分析/"},{"title":"Leetcode 236 二叉树的最近公共祖先(Lowest Common Ancestor of a Binary Tree) 题解分析","url":"/2021/05/23/Leetcode-236-二叉树的最近公共祖先-Lowest-Common-Ancestor-of-a-Binary-Tree-题解分析/"},{"title":"Leetcode 3 Longest Substring Without Repeating Characters 题解分析","url":"/2020/09/20/Leetcode-3-Longest-Substring-Without-Repeating-Characters-题解分析/"},{"title":"Leetcode 278 第一个错误的版本 ( First Bad Version *Easy* ) 题解分析","url":"/2022/08/14/Leetcode-278-第一个错误的版本-First-Bad-Version-Easy-题解分析/"},{"title":"2019年终总结","url":"/2020/02/01/2019年终总结/"},{"title":"Leetcode 42 接雨水 (Trapping Rain Water) 题解分析","url":"/2021/07/04/Leetcode-42-接雨水-Trapping-Rain-Water-题解分析/"},{"title":"Leetcode 349 两个数组的交集 ( Intersection of Two Arrays *Easy* ) 题解分析","url":"/2022/03/07/Leetcode-349-两个数组的交集-Intersection-of-Two-Arrays-Easy-题解分析/"},{"title":"Leetcode 48 旋转图像(Rotate Image) 题解分析","url":"/2021/05/01/Leetcode-48-旋转图像-Rotate-Image-题解分析/"},{"title":"2021 年中总结","url":"/2021/07/18/2021-年中总结/"},{"title":"Leetcode 4 寻找两个正序数组的中位数 ( Median of Two Sorted Arrays *Hard* ) 题解分析","url":"/2022/03/27/Leetcode-4-寻找两个正序数组的中位数-Median-of-Two-Sorted-Arrays-Hard-题解分析/"},{"title":"leetcode no.3","url":"/2015/04/15/Leetcode-No-3/"},{"title":"Linux 下 grep 命令的一点小技巧","url":"/2020/08/06/Linux-下-grep-命令的一点小技巧/"},{"title":"MFC 模态对话框","url":"/2014/12/24/MFC 模态对话框/"},{"title":"Maven实用小技巧","url":"/2020/02/16/Maven实用小技巧/"},{"title":"Number of 1 Bits","url":"/2015/03/11/Number-Of-1-Bits/"},{"title":"Path Sum","url":"/2015/01/04/Path-Sum/"},{"title":"Leetcode 698 划分为k个相等的子集 ( Partition to K Equal Sum Subsets *Medium* ) 题解分析","url":"/2022/06/19/Leetcode-698-划分为k个相等的子集-Partition-to-K-Equal-Sum-Subsets-Medium-题解分析/"},{"title":"Reverse Bits","url":"/2015/03/11/Reverse-Bits/"},{"title":"Leetcode 83 删除排序链表中的重复元素 ( Remove Duplicates from Sorted List *Easy* ) 题解分析","url":"/2022/03/13/Leetcode-83-删除排序链表中的重复元素-Remove-Duplicates-from-Sorted-List-Easy-题解分析/"},{"title":"Reverse Integer","url":"/2015/03/13/Reverse-Integer/"},{"title":"two sum","url":"/2015/01/14/Two-Sum/"},{"title":"ambari-summary","url":"/2017/05/09/ambari-summary/"},{"title":"docker-mysql-cluster","url":"/2016/08/14/docker-mysql-cluster/"},{"title":"docker比一般多一点的初学者介绍","url":"/2020/03/08/docker比一般多一点的初学者介绍/"},{"title":"docker比一般多一点的初学者介绍三","url":"/2020/03/21/docker比一般多一点的初学者介绍三/"},{"title":"docker比一般多一点的初学者介绍二","url":"/2020/03/15/docker比一般多一点的初学者介绍二/"},{"title":"dubbo 客户端配置的一个重要知识点","url":"/2022/06/11/dubbo-客户端配置的一个重要知识点/"},{"title":"docker使用中发现的echo命令的一个小技巧及其他","url":"/2020/03/29/echo命令的一个小技巧/"},{"title":"gogs使用webhook部署react单页应用","url":"/2020/02/22/gogs使用webhook部署react单页应用/"},{"title":"Leetcode 885 螺旋矩阵 III ( Spiral Matrix III *Medium* ) 题解分析","url":"/2022/08/23/Leetcode-885-螺旋矩阵-III-Spiral-Matrix-III-Medium-题解分析/"},{"title":"minimum-size-subarray-sum-209","url":"/2016/10/11/minimum-size-subarray-sum-209/"},{"title":"C++ 指针使用中的一个小问题","url":"/2014/12/23/my-new-post/"},{"title":"Redis_分布式锁","url":"/2019/12/10/Redis-Part-1/"},{"title":"mybatis 的 $ 和 # 是有啥区别","url":"/2020/09/06/mybatis-的-和-是有啥区别/"},{"title":"mybatis 的缓存是怎么回事","url":"/2020/10/03/mybatis-的缓存是怎么回事/"},{"title":"binary-watch","url":"/2016/09/29/binary-watch/"},{"title":"openresty","url":"/2019/06/18/openresty/"},{"title":"pcre-intro-and-a-simple-package","url":"/2015/01/16/pcre-intro-and-a-simple-package/"},{"title":"nginx 日志小记","url":"/2022/04/17/nginx-日志小记/"},{"title":"rabbitmq-tips","url":"/2017/04/25/rabbitmq-tips/"},{"title":"redis 的 rdb 和 COW 介绍","url":"/2021/08/15/redis-的-rdb-和-COW-介绍/"},{"title":"php-abstract-class-and-interface","url":"/2016/11/10/php-abstract-class-and-interface/"},{"title":"redis数据结构介绍三-第三部分 整数集合","url":"/2020/01/10/redis数据结构介绍三/"},{"title":"mybatis 的 foreach 使用的注意点","url":"/2022/07/09/mybatis-的-foreach-使用的注意点/"},{"title":"redis数据结构介绍五-第五部分 对象","url":"/2020/01/20/redis数据结构介绍五/"},{"title":"redis数据结构介绍六 快表","url":"/2020/01/22/redis数据结构介绍六/"},{"title":"redis数据结构介绍四-第四部分 压缩表","url":"/2020/01/19/redis数据结构介绍四/"},{"title":"redis淘汰策略复习","url":"/2021/08/01/redis淘汰策略复习/"},{"title":"redis数据结构介绍二-第二部分 跳表","url":"/2020/01/04/redis数据结构介绍二/"},{"title":"redis系列介绍七-过期策略","url":"/2020/04/12/redis系列介绍七/"},{"title":"redis系列介绍八-淘汰策略","url":"/2020/04/18/redis系列介绍八/"},{"title":"redis过期策略复习","url":"/2021/07/25/redis过期策略复习/"},{"title":"rust学习笔记-所有权三之切片","url":"/2021/05/16/rust学习笔记-所有权三之切片/"},{"title":"rust学习笔记-所有权二","url":"/2021/04/18/rust学习笔记-所有权二/"},{"title":"rust学习笔记-所有权一","url":"/2021/04/18/rust学习笔记/"},{"title":"spark-little-tips","url":"/2017/03/28/spark-little-tips/"},{"title":"spring event 介绍","url":"/2022/01/30/spring-event-介绍/"},{"title":"summary-ranges-228","url":"/2016/10/12/summary-ranges-228/"},{"title":"swoole-websocket-test","url":"/2016/07/13/swoole-websocket-test/"},{"title":"wordpress 忘记密码的一种解决方法","url":"/2021/12/05/wordpress-忘记密码的一种解决方法/"},{"title":"《垃圾回收算法手册读书》笔记之整理算法","url":"/2021/03/07/《垃圾回收算法手册读书》笔记之整理算法/"},{"title":"invert-binary-tree","url":"/2015/06/22/invert-binary-tree/"},{"title":"上次的其他 外行聊国足","url":"/2022/03/06/上次的其他-外行聊国足/"},{"title":"介绍一下 RocketMQ","url":"/2020/06/21/介绍一下-RocketMQ/"},{"title":"介绍下最近比较实用的端口转发","url":"/2021/11/14/介绍下最近比较实用的端口转发/"},{"title":"从丁仲礼被美国制裁聊点啥","url":"/2020/12/20/从丁仲礼被美国制裁聊点啥/"},{"title":"从清华美院学姐聊聊我们身边的恶人","url":"/2020/11/29/从清华美院学姐聊聊我们身边的恶人/"},{"title":"关于公共交通再吐个槽","url":"/2021/03/21/关于公共交通再吐个槽/"},{"title":"关于读书打卡与分享","url":"/2021/02/07/关于读书打卡与分享/"},{"title":"分享记录一下一个 git 操作方法","url":"/2022/02/06/分享记录一下一个-git-操作方法/"},{"title":"分享记录一下一个 scp 操作方法","url":"/2022/02/06/分享记录一下一个-scp-操作方法/"},{"title":"周末我在老丈人家打了天小工","url":"/2020/08/16/周末我在老丈人家打了天小工/"},{"title":"在老丈人家的小工记三","url":"/2020/09/13/在老丈人家的小工记三/"},{"title":"在老丈人家的小工记五","url":"/2020/10/18/在老丈人家的小工记五/"},{"title":"在老丈人家的小工记四","url":"/2020/09/26/在老丈人家的小工记四/"},{"title":"寄生虫观后感","url":"/2020/03/01/寄生虫观后感/"},{"title":"redis数据结构介绍-第一部分 SDS,链表,字典","url":"/2019/12/26/redis数据结构介绍/"},{"title":"我是如何走上跑步这条不归路的","url":"/2020/07/26/我是如何走上跑步这条不归路的/"},{"title":"《长安的荔枝》读后感","url":"/2022/07/17/《长安的荔枝》读后感/"},{"title":"一个 nginx 的简单记忆点","url":"/2022/08/21/一个-nginx-的简单记忆点/"},{"title":"屯菜惊魂记","url":"/2022/04/24/屯菜惊魂记/"},{"title":"聊一下 RocketMQ 的 DefaultMQPushConsumer 源码","url":"/2020/06/26/聊一下-RocketMQ-的-Consumer/"},{"title":"聊一下 RocketMQ 的 NameServer 源码","url":"/2020/07/05/聊一下-RocketMQ-的-NameServer-源码/"},{"title":"看完了扫黑风暴,聊聊感想","url":"/2021/10/24/看完了扫黑风暴-聊聊感想/"},{"title":"聊一下 RocketMQ 的消息存储之 MMAP","url":"/2021/09/04/聊一下-RocketMQ-的消息存储/"},{"title":"聊一下 RocketMQ 的消息存储三","url":"/2021/10/03/聊一下-RocketMQ-的消息存储三/"},{"title":"聊一下 RocketMQ 的消息存储二","url":"/2021/09/12/聊一下-RocketMQ-的消息存储二/"},{"title":"聊一下 RocketMQ 的消息存储四","url":"/2021/10/17/聊一下-RocketMQ-的消息存储四/"},{"title":"聊一下 RocketMQ 的顺序消息","url":"/2021/08/29/聊一下-RocketMQ-的顺序消息/"},{"title":"给小电驴上牌","url":"/2022/03/20/给小电驴上牌/"},{"title":"搬运两个 StackOverflow 上的 Mysql 编码相关的问题解答","url":"/2022/01/16/搬运两个-StackOverflow-上的-Mysql-编码相关的问题解答/"},{"title":"聊一下 SpringBoot 中动态切换数据源的方法","url":"/2021/09/26/聊一下-SpringBoot-中动态切换数据源的方法/"},{"title":"聊在东京奥运会闭幕式这天-二","url":"/2021/08/19/聊在东京奥运会闭幕式这天-二/"},{"title":"聊在东京奥运会闭幕式这天","url":"/2021/08/08/聊在东京奥运会闭幕式这天/"},{"title":"聊聊 Dubbo 的 SPI 续之自适应拓展","url":"/2020/06/06/聊聊-Dubbo-的-SPI-续之自适应拓展/"},{"title":"聊一下 SpringBoot 中使用的 cglib 作为动态代理中的一个注意点","url":"/2021/09/19/聊一下-SpringBoot-中使用的-cglib-作为动态代理中的一个注意点/"},{"title":"聊聊 Dubbo 的 SPI","url":"/2020/05/31/聊聊-Dubbo-的-SPI/"},{"title":"聊聊 Java 中绕不开的 Synchronized 关键字-二","url":"/2021/06/27/聊聊-Java-中绕不开的-Synchronized-关键字-二/"},{"title":"聊聊 Dubbo 的容错机制","url":"/2020/11/22/聊聊-Dubbo-的容错机制/"},{"title":"聊聊 Java 中绕不开的 Synchronized 关键字","url":"/2021/06/20/聊聊-Java-中绕不开的-Synchronized-关键字/"},{"title":"聊聊 Java 的 equals 和 hashCode 方法","url":"/2021/01/03/聊聊-Java-的-equals-和-hashCode-方法/"},{"title":"是何原因竟让两人深夜奔袭十公里","url":"/2022/06/05/是何原因竟让两人深夜奔袭十公里/"},{"title":"聊聊 Java 的类加载机制二","url":"/2021/06/13/聊聊-Java-的类加载机制二/"},{"title":"聊聊 Java 自带的那些*逆天*工具","url":"/2020/08/02/聊聊-Java-自带的那些逆天工具/"},{"title":"聊聊 Linux 下的 top 命令","url":"/2021/03/28/聊聊-Linux-下的-top-命令/"},{"title":"聊聊 RocketMQ 的 Broker 源码","url":"/2020/07/19/聊聊-RocketMQ-的-Broker-源码/"},{"title":"聊一下 SpringBoot 设置非 web 应用的方法","url":"/2022/07/31/聊一下-SpringBoot-设置非-web-应用的方法/"},{"title":"聊聊 Sharding-Jdbc 分库分表下的分页方案","url":"/2022/01/09/聊聊-Sharding-Jdbc-分库分表下的分页方案/"},{"title":"聊聊 dubbo 的线程池","url":"/2021/04/04/聊聊-dubbo-的线程池/"},{"title":"聊聊 mysql 的 MVCC 续篇","url":"/2020/05/02/聊聊-mysql-的-MVCC-续篇/"},{"title":"聊聊 mysql 的 MVCC 续续篇之锁分析","url":"/2020/05/10/聊聊-mysql-的-MVCC-续续篇之加锁分析/"},{"title":"聊聊 Sharding-Jdbc 的简单原理初篇","url":"/2021/12/26/聊聊-Sharding-Jdbc-的简单原理初篇/"},{"title":"聊聊 mysql 索引的一些细节","url":"/2020/12/27/聊聊-mysql-索引的一些细节/"},{"title":"聊聊 redis 缓存的应用问题","url":"/2021/01/31/聊聊-redis-缓存的应用问题/"},{"title":"聊聊Java中的单例模式","url":"/2019/12/21/聊聊Java中的单例模式/"},{"title":"聊聊 SpringBoot 自动装配","url":"/2021/07/11/聊聊SpringBoot-自动装配/"},{"title":"聊聊一次 brew update 引发的血案","url":"/2020/06/13/聊聊一次-brew-update-引发的血案/"},{"title":"聊聊传说中的 ThreadLocal","url":"/2021/05/30/聊聊传说中的-ThreadLocal/"},{"title":"聊聊厦门旅游的好与不好","url":"/2021/04/11/聊聊厦门旅游的好与不好/"},{"title":"聊聊如何识别和意识到日常生活中的各类危险","url":"/2021/06/06/聊聊如何识别和意识到日常生活中的各类危险/"},{"title":"聊聊我刚学会的应用诊断方法","url":"/2020/05/22/聊聊我刚学会的应用诊断方法/"},{"title":"聊聊我理解的分布式事务","url":"/2020/05/17/聊聊我理解的分布式事务/"},{"title":"聊聊我的远程工作体验","url":"/2022/06/26/聊聊我的远程工作体验/"},{"title":"聊聊最近平淡的生活之又聊通勤","url":"/2021/11/07/聊聊最近平淡的生活/"},{"title":"聊聊最近平淡的生活之《花束般的恋爱》观后感","url":"/2021/12/31/聊聊最近平淡的生活之《花束般的恋爱》观后感/"},{"title":"聊聊最近平淡的生活之看《神探狄仁杰》","url":"/2021/12/19/聊聊最近平淡的生活之看《神探狄仁杰》/"},{"title":"聊聊最近平淡的生活之看看老剧","url":"/2021/11/21/聊聊最近平淡的生活之看看老剧/"},{"title":"聊聊给亲戚朋友的老电脑重装系统那些事儿","url":"/2021/05/09/聊聊给亲戚朋友的老电脑重装系统那些事儿/"},{"title":"聊聊这次换车牌及其他","url":"/2022/02/20/聊聊这次换车牌及其他/"},{"title":"聊聊那些加塞狗","url":"/2021/01/17/聊聊那些加塞狗/"},{"title":"聊聊部分公交车的设计bug","url":"/2021/12/05/聊聊部分公交车的设计bug/"},{"title":"记录下 Java Stream 的一些高效操作","url":"/2022/05/15/记录下-Java-Lambda-的一些高效操作/"},{"title":"记录下 zookeeper 集群迁移和易错点","url":"/2022/05/29/记录下-zookeeper-集群迁移/"},{"title":"这周末我又在老丈人家打了天小工","url":"/2020/08/30/这周末我又在老丈人家打了天小工/"},{"title":"重看了下《蛮荒记》说说感受","url":"/2021/10/10/重看了下《蛮荒记》说说感受/"},{"title":"闲聊下乘公交的用户体验","url":"/2021/02/28/闲聊下乘公交的用户体验/"},{"title":"闲话篇-也算碰到了为老不尊和坏人变老了的典型案例","url":"/2022/05/22/闲话篇-也算碰到了为老不尊和坏人变老了的典型案例/"},{"title":"闲话篇-路遇神逻辑骑车带娃爹","url":"/2022/05/08/闲话篇-路遇神逻辑骑车带娃爹/"},{"title":"聊聊 Java 的类加载机制一","url":"/2020/11/08/聊聊-Java-的类加载机制/"},{"title":"难得的大扫除","url":"/2022/04/10/难得的大扫除/"},{"title":"聊聊 Sharding-Jdbc 的简单使用","url":"/2021/12/12/聊聊-Sharding-Jdbc-的简单使用/"},{"title":"聊聊 mysql 的 MVCC","url":"/2020/04/26/聊聊-mysql-的-MVCC/"}]
\ No newline at end of file
+[{"title":"2019年终总结","url":"/2020/02/01/2019年终总结/"},{"title":"2020 年终总结","url":"/2021/03/31/2020-年终总结/"},{"title":"2021 年中总结","url":"/2021/07/18/2021-年中总结/"},{"title":"34_Search_for_a_Range","url":"/2016/08/14/34-Search-for-a-Range/"},{"title":"AQS篇二 之 Condition 浅析笔记","url":"/2021/02/21/AQS-之-Condition-浅析笔记/"},{"title":"AQS篇一","url":"/2021/02/14/AQS篇一/"},{"title":"AbstractQueuedSynchronizer","url":"/2019/09/23/AbstractQueuedSynchronizer/"},{"title":"add-two-number","url":"/2015/04/14/Add-Two-Number/"},{"title":"Apollo 的 value 注解是怎么自动更新的","url":"/2020/11/01/Apollo-的-value-注解是怎么自动更新的/"},{"title":"Clone Graph Part I","url":"/2014/12/30/Clone-Graph-Part-I/"},{"title":"Comparator使用小记","url":"/2020/04/05/Comparator使用小记/"},{"title":"Disruptor 系列一","url":"/2022/02/13/Disruptor-系列一/"},{"title":"Disruptor 系列二","url":"/2022/02/27/Disruptor-系列二/"},{"title":"Dubbo 使用的几个记忆点","url":"/2022/04/02/Dubbo-使用的几个记忆点/"},{"title":"Filter, Interceptor, Aop, 啥, 啥, 啥? 这些都是啥?","url":"/2020/08/22/Filter-Intercepter-Aop-啥-啥-啥-这些都是啥/"},{"title":"G1收集器概述","url":"/2020/02/09/G1收集器概述/"},{"title":"JVM源码分析之G1垃圾收集器分析一","url":"/2019/12/07/JVM-G1-Part-1/"},{"title":"Leetcode 021 合并两个有序链表 ( Merge Two Sorted Lists ) 题解分析","url":"/2021/10/07/Leetcode-021-合并两个有序链表-Merge-Two-Sorted-Lists-题解分析/"},{"title":"Leetcode 028 实现 strStr() ( Implement strStr() ) 题解分析","url":"/2021/10/31/Leetcode-028-实现-strStr-Implement-strStr-题解分析/"},{"title":"Leetcode 053 最大子序和 ( Maximum Subarray ) 题解分析","url":"/2021/11/28/Leetcode-053-最大子序和-Maximum-Subarray-题解分析/"},{"title":"Leetcode 104 二叉树的最大深度(Maximum Depth of Binary Tree) 题解分析","url":"/2020/10/25/Leetcode-104-二叉树的最大深度-Maximum-Depth-of-Binary-Tree-题解分析/"},{"title":"Leetcode 105 从前序与中序遍历序列构造二叉树(Construct Binary Tree from Preorder and Inorder Traversal) 题解分析","url":"/2020/12/13/Leetcode-105-从前序与中序遍历序列构造二叉树-Construct-Binary-Tree-from-Preorder-and-Inorder-Traversal-题解分析/"},{"title":"Leetcode 1115 交替打印 FooBar ( Print FooBar Alternately *Medium* ) 题解分析","url":"/2022/05/01/Leetcode-1115-交替打印-FooBar-Print-FooBar-Alternately-Medium-题解分析/"},{"title":"Leetcode 121 买卖股票的最佳时机(Best Time to Buy and Sell Stock) 题解分析","url":"/2021/03/14/Leetcode-121-买卖股票的最佳时机-Best-Time-to-Buy-and-Sell-Stock-题解分析/"},{"title":"Leetcode 124 二叉树中的最大路径和(Binary Tree Maximum Path Sum) 题解分析","url":"/2021/01/24/Leetcode-124-二叉树中的最大路径和-Binary-Tree-Maximum-Path-Sum-题解分析/"},{"title":"Leetcode 1260 二维网格迁移 ( Shift 2D Grid *Easy* ) 题解分析","url":"/2022/07/22/Leetcode-1260-二维网格迁移-Shift-2D-Grid-Easy-题解分析/"},{"title":"Leetcode 155 最小栈(Min Stack) 题解分析","url":"/2020/12/06/Leetcode-155-最小栈-Min-Stack-题解分析/"},{"title":"Leetcode 16 最接近的三数之和 ( 3Sum Closest *Medium* ) 题解分析","url":"/2022/08/06/Leetcode-16-最接近的三数之和-3Sum-Closest-Medium-题解分析/"},{"title":"Leetcode 160 相交链表(intersection-of-two-linked-lists) 题解分析","url":"/2021/01/10/Leetcode-160-相交链表-intersection-of-two-linked-lists-题解分析/"},{"title":"Leetcode 2 Add Two Numbers 题解分析","url":"/2020/10/11/Leetcode-2-Add-Two-Numbers-题解分析/"},{"title":"Leetcode 20 有效的括号 ( Valid Parentheses *Easy* ) 题解分析","url":"/2022/07/02/Leetcode-20-有效的括号-Valid-Parentheses-Easy-题解分析/"},{"title":"Leetcode 234 回文链表(Palindrome Linked List) 题解分析","url":"/2020/11/15/Leetcode-234-回文联表-Palindrome-Linked-List-题解分析/"},{"title":"Leetcode 236 二叉树的最近公共祖先(Lowest Common Ancestor of a Binary Tree) 题解分析","url":"/2021/05/23/Leetcode-236-二叉树的最近公共祖先-Lowest-Common-Ancestor-of-a-Binary-Tree-题解分析/"},{"title":"Leetcode 278 第一个错误的版本 ( First Bad Version *Easy* ) 题解分析","url":"/2022/08/14/Leetcode-278-第一个错误的版本-First-Bad-Version-Easy-题解分析/"},{"title":"Leetcode 3 Longest Substring Without Repeating Characters 题解分析","url":"/2020/09/20/Leetcode-3-Longest-Substring-Without-Repeating-Characters-题解分析/"},{"title":"Leetcode 349 两个数组的交集 ( Intersection of Two Arrays *Easy* ) 题解分析","url":"/2022/03/07/Leetcode-349-两个数组的交集-Intersection-of-Two-Arrays-Easy-题解分析/"},{"title":"Leetcode 4 寻找两个正序数组的中位数 ( Median of Two Sorted Arrays *Hard* ) 题解分析","url":"/2022/03/27/Leetcode-4-寻找两个正序数组的中位数-Median-of-Two-Sorted-Arrays-Hard-题解分析/"},{"title":"Leetcode 42 接雨水 (Trapping Rain Water) 题解分析","url":"/2021/07/04/Leetcode-42-接雨水-Trapping-Rain-Water-题解分析/"},{"title":"Leetcode 48 旋转图像(Rotate Image) 题解分析","url":"/2021/05/01/Leetcode-48-旋转图像-Rotate-Image-题解分析/"},{"title":"Leetcode 698 划分为k个相等的子集 ( Partition to K Equal Sum Subsets *Medium* ) 题解分析","url":"/2022/06/19/Leetcode-698-划分为k个相等的子集-Partition-to-K-Equal-Sum-Subsets-Medium-题解分析/"},{"title":"Leetcode 83 删除排序链表中的重复元素 ( Remove Duplicates from Sorted List *Easy* ) 题解分析","url":"/2022/03/13/Leetcode-83-删除排序链表中的重复元素-Remove-Duplicates-from-Sorted-List-Easy-题解分析/"},{"title":"Leetcode 885 螺旋矩阵 III ( Spiral Matrix III *Medium* ) 题解分析","url":"/2022/08/23/Leetcode-885-螺旋矩阵-III-Spiral-Matrix-III-Medium-题解分析/"},{"title":"leetcode no.3","url":"/2015/04/15/Leetcode-No-3/"},{"title":"Linux 下 grep 命令的一点小技巧","url":"/2020/08/06/Linux-下-grep-命令的一点小技巧/"},{"title":"MFC 模态对话框","url":"/2014/12/24/MFC 模态对话框/"},{"title":"Maven实用小技巧","url":"/2020/02/16/Maven实用小技巧/"},{"title":"Number of 1 Bits","url":"/2015/03/11/Number-Of-1-Bits/"},{"title":"Path Sum","url":"/2015/01/04/Path-Sum/"},{"title":"Redis_分布式锁","url":"/2019/12/10/Redis-Part-1/"},{"title":"Reverse Bits","url":"/2015/03/11/Reverse-Bits/"},{"title":"Reverse Integer","url":"/2015/03/13/Reverse-Integer/"},{"title":"two sum","url":"/2015/01/14/Two-Sum/"},{"title":"ambari-summary","url":"/2017/05/09/ambari-summary/"},{"title":"binary-watch","url":"/2016/09/29/binary-watch/"},{"title":"docker-mysql-cluster","url":"/2016/08/14/docker-mysql-cluster/"},{"title":"docker比一般多一点的初学者介绍","url":"/2020/03/08/docker比一般多一点的初学者介绍/"},{"title":"docker比一般多一点的初学者介绍三","url":"/2020/03/21/docker比一般多一点的初学者介绍三/"},{"title":"docker比一般多一点的初学者介绍二","url":"/2020/03/15/docker比一般多一点的初学者介绍二/"},{"title":"dubbo 客户端配置的一个重要知识点","url":"/2022/06/11/dubbo-客户端配置的一个重要知识点/"},{"title":"docker使用中发现的echo命令的一个小技巧及其他","url":"/2020/03/29/echo命令的一个小技巧/"},{"title":"gogs使用webhook部署react单页应用","url":"/2020/02/22/gogs使用webhook部署react单页应用/"},{"title":"invert-binary-tree","url":"/2015/06/22/invert-binary-tree/"},{"title":"minimum-size-subarray-sum-209","url":"/2016/10/11/minimum-size-subarray-sum-209/"},{"title":"C++ 指针使用中的一个小问题","url":"/2014/12/23/my-new-post/"},{"title":"mybatis 的 foreach 使用的注意点","url":"/2022/07/09/mybatis-的-foreach-使用的注意点/"},{"title":"mybatis 的 $ 和 # 是有啥区别","url":"/2020/09/06/mybatis-的-和-是有啥区别/"},{"title":"mybatis 的缓存是怎么回事","url":"/2020/10/03/mybatis-的缓存是怎么回事/"},{"title":"nginx 日志小记","url":"/2022/04/17/nginx-日志小记/"},{"title":"openresty","url":"/2019/06/18/openresty/"},{"title":"pcre-intro-and-a-simple-package","url":"/2015/01/16/pcre-intro-and-a-simple-package/"},{"title":"php-abstract-class-and-interface","url":"/2016/11/10/php-abstract-class-and-interface/"},{"title":"rabbitmq-tips","url":"/2017/04/25/rabbitmq-tips/"},{"title":"redis 的 rdb 和 COW 介绍","url":"/2021/08/15/redis-的-rdb-和-COW-介绍/"},{"title":"redis数据结构介绍-第一部分 SDS,链表,字典","url":"/2019/12/26/redis数据结构介绍/"},{"title":"redis数据结构介绍三-第三部分 整数集合","url":"/2020/01/10/redis数据结构介绍三/"},{"title":"redis数据结构介绍二-第二部分 跳表","url":"/2020/01/04/redis数据结构介绍二/"},{"title":"redis数据结构介绍五-第五部分 对象","url":"/2020/01/20/redis数据结构介绍五/"},{"title":"redis数据结构介绍六 快表","url":"/2020/01/22/redis数据结构介绍六/"},{"title":"redis数据结构介绍四-第四部分 压缩表","url":"/2020/01/19/redis数据结构介绍四/"},{"title":"redis淘汰策略复习","url":"/2021/08/01/redis淘汰策略复习/"},{"title":"redis系列介绍七-过期策略","url":"/2020/04/12/redis系列介绍七/"},{"title":"redis系列介绍八-淘汰策略","url":"/2020/04/18/redis系列介绍八/"},{"title":"redis过期策略复习","url":"/2021/07/25/redis过期策略复习/"},{"title":"rust学习笔记-所有权三之切片","url":"/2021/05/16/rust学习笔记-所有权三之切片/"},{"title":"rust学习笔记-所有权一","url":"/2021/04/18/rust学习笔记/"},{"title":"rust学习笔记-所有权二","url":"/2021/04/18/rust学习笔记-所有权二/"},{"title":"spark-little-tips","url":"/2017/03/28/spark-little-tips/"},{"title":"spring event 介绍","url":"/2022/01/30/spring-event-介绍/"},{"title":"summary-ranges-228","url":"/2016/10/12/summary-ranges-228/"},{"title":"swoole-websocket-test","url":"/2016/07/13/swoole-websocket-test/"},{"title":"wordpress 忘记密码的一种解决方法","url":"/2021/12/05/wordpress-忘记密码的一种解决方法/"},{"title":"《垃圾回收算法手册读书》笔记之整理算法","url":"/2021/03/07/《垃圾回收算法手册读书》笔记之整理算法/"},{"title":"《长安的荔枝》读后感","url":"/2022/07/17/《长安的荔枝》读后感/"},{"title":"村上春树《1Q84》读后感","url":"/2019/12/18/1Q84读后感/"},{"title":"上次的其他 外行聊国足","url":"/2022/03/06/上次的其他-外行聊国足/"},{"title":"一个 nginx 的简单记忆点","url":"/2022/08/21/一个-nginx-的简单记忆点/"},{"title":"介绍一下 RocketMQ","url":"/2020/06/21/介绍一下-RocketMQ/"},{"title":"从丁仲礼被美国制裁聊点啥","url":"/2020/12/20/从丁仲礼被美国制裁聊点啥/"},{"title":"从清华美院学姐聊聊我们身边的恶人","url":"/2020/11/29/从清华美院学姐聊聊我们身边的恶人/"},{"title":"关于公共交通再吐个槽","url":"/2021/03/21/关于公共交通再吐个槽/"},{"title":"关于读书打卡与分享","url":"/2021/02/07/关于读书打卡与分享/"},{"title":"分享记录一下一个 git 操作方法","url":"/2022/02/06/分享记录一下一个-git-操作方法/"},{"title":"分享记录一下一个 scp 操作方法","url":"/2022/02/06/分享记录一下一个-scp-操作方法/"},{"title":"2021 年终总结","url":"/2022/01/22/2021-年终总结/"},{"title":"在老丈人家的小工记三","url":"/2020/09/13/在老丈人家的小工记三/"},{"title":"在老丈人家的小工记五","url":"/2020/10/18/在老丈人家的小工记五/"},{"title":"在老丈人家的小工记四","url":"/2020/09/26/在老丈人家的小工记四/"},{"title":"寄生虫观后感","url":"/2020/03/01/寄生虫观后感/"},{"title":"2020年中总结","url":"/2020/07/11/2020年中总结/"},{"title":"我是如何走上跑步这条不归路的","url":"/2020/07/26/我是如何走上跑步这条不归路的/"},{"title":"搬运两个 StackOverflow 上的 Mysql 编码相关的问题解答","url":"/2022/01/16/搬运两个-StackOverflow-上的-Mysql-编码相关的问题解答/"},{"title":"屯菜惊魂记","url":"/2022/04/24/屯菜惊魂记/"},{"title":"看完了扫黑风暴,聊聊感想","url":"/2021/10/24/看完了扫黑风暴-聊聊感想/"},{"title":"是何原因竟让两人深夜奔袭十公里","url":"/2022/06/05/是何原因竟让两人深夜奔袭十公里/"},{"title":"聊一下 RocketMQ 的 DefaultMQPushConsumer 源码","url":"/2020/06/26/聊一下-RocketMQ-的-Consumer/"},{"title":"聊一下 RocketMQ 的 NameServer 源码","url":"/2020/07/05/聊一下-RocketMQ-的-NameServer-源码/"},{"title":"给小电驴上牌","url":"/2022/03/20/给小电驴上牌/"},{"title":"聊一下 RocketMQ 的消息存储三","url":"/2021/10/03/聊一下-RocketMQ-的消息存储三/"},{"title":"聊一下 RocketMQ 的消息存储二","url":"/2021/09/12/聊一下-RocketMQ-的消息存储二/"},{"title":"聊一下 RocketMQ 的消息存储四","url":"/2021/10/17/聊一下-RocketMQ-的消息存储四/"},{"title":"聊一下 RocketMQ 的顺序消息","url":"/2021/08/29/聊一下-RocketMQ-的顺序消息/"},{"title":"聊一下 SpringBoot 中使用的 cglib 作为动态代理中的一个注意点","url":"/2021/09/19/聊一下-SpringBoot-中使用的-cglib-作为动态代理中的一个注意点/"},{"title":"聊一下 SpringBoot 中动态切换数据源的方法","url":"/2021/09/26/聊一下-SpringBoot-中动态切换数据源的方法/"},{"title":"介绍下最近比较实用的端口转发","url":"/2021/11/14/介绍下最近比较实用的端口转发/"},{"title":"聊在东京奥运会闭幕式这天-二","url":"/2021/08/19/聊在东京奥运会闭幕式这天-二/"},{"title":"聊在东京奥运会闭幕式这天","url":"/2021/08/08/聊在东京奥运会闭幕式这天/"},{"title":"聊聊 Dubbo 的 SPI 续之自适应拓展","url":"/2020/06/06/聊聊-Dubbo-的-SPI-续之自适应拓展/"},{"title":"聊聊 Dubbo 的 SPI","url":"/2020/05/31/聊聊-Dubbo-的-SPI/"},{"title":"聊聊 Dubbo 的容错机制","url":"/2020/11/22/聊聊-Dubbo-的容错机制/"},{"title":"聊聊 Java 中绕不开的 Synchronized 关键字-二","url":"/2021/06/27/聊聊-Java-中绕不开的-Synchronized-关键字-二/"},{"title":"聊聊 Java 中绕不开的 Synchronized 关键字","url":"/2021/06/20/聊聊-Java-中绕不开的-Synchronized-关键字/"},{"title":"聊聊 Java 的 equals 和 hashCode 方法","url":"/2021/01/03/聊聊-Java-的-equals-和-hashCode-方法/"},{"title":"聊聊 Java 的类加载机制一","url":"/2020/11/08/聊聊-Java-的类加载机制/"},{"title":"聊聊 Java 的类加载机制二","url":"/2021/06/13/聊聊-Java-的类加载机制二/"},{"title":"聊聊 Java 自带的那些*逆天*工具","url":"/2020/08/02/聊聊-Java-自带的那些逆天工具/"},{"title":"聊聊 Linux 下的 top 命令","url":"/2021/03/28/聊聊-Linux-下的-top-命令/"},{"title":"聊聊 RocketMQ 的 Broker 源码","url":"/2020/07/19/聊聊-RocketMQ-的-Broker-源码/"},{"title":"聊一下 SpringBoot 设置非 web 应用的方法","url":"/2022/07/31/聊一下-SpringBoot-设置非-web-应用的方法/"},{"title":"聊聊 Sharding-Jdbc 的简单使用","url":"/2021/12/12/聊聊-Sharding-Jdbc-的简单使用/"},{"title":"周末我在老丈人家打了天小工","url":"/2020/08/16/周末我在老丈人家打了天小工/"},{"title":"聊聊 dubbo 的线程池","url":"/2021/04/04/聊聊-dubbo-的线程池/"},{"title":"聊聊 mysql 的 MVCC 续篇","url":"/2020/05/02/聊聊-mysql-的-MVCC-续篇/"},{"title":"聊聊 mysql 的 MVCC 续续篇之锁分析","url":"/2020/05/10/聊聊-mysql-的-MVCC-续续篇之加锁分析/"},{"title":"聊聊 mysql 的 MVCC","url":"/2020/04/26/聊聊-mysql-的-MVCC/"},{"title":"聊聊 Sharding-Jdbc 分库分表下的分页方案","url":"/2022/01/09/聊聊-Sharding-Jdbc-分库分表下的分页方案/"},{"title":"聊聊 mysql 索引的一些细节","url":"/2020/12/27/聊聊-mysql-索引的一些细节/"},{"title":"聊聊 redis 缓存的应用问题","url":"/2021/01/31/聊聊-redis-缓存的应用问题/"},{"title":"聊聊Java中的单例模式","url":"/2019/12/21/聊聊Java中的单例模式/"},{"title":"聊聊一次 brew update 引发的血案","url":"/2020/06/13/聊聊一次-brew-update-引发的血案/"},{"title":"聊聊传说中的 ThreadLocal","url":"/2021/05/30/聊聊传说中的-ThreadLocal/"},{"title":"聊聊厦门旅游的好与不好","url":"/2021/04/11/聊聊厦门旅游的好与不好/"},{"title":"聊聊如何识别和意识到日常生活中的各类危险","url":"/2021/06/06/聊聊如何识别和意识到日常生活中的各类危险/"},{"title":"聊聊我刚学会的应用诊断方法","url":"/2020/05/22/聊聊我刚学会的应用诊断方法/"},{"title":"聊聊我理解的分布式事务","url":"/2020/05/17/聊聊我理解的分布式事务/"},{"title":"聊聊我的远程工作体验","url":"/2022/06/26/聊聊我的远程工作体验/"},{"title":"聊聊最近平淡的生活之又聊通勤","url":"/2021/11/07/聊聊最近平淡的生活/"},{"title":"聊聊 Sharding-Jdbc 的简单原理初篇","url":"/2021/12/26/聊聊-Sharding-Jdbc-的简单原理初篇/"},{"title":"聊聊最近平淡的生活之看《神探狄仁杰》","url":"/2021/12/19/聊聊最近平淡的生活之看《神探狄仁杰》/"},{"title":"聊聊最近平淡的生活之看看老剧","url":"/2021/11/21/聊聊最近平淡的生活之看看老剧/"},{"title":"聊聊最近平淡的生活之《花束般的恋爱》观后感","url":"/2021/12/31/聊聊最近平淡的生活之《花束般的恋爱》观后感/"},{"title":"聊聊这次换车牌及其他","url":"/2022/02/20/聊聊这次换车牌及其他/"},{"title":"聊聊那些加塞狗","url":"/2021/01/17/聊聊那些加塞狗/"},{"title":"聊聊部分公交车的设计bug","url":"/2021/12/05/聊聊部分公交车的设计bug/"},{"title":"记录下 Java Stream 的一些高效操作","url":"/2022/05/15/记录下-Java-Lambda-的一些高效操作/"},{"title":"记录下 zookeeper 集群迁移和易错点","url":"/2022/05/29/记录下-zookeeper-集群迁移/"},{"title":"这周末我又在老丈人家打了天小工","url":"/2020/08/30/这周末我又在老丈人家打了天小工/"},{"title":"重看了下《蛮荒记》说说感受","url":"/2021/10/10/重看了下《蛮荒记》说说感受/"},{"title":"闲聊下乘公交的用户体验","url":"/2021/02/28/闲聊下乘公交的用户体验/"},{"title":"闲话篇-也算碰到了为老不尊和坏人变老了的典型案例","url":"/2022/05/22/闲话篇-也算碰到了为老不尊和坏人变老了的典型案例/"},{"title":"闲话篇-路遇神逻辑骑车带娃爹","url":"/2022/05/08/闲话篇-路遇神逻辑骑车带娃爹/"},{"title":"难得的大扫除","url":"/2022/04/10/难得的大扫除/"},{"title":"聊一下 RocketMQ 的消息存储之 MMAP","url":"/2021/09/04/聊一下-RocketMQ-的消息存储/"},{"title":"聊聊 SpringBoot 自动装配","url":"/2021/07/11/聊聊SpringBoot-自动装配/"},{"title":"聊聊给亲戚朋友的老电脑重装系统那些事儿","url":"/2021/05/09/聊聊给亲戚朋友的老电脑重装系统那些事儿/"}]
\ No newline at end of file
diff --git a/search.xml b/search.xml
index c94061a8c5..bad70a8e62 100644
--- a/search.xml
+++ b/search.xml
@@ -1,23 +1,32 @@
- 村上春树《1Q84》读后感
- /2019/12/18/1Q84%E8%AF%BB%E5%90%8E%E6%84%9F/
- 看完了村上春树的《1Q84》,这应该是第五本看的他的书了,继 跑步,挪威的森林,刺杀骑士团长,海边的卡夫卡之后,不是其中最长的,好像是海边的卡夫卡还是刺杀骑士团长比较长一点,都是在微信读书上看的,比较方便,最开始在上面看的是高晓松的《鱼羊野史》,不知道为啥取这个名字,但是还是满吸引我的,不过由于去年的种种,没有很多心思把它看完,而且本身的组织形式就是比较松散的,看到哪算哪,其实一些野史部分是我比较喜欢,有些谈到人物的就不太有兴趣,而且类似于大祥哥吃的东西,反正都是哇,怎么这么好吃,嗯,太爱(niu)你(bi)了,高晓松就是这个人是我最喜欢的 xxx 家,我也没去细究过他有没有说重复过,反正是不太爱,后来因为这书还一度对战争史有了浓厚的兴趣,然而事实告诉我,大部头的战争史,其实正史我是真的啃不下去,我可能只对其中 10%的内容感兴趣,不过终于也在今年把它看完了,好像高晓松的晓说也最终季了,貌似其中讲朝鲜战争的还被和谐了,看样子是说出了一些故事(truth)。
-本来只是想把 《1Q84》的读后感写下,现在觉得还是把这篇当成我今年的读书总结吧,不过先从《1Q84》说起。
-严格来讲,这不是很书面化的读后感,可能我想写的也只是像聊天一样的说下我读过的书,包括的技术博客其实也是类似的,以后或许会转变,但是目前水平如此吧,写多了可能会变好,也可能不会。
-开始正文吧,这书有点类似于海边的卡夫卡,一开始是通过两条故事线,穿插着叙述,一条是青豆的,不算是个职业杀手的女杀手,要去解决一个经常家暴的斯文败类,穿着描述得比较性感吧,杀人方式是通过比较长的细针,从脖子后面一个精巧的位置插入,可以造成是未知原因死亡的假象,可能会推断成心梗之类的,这里有个前置的细节,就是青豆是乘坐一辆很高级的出租车,内饰什么的都非常有质感,有点不像一辆出租车,然后车里放了一首比较小众的歌,雅纳切克的《小交响曲》,但是青豆知道它,这跟后面的情节也有些许关系,这是女主人公青豆的出场;相应的男主的出场印象不是太深刻,男主叫天吾,是个不知名的作家,跟一个叫小松的编辑有比较好的关系,虽然天吾还没有拿到比较有分量的奖项,但是小松很看好他,也让他帮忙审校一个新作家奖的投稿文章,虽然天吾自身还没获得过这个奖,天吾还有个正式工作,是当数学老师,天吾在学生时代是个数学天才,但后面有对文学产生了兴趣,文学还不足以养活自己,靠着教课还是能保持温饱;
-接下来是正式故事的起点了,就是小松收到了一部小说投稿,名叫《空气蛹》,是个叫深绘里的女孩子投的稿,小松对他赋予了很高的评价,这里好像记岔了,好像是天吾对这部小说很有好感,但是小松比较怀疑,然后小松看了之后也有了浓厚的兴趣,这里就是开端了,小松想让天吾来重写润色这部《空气蛹》,因为故事本身很有分量,但是描写手法叙事方式等都很拙劣,而天吾正好擅长这个,小松对天吾的评价是,描写技巧无可挑剔,就是故事主体的火花还没际遇迸发,需要一个导火索,这个就可以类比我们程序员,很多比较初中级的程序员主要擅长在原来的代码上修修改改或者给他分配个小功能,比较高级的程序员就需要能做一些项目的架构设计,核心的技术方案设计,以前我也觉得写文档这个比较无聊,但是当一个项目真的比较庞大,复杂的时候,整体和核心部分的架构设计和方案还是需要有文档沉淀的,不然别人不知道没法接受,自己过段时间也会忘记。
-对于小松的这个建议,他的初衷是想搅一搅这个死气沉沉套路颇深的文坛,因为本身《空气蛹》这部小说的内容很吸引人,小松想通过天吾的润色补充让这部小说冲击新人奖,有种恶作剧的意图,天吾对此表示很多担心和顾虑,小松的这个建议其实也是一种文学作假,有两方面的担心,一方面是原作者深绘里是否同意如此操作,一方面是外界如果发现了这个事实会有什么样的后果,但是小松表示不用担心,前一步由小松牵线,让天吾跟原作者深绘里当面沟通这个代写是否被允许,结果当然是被允许了,这里有了对深绘里的初步描写,按我的理解是比较仙的感觉,然后语言沟通有些吃力,或者说有她自己的特色,当面沟通时貌似是让深绘里回去再考虑下,然后后面再由天吾去深绘里寄宿的戎野老师家沟通具体的细节。
-2019年12月18日23:37:19 更新 去到戎野老师家之后,天吾知道了关于深绘里的一些事情,深绘里的父亲与戎野老师应该是老友,深绘里的父亲在当初成立了一个叫”先驱”的公社,一个独立运行的社会组织,以运营农场作为物资来源,追求更为松散的共同体,即不过分激进地公有制,进行松散的共同生活,承认私有财产,简而言之就是这样一个能稳定存活下来的独立社会组织,但是随着稳定运行,内部的激进派和稳健派开始出现分歧,不可磨合,后来两派就分裂了,深绘里的父亲,深田保留在了稳健派,但是此时其实深田保内心是矛盾的,以为一开始其实是他倡导的独立革命才组织起了这群人,然而现在他又认清了现实社会已经不太相信能通过革命来独立的可能性,后来激进派便开始越加封闭,而且进行军事训练和思想教育,而后这个先驱的激进派别便有了新的名字”黎明”,深绘里也是在此时从先驱逃离来投靠戎野老师 暂时先写到这,未完待续~
+ 2019年终总结
+ /2020/02/01/2019%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/
+ 今天是农历初八了,年前一个月的时候就准备做下今年的年终总结,可是写了一点觉得太情绪化了,希望后面写个平淡点的,正好最近技术方面还没有看到一个完整成文的内容,就来写一下这一年的总结,尽量少写一点太情绪化的东西。
+跳槽 年初换了个公司,也算换了个环境,跟前公司不太一样,做的事情方向也不同,可能是侧重点不同,一开始有些不适应,主要是压力上,会觉得压力比较大,但是总体来说与人相处的部分还是不错的,做的技术方向还是Java,这里也感谢前东家让我有机会转了Java,个人感觉杭州整个市场还是Java比较有优势,不过在开始的时候总觉得对Java有点不适应,应该值得深究的东西还是很多的,而且对于面试来说,也是有很多可以问的,后面慢慢发现除开某里等一线超一线互联网公司之外,大部分的面试还是有大概的套路跟大纲的,不过更细致的则因人而异了,面试有时候也还看缘分,面试官关注的点跟应试者比较契合的话就很容易通过面试,不然的话总会有能刁难或者理性化地说比较难回答的问题。这个后面可以单独说一下,先按下不表。 刚进公司没多久就负责比较重要的项目,工期也比较紧张,整体来说那段时间的压力的确是比较大的,不过总算最后结果不坏,这里应该说对一些原来在前东家都是掌握的不太好的部分,比如maven,其实maven对于java程序员来说还是很重要的,但是我碰到过的面试基本没问过这个,我自己也在后面的面试中没问过相关的,不知道咋问,比如dependence分析、冲突解决,比如对bean的理解,这个算是我一直以来的疑问点,因为以前刚开始学Java学spring,上来就是bean,但是bean到底是啥,IOC是啥,可能网上的文章跟大多数书籍跟我的理解思路不太match,导致一直不能很好的理解这玩意,到后面才理解,要理解这个bean,需要有两个基本概念,一个是面向对象,一个是对象容器跟依赖反转,还是只说到这,后面可以有专题说一下,总之自认为技术上有了不小的长进了,方向上应该是偏实用的。这个重要的项目完成后慢慢能喘口气了,后面也有一些比较紧急且工作量大的,不过在我TL的帮助下还是能尽量协调好资源。
+面试 后面因为项目比较多,缺少开发,所以也参与帮忙做一些面试,这里总体感觉是面的候选人还是比较多样的,有些工作了蛮多年但是一些基础问题回答的不好,有些还是在校学生,但是面试技巧不错,针对常见的面试题都有不错的准备,不过还是觉得光靠这些面试题不能完全说明问题,真正工作了需要的是解决问题的人,而不是会背题的,退一步来说能好好准备面试还是比较重要的,也是双向选择中的基本尊重,印象比较深刻的是参加了去杭州某高校的校招面试,感觉参加校招的同学还是很多的,大部分是20年将毕业的研究生,挺多都是基础很扎实,对比起我刚要毕业时还是很汗颜,挺多来面试的同学都非常不错,那天强度也很大,从下午到那开始一直面到六七点,在这祝福那些来面试的同学,也都不容易的,能找到心仪的工作。
+技术方向 这一年前大半部分还是比较焦虑不能恢复那种主动找时间学习的状态,可能换了公司是主要的原因,初期有个适应的过程也比较正常,总体来说可能是到九十月份开始慢慢有所改善,对这些方面有学习了下,
+
+spring方向,spring真的是个庞然大物,但是还是要先抓住根本,慢慢发散去了解其他的细节,抓住bean的生命周期,当然也不是死记硬背,让我一个个背下来我也不行,但是知道它究竟是干嘛的,有啥用,并且在工作中能用起来是最重要的
+mysql数据库,这部分主要是关注了mvcc,知道了个大概,源码实现细节还没具体研究,有时间可以来个专题(一大堆待写的内容)
+java的一些源码,比如aqs这种,结合文章看了下源码,一开始总感觉静不下心来看,然后有一次被LD刺激了下就看完了,包括conditionObject等
+redis的源码,这里包括了Redis分布式锁和redis的数据结构源码,已经写成文章,不过比较着急成文,所以质量不是特别好,希望后面再来补补
+jvm源码,这部分正好是想了解下g1收集器,大概把周志明的书看完了,但是还没完整的理解掌握,还有就是g1收集器的部分,一是概念部分大概理解了,后面是就是想从源码层面去学习理解,这也是新一年的主要计划
+mq的部分是了解了zero copy,sendfile等,跟消息队列主题关系不大🤦♂️ 这么看还是学了点东西的,希望新一年再接再厉。
+
+生活 住的地方没变化,主要是周边设施比较方便,暂时没找到更好的就没打算换,主要的问题是没电梯,一开始没觉得有啥,真正住起来还是觉得比较累的,希望后面租的可以有电梯,或者楼层低一点,还有就是要通下水道,第一次让师傅上门,花了两百大洋,后来自学成才了,让师傅通了一次才撑了一个月就不行了,后面自己通的差不多可以撑半年,还是比较有成就感的😀,然后就是跑步了,年初的时候去了紫金港跑步,后面因为工作的原因没去了,但是公司的跑步机倒是让我重拾起这个唯一的运动健身项目,后面因为肠胃问题,体重也需要控制,所以就周末回来也在家这边坚持跑步,下半年的话基本保持每周一次以上,比较那些跑马拉松的大牛还是差距很大,不过也是突破自我了,有一次跑了12公里,最远的距离,而且后面感觉跑十公里也不是特别吃不消了,这一年达成了300公里的目标,体重也稍有下降,比较满意的结果。
+期待 希望工作方面技术方面能有所长进,生活上能多点时间陪家人,继续跑步减肥,家人健健康康的,嗯
]]>
生活
- 读后感
- 村上春树
+ 年终总结
+ 2019
- 读后感
+ 生活
+ 年终总结
+ 2019
@@ -46,39 +55,26 @@
- 2020年中总结
- /2020/07/11/2020%E5%B9%B4%E4%B8%AD%E6%80%BB%E7%BB%93/
- 很快2020 年就过了一半了,而且是今年这么特殊的一年,很多事情都发生的出乎意料,疫情这个绕不过去的话题,之前写了点比较愤青的文字,感觉不太适合发出来就烂在草稿箱里吧,这个目前一大影响估计是今年都没办法完全摘下口罩了,前面几个月来回杭州都开车,因为彭埠大桥不通行了,实在是非常不方便,每条路都灰常堵,心累,吐槽下杭州的交通规划和交警同志,工作实在做的不咋地。
-另外一件是就是蜗壳,从前不知道黝黑蜗壳是啥意思,只是经常会在他的视频里看到,大学的时候在缘网下了一个集锦,炒鸡帅气,各种空接扣篮,越来越能明白那句“你永远不知道意外和明天不知道哪个会先来,且行且珍惜”的含义,只是听了很多道理,依然活不好这一生,知易行难,王阳明真的是这方面的大师,有空可以看看这方面的书,一直想写写我跟篮球跟蜗壳的这十几年,争取能早日写好吧,不过得找个静得下来的时候写。
-正事方面上半年还是挺让人失望的,没有达成一些目标,应该还是能力不足吧,技术方面分析一下还是停留在看的表面层,有些实操的,或者结合业务场景的能力不太行,算是在坚持写写 blog,主要是被这个每周一篇的目标推着走,有时会比较焦虑,内容产出也还比较差,希望能在后面有些改善,可能会降低频率,只是觉得降低了也不一定能有比较好的提升,无法战胜自己的惰性,所以暂时还是坚持下这个目标吧,还有就是 coding 能力,有时候也应该刷刷题,提升思维敏捷度,大脑用太少可能生锈了,况且本来就不是很有优势,虽然失望也只能继续努力吧,日拱一卒,来日方长,加油吧~😔
-还有就是跑步减肥了,截止今天,上半年跑了 136 公里了,因为疫情影响,农历年后是从 4 月 17 号开始跑的,去年跑到了 300 公里,奖励自己了一个手表(真的挺后悔的,还不如 200 块买个手表),今年希望可以能在这个基础上再进一步,一直跟领导说,跑步算是我坚持下来的唯一一个好习惯了,618 买了个跑步机,周末回家了可以不受天气影响的多跑跑,不过如果天气好可能还是会出去跑跑,跑步机跑道多少还是有点拘束,只是感觉可能是我还是吃得太多了🤦♂️,效果不是很明显,还在 80 这个坎徘徊,等于浪费了大半年,可能是年初的项目太费心力,压力比较大,吃得更多,是不是可以算工伤😄,这方面也需要好好调整,可以放得开一点,虽然不太可能一下子到位,但是总要去努力下,随着年龄成长总要承担更多,也要看得开一点,没法事事如愿,尽力就好了,减肥这个事情还在结合一些俯卧撑啥的,希望也能坚持下去,加油吧,不知道原话怎么说的,意思是人类最大的勇敢就是看透了人世间的苦难,仍然热爱生活。我当然没可能让内心变得这么强大,试着去努力吧,奥力给!
+ 2021 年中总结
+ /2021/07/18/2021-%E5%B9%B4%E4%B8%AD%E6%80%BB%E7%BB%93/
+ 又到半年总结时,第一次写总结类型的文章感觉挺好写的,但是后面总觉得这过去的一段时间所做的事情,能力上的成长低于预期,但是是需要总结下,找找问题,顺便展望下未来。
+这一年做的最让自己满意的应该就是看了一些书,由折腾群洋总发起的读书打卡活动,到目前为止已经读完了这几本书,《cUrl 必知必会》,《古董局中局 1》,《古董局中局 2》,《算法图解》,《每天 5 分钟玩转 Kubernetes》《幸福了吗?》《高可用可伸缩微服务架构:基于 Dubbo、Spring Cloud和 Service Mesh》《Rust 权威指南》后面可以写个专题说说看的这些书,虽然每天打卡如果时间安排不好,并且看的书像 rust 这样比较难的话还是会有点小焦虑,不过也是个调整过程,一方面可以在白天就抽空看一会,然后也不必要每次都看很大一章,注重吸收。
+技术上的成长的话,有一些比较小的长进吧,对于一些之前忽视的 synchronized,ThreadLocal 和 AQS 等知识点做了下查漏补缺了,然后多了解了一些 Java 垃圾回收的内容,但是在实操上还是比较欠缺,成型的技术方案,架构上所谓的优化也比较少,一些想法也还有考虑不周全的地方,还需要多花时间和心思去学习加强,特别是在目前已经有的基础上如何做系统深层次的优化,既不要是鸡毛蒜皮的,也不能出现一些不可接受的问题和故障,这是个很重要的课题,需要好好学习,后面考虑定一些周期性目标,两个月左右能有一些成果和总结。
+另外一部分是自己的服务,因为 ucloud 的机器太贵就没续费了,所以都迁移到腾讯云的小机器上了,顺便折腾了一点点 traefik,但是还很不熟练,不太习惯这一套,一方面是 docker 还不习惯,这也加重了对这套环境的不适应,还是习惯裸机部署,另一方面就是 k8s 了,家里的机器还没虚拟化,没有很好的条件可以做实验,这也是读书打卡的一个没做好的点,整体的学习效果受限于深度和实操,后面是看都是用 traefik,也找到了一篇文章可以 traefik 转发到裸机应用,因为主仓库用的是裸机的 gogs。
+还有就是运动减肥上,唉,这又是很大的一个痛点,基本没效果,只是还算稳定,昨天看到一个视频说还需要力量训练来增肌,以此可以提升基础代谢,打算往这个方向尝试下,因为今天没有疫情限制了,在 6 月底完成了 200 公里的跑步小目标,只是有些膝盖跟大腿根外侧不适,抽空得去看下医生,后面打算每天也能做点卷腹跟俯卧撑。
+下半年还希望能继续多看看书,比很多网上各种乱七八糟的文章会好很多,结合豆瓣评分,找一些评价高一些的文章,但也不是说分稍低点的就不行,有些也看人是不是适合,一般 6 分以上评价比较多的就可以试试。
]]>
生活
年中总结
- 2020
-
-
- 生活
- 2020
- 年中总结
-
-
-
- 2021 年终总结
- /2022/01/22/2021-%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/
- 又是一年年终总结,本着极度讨厌实时需求的理念,我还是 T+N 发布这个年终总结
-工作篇 工作没什么大变化,有了些微的提升,可能因为是来了之后做了些项目对比公司与来还算是比较重要的,但是技术难度上没有特别突出的点,可能最开始用 openresty+lua 做了个 ab 测的工具,还是让我比较满意的,后面一般都是业务型的需求,今年可能在业务相关的技术逻辑上有了一些深度的了解,而原来一直想做的业务架构升级和通用型技术中间件这样的优化还是停留在想象中,前面说的 ab 测应该算是个半成品,还是没能多走出这一步,得需要多做一些实在的事情,比如轻量级的业务框架,能够对原先不熟悉的业务逻辑,代码逻辑有比较深入的理解,而不是一直都是让特定的同学负责特定的逻辑,很多时候还是在偷懒,习惯以一些简单安全的方案去做事情,在技术上还是要有所追求,还有就是能够在新语言,主要是 rust,swift 这类的能有些小玩具可以做,rust 的话是因为今年看了一本相关的书,后面三分之一其实消化得不好,这本书整体来说是很不错的,只是 rust 本身在所有权这块,还有引用包装等方面是设计得比较难懂,也可能是我基础差,所以还是想在复习下,可以做一个简单的命令行工具这种,然后 swift 是想说可以做点 mac 的小软件,原生的毕竟性能好点,又小。基于 web 做的客户端大部分都是又丑又大,极少数能好看点,但也是很重,起码 7~80M 的大小,原生的估计能除以 10。 整体的职业规划貌似陷入了比较大的困惑期,在目前公司发展前景不是很大,但是出去貌似也没有比较适合我的机会,总的来说还是杭州比较卷,个人觉得有自己的时间是非常重要的,而且这个不光是用来自我提升的,还是让自己有足够的时间做缓冲,有足够的时间锻炼减肥,时间少的情况下,不光会在仅有的时间里暴饮暴食,还没空锻炼,身体是革命的本钱,现在其实能特别明显地感觉到身体状态下滑,容易疲劳,焦虑。所以是否也许有可能以后要往外企这类的方向去发展。 工作上其实还是有个不大不小的缺点,就是容易激动,容易焦虑,前一点可能有稍稍地改观,因为工作中的很多现状其实是我个人难以改变的,即使觉得不合理,但是结构在那里,还不如自己放宽心,尽量做好事情就行。第二点的话还是做得比较差,一直以来抗压能力都比较差,跟成长环境,家庭环境都有比较大的关系,而且说实在的特别是父母,基本也没有在这方面给我正向的帮助,比较擅长给我施压,从小就是通过压力让我好好读书,当个乖学生,考个好学校,并没有能真正地理解我的压力,教我或者帮助我解压,只会在那说着不着边际的空话,甚至经常反过来对我施压。还是希望能慢慢解开,这点可能对我身体也有影响,也许需要看一些心理疏导相关的书籍。工作篇暂时到这,后续还有其他篇,未完待续哈哈😀
-]]>
-
- 生活
- 年终总结
+ 2021
生活
- 年终总结
2021
- 拖更
+ 技术
+ 读书
+ 年中总结
@@ -1108,6 +1104,59 @@ public:
environment
+
+ Clone Graph Part I
+ /2014/12/30/Clone-Graph-Part-I/
+ problemClone a graph. Input is a Node pointer. Return the Node pointer of the cloned graph.
+
+A graph is defined below:
+struct Node {
+vector neighbors;
+}
+
+
+code typedef unordered_map<Node *, Node *> Map;
+
+Node *clone(Node *graph) {
+ if (!graph) return NULL;
+
+ Map map;
+ queue<Node *> q;
+ q.push(graph);
+
+ Node *graphCopy = new Node();
+ map[graph] = graphCopy;
+
+ while (!q.empty()) {
+ Node *node = q.front();
+ q.pop();
+ int n = node->neighbors.size();
+ for (int i = 0; i < n; i++) {
+ Node *neighbor = node->neighbors[i];
+ // no copy exists
+ if (map.find(neighbor) == map.end()) {
+ Node *p = new Node();
+ map[node]->neighbors.push_back(p);
+ map[neighbor] = p;
+ q.push(neighbor);
+ } else { // a copy already exists
+ map[node]->neighbors.push_back(map[neighbor]);
+ }
+ }
+ }
+
+ return graphCopy;
+}
+anlysis using the Breadth-first traversal and use a map to save the neighbors not to be duplicated.
+]]>
+
+ leetcode
+
+
+ C++
+ leetcode
+
+
Comparator使用小记
/2020/04/05/Comparator%E4%BD%BF%E7%94%A8%E5%B0%8F%E8%AE%B0/
@@ -1209,59 +1258,6 @@ public:
nullsfirst
-
- Clone Graph Part I
- /2014/12/30/Clone-Graph-Part-I/
- problemClone a graph. Input is a Node pointer. Return the Node pointer of the cloned graph.
-
-A graph is defined below:
-struct Node {
-vector neighbors;
-}
-
-
-code typedef unordered_map<Node *, Node *> Map;
-
-Node *clone(Node *graph) {
- if (!graph) return NULL;
-
- Map map;
- queue<Node *> q;
- q.push(graph);
-
- Node *graphCopy = new Node();
- map[graph] = graphCopy;
-
- while (!q.empty()) {
- Node *node = q.front();
- q.pop();
- int n = node->neighbors.size();
- for (int i = 0; i < n; i++) {
- Node *neighbor = node->neighbors[i];
- // no copy exists
- if (map.find(neighbor) == map.end()) {
- Node *p = new Node();
- map[node]->neighbors.push_back(p);
- map[neighbor] = p;
- q.push(neighbor);
- } else { // a copy already exists
- map[node]->neighbors.push_back(map[neighbor]);
- }
- }
- }
-
- return graphCopy;
-}
-anlysis using the Breadth-first traversal and use a map to save the neighbors not to be duplicated.
-]]>
-
- leetcode
-
-
- C++
- leetcode
-
-
Disruptor 系列一
/2022/02/13/Disruptor-%E7%B3%BB%E5%88%97%E4%B8%80/
@@ -3603,6 +3599,53 @@ Output: [8,9,9,9,0,0,0,1]Lowest Common Ancestor of a Binary Tree
+
+ Leetcode 278 第一个错误的版本 ( First Bad Version *Easy* ) 题解分析
+ /2022/08/14/Leetcode-278-%E7%AC%AC%E4%B8%80%E4%B8%AA%E9%94%99%E8%AF%AF%E7%9A%84%E7%89%88%E6%9C%AC-First-Bad-Version-Easy-%E9%A2%98%E8%A7%A3%E5%88%86%E6%9E%90/
+ 题目介绍You are a product manager and currently leading a team to develop a new product. Unfortunately, the latest version of your product fails the quality check. Since each version is developed based on the previous version, all the versions after a bad version are also bad.
+Suppose you have n versions [1, 2, ..., n] and you want to find out the first bad one, which causes all the following ones to be bad.
+You are given an API bool isBadVersion(version) which returns whether version is bad. Implement a function to find the first bad version. You should minimize the number of calls to the API.
+示例 Example 1:
+Input: n = 5, bad = 4Output: 4Explanation: call isBadVersion(3) -> false call isBadVersion(5) -> true call isBadVersion(4) -> true Then 4 is the first bad version.
+
+Example 2:
+Input: n = 1, bad = 1Output: 1
+
+简析 简单来说就是一个二分查找,但是这个问题其实处理起来还是需要搞清楚一些边界问题
+代码 public int firstBadVersion ( int n) {
+
+ int left = 1 , right = n, mid;
+ while ( left < right) {
+
+ mid = left + ( right - left) / 2 ;
+
+ if ( ! isBadVersion ( mid) ) {
+ left = mid + 1 ;
+ } else {
+
+ right = mid;
+ }
+ }
+
+
+
+ return left;
+}
+
+往右移动示例 往左移动示例
+结果
+]]>
+
+ Java
+ leetcode
+
+
+ leetcode
+ java
+ 题解
+ First Bad Version
+
+
Leetcode 3 Longest Substring Without Repeating Characters 题解分析
/2020/09/20/Leetcode-3-Longest-Substring-Without-Repeating-Characters-%E9%A2%98%E8%A7%A3%E5%88%86%E6%9E%90/
@@ -3673,41 +3716,60 @@ Output: 0<
- Leetcode 278 第一个错误的版本 ( First Bad Version *Easy* ) 题解分析
- /2022/08/14/Leetcode-278-%E7%AC%AC%E4%B8%80%E4%B8%AA%E9%94%99%E8%AF%AF%E7%9A%84%E7%89%88%E6%9C%AC-First-Bad-Version-Easy-%E9%A2%98%E8%A7%A3%E5%88%86%E6%9E%90/
- 题目介绍You are a product manager and currently leading a team to develop a new product. Unfortunately, the latest version of your product fails the quality check. Since each version is developed based on the previous version, all the versions after a bad version are also bad.
-Suppose you have n versions [1, 2, ..., n] and you want to find out the first bad one, which causes all the following ones to be bad.
-You are given an API bool isBadVersion(version) which returns whether version is bad. Implement a function to find the first bad version. You should minimize the number of calls to the API.
-示例 Example 1:
-Input: n = 5, bad = 4Output: 4Explanation: call isBadVersion(3) -> false call isBadVersion(5) -> true call isBadVersion(4) -> true Then 4 is the first bad version.
+ Leetcode 349 两个数组的交集 ( Intersection of Two Arrays *Easy* ) 题解分析
+ /2022/03/07/Leetcode-349-%E4%B8%A4%E4%B8%AA%E6%95%B0%E7%BB%84%E7%9A%84%E4%BA%A4%E9%9B%86-Intersection-of-Two-Arrays-Easy-%E9%A2%98%E8%A7%A3%E5%88%86%E6%9E%90/
+ 题目介绍给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
+
+示例
+示例 1:
+输入 :nums1 = [1,2,2,1], nums2 = [2,2]输出 :[2]
-Example 2:
-Input: n = 1, bad = 1Output: 1
+
+示例 2:
+输入 :nums1 = [4,9,5], nums2 = [9,4,9,8,4]输出 :[9,4]解释 :[4,9] 也是可通过的
+
-简析 简单来说就是一个二分查找,但是这个问题其实处理起来还是需要搞清楚一些边界问题
-代码 public int firstBadVersion ( int n) {
-
- int left = 1 , right = n, mid;
- while ( left < right) {
-
- mid = left + ( right - left) / 2 ;
-
- if ( ! isBadVersion ( mid) ) {
- left = mid + 1 ;
- } else {
-
- right = mid;
+提示:
+1 <= nums1.length, nums2.length <= 1000
+0 <= nums1[i], nums2[i] <= 1000
+
+分析与题解 两个数组的交集,最简单就是两层循环了把两个都存在的找出来,不过还有个要去重的问题,稍微思考下可以使用集合 set 来处理,先把一个数组全丢进去,再对比另外一个,如果出现在第一个集合里就丢进一个新的集合,最后转换成数组,这次我稍微取了个巧,因为看到了提示里的条件,两个数组中的元素都是不大于 1000 的,所以就搞了个 1000 长度的数组,如果在第一个数组出现,就在对应的下标设置成 1,如果在第二个数组也出现了就加 1,
+code public int [ ] intersection ( int [ ] nums1, int [ ] nums2) {
+
+
+ int [ ] inter = new int [ 1000 ] ;
+ int [ ] outer;
+ int m = 0 ;
+ for ( int j : nums1) {
+
+
+ inter[ j] = 1 ;
}
- }
-
-
-
- return left;
-}
-
-往右移动示例 往左移动示例
-结果
-]]>
+ for ( int j : nums2) {
+ if ( inter[ j] > 0 ) {
+
+
+ inter[ j] += 1 ;
+ }
+ }
+ for ( int i = 0 ; i < inter. length; i++ ) {
+
+
+ if ( inter[ i] > 1 ) {
+ m++ ;
+ }
+ }
+
+ outer = new int [ m] ;
+ m = 0 ;
+ for ( int i = 0 ; i < inter. length; i++ ) {
+ if ( inter[ i] > 1 ) {
+
+ outer[ m++ ] = i;
+ }
+ }
+ return outer;
+ } ]]>
Java
leetcode
@@ -3716,36 +3778,78 @@ Output: 0<
leetcode
java
题解
- First Bad Version
+ Intersection of Two Arrays
- 2019年终总结
- /2020/02/01/2019%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/
- 今天是农历初八了,年前一个月的时候就准备做下今年的年终总结,可是写了一点觉得太情绪化了,希望后面写个平淡点的,正好最近技术方面还没有看到一个完整成文的内容,就来写一下这一年的总结,尽量少写一点太情绪化的东西。
-跳槽 年初换了个公司,也算换了个环境,跟前公司不太一样,做的事情方向也不同,可能是侧重点不同,一开始有些不适应,主要是压力上,会觉得压力比较大,但是总体来说与人相处的部分还是不错的,做的技术方向还是Java,这里也感谢前东家让我有机会转了Java,个人感觉杭州整个市场还是Java比较有优势,不过在开始的时候总觉得对Java有点不适应,应该值得深究的东西还是很多的,而且对于面试来说,也是有很多可以问的,后面慢慢发现除开某里等一线超一线互联网公司之外,大部分的面试还是有大概的套路跟大纲的,不过更细致的则因人而异了,面试有时候也还看缘分,面试官关注的点跟应试者比较契合的话就很容易通过面试,不然的话总会有能刁难或者理性化地说比较难回答的问题。这个后面可以单独说一下,先按下不表。 刚进公司没多久就负责比较重要的项目,工期也比较紧张,整体来说那段时间的压力的确是比较大的,不过总算最后结果不坏,这里应该说对一些原来在前东家都是掌握的不太好的部分,比如maven,其实maven对于java程序员来说还是很重要的,但是我碰到过的面试基本没问过这个,我自己也在后面的面试中没问过相关的,不知道咋问,比如dependence分析、冲突解决,比如对bean的理解,这个算是我一直以来的疑问点,因为以前刚开始学Java学spring,上来就是bean,但是bean到底是啥,IOC是啥,可能网上的文章跟大多数书籍跟我的理解思路不太match,导致一直不能很好的理解这玩意,到后面才理解,要理解这个bean,需要有两个基本概念,一个是面向对象,一个是对象容器跟依赖反转,还是只说到这,后面可以有专题说一下,总之自认为技术上有了不小的长进了,方向上应该是偏实用的。这个重要的项目完成后慢慢能喘口气了,后面也有一些比较紧急且工作量大的,不过在我TL的帮助下还是能尽量协调好资源。
-面试 后面因为项目比较多,缺少开发,所以也参与帮忙做一些面试,这里总体感觉是面的候选人还是比较多样的,有些工作了蛮多年但是一些基础问题回答的不好,有些还是在校学生,但是面试技巧不错,针对常见的面试题都有不错的准备,不过还是觉得光靠这些面试题不能完全说明问题,真正工作了需要的是解决问题的人,而不是会背题的,退一步来说能好好准备面试还是比较重要的,也是双向选择中的基本尊重,印象比较深刻的是参加了去杭州某高校的校招面试,感觉参加校招的同学还是很多的,大部分是20年将毕业的研究生,挺多都是基础很扎实,对比起我刚要毕业时还是很汗颜,挺多来面试的同学都非常不错,那天强度也很大,从下午到那开始一直面到六七点,在这祝福那些来面试的同学,也都不容易的,能找到心仪的工作。
-技术方向 这一年前大半部分还是比较焦虑不能恢复那种主动找时间学习的状态,可能换了公司是主要的原因,初期有个适应的过程也比较正常,总体来说可能是到九十月份开始慢慢有所改善,对这些方面有学习了下,
-
-spring方向,spring真的是个庞然大物,但是还是要先抓住根本,慢慢发散去了解其他的细节,抓住bean的生命周期,当然也不是死记硬背,让我一个个背下来我也不行,但是知道它究竟是干嘛的,有啥用,并且在工作中能用起来是最重要的
-mysql数据库,这部分主要是关注了mvcc,知道了个大概,源码实现细节还没具体研究,有时间可以来个专题(一大堆待写的内容)
-java的一些源码,比如aqs这种,结合文章看了下源码,一开始总感觉静不下心来看,然后有一次被LD刺激了下就看完了,包括conditionObject等
-redis的源码,这里包括了Redis分布式锁和redis的数据结构源码,已经写成文章,不过比较着急成文,所以质量不是特别好,希望后面再来补补
-jvm源码,这部分正好是想了解下g1收集器,大概把周志明的书看完了,但是还没完整的理解掌握,还有就是g1收集器的部分,一是概念部分大概理解了,后面是就是想从源码层面去学习理解,这也是新一年的主要计划
-mq的部分是了解了zero copy,sendfile等,跟消息队列主题关系不大🤦♂️ 这么看还是学了点东西的,希望新一年再接再厉。
-
-生活 住的地方没变化,主要是周边设施比较方便,暂时没找到更好的就没打算换,主要的问题是没电梯,一开始没觉得有啥,真正住起来还是觉得比较累的,希望后面租的可以有电梯,或者楼层低一点,还有就是要通下水道,第一次让师傅上门,花了两百大洋,后来自学成才了,让师傅通了一次才撑了一个月就不行了,后面自己通的差不多可以撑半年,还是比较有成就感的😀,然后就是跑步了,年初的时候去了紫金港跑步,后面因为工作的原因没去了,但是公司的跑步机倒是让我重拾起这个唯一的运动健身项目,后面因为肠胃问题,体重也需要控制,所以就周末回来也在家这边坚持跑步,下半年的话基本保持每周一次以上,比较那些跑马拉松的大牛还是差距很大,不过也是突破自我了,有一次跑了12公里,最远的距离,而且后面感觉跑十公里也不是特别吃不消了,这一年达成了300公里的目标,体重也稍有下降,比较满意的结果。
-期待 希望工作方面技术方面能有所长进,生活上能多点时间陪家人,继续跑步减肥,家人健健康康的,嗯
+ Leetcode 4 寻找两个正序数组的中位数 ( Median of Two Sorted Arrays *Hard* ) 题解分析
+ /2022/03/27/Leetcode-4-%E5%AF%BB%E6%89%BE%E4%B8%A4%E4%B8%AA%E6%AD%A3%E5%BA%8F%E6%95%B0%E7%BB%84%E7%9A%84%E4%B8%AD%E4%BD%8D%E6%95%B0-Median-of-Two-Sorted-Arrays-Hard-%E9%A2%98%E8%A7%A3%E5%88%86%E6%9E%90/
+ 题目介绍给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
+算法的时间复杂度应该为 O(log (m+n)) 。
+示例 1:
+输入:nums1 = [1,3], nums2 = [2] 输出:2.00000 解释:合并数组 = [1,2,3] ,中位数 2
+
+示例 2:
+输入:nums1 = [1,2], nums2 = [3,4] 输出:2.50000 解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
+
+分析与题解 这个题也是我随机出来的,之前都是随机到 easy 的,而且是序号这么靠前的,然后翻一下,之前应该是用 C++做过的,具体的方法其实可以从他的算法时间复杂度要求看出来,大概率是要二分法这种,后面就结合代码来讲了
+public double findMedianSortedArrays ( int [ ] nums1, int [ ] nums2) {
+ int n1 = nums1. length;
+ int n2 = nums2. length;
+ if ( n1 > n2) {
+ return findMedianSortedArrays ( nums2, nums1) ;
+ }
+
+
+ int k = ( n1 + n2 + 1 ) / 2 ;
+
+
+ int left = 0 ;
+ int right = n1;
+ while ( left < right) {
+
+ int m1 = left + ( right - left) / 2 ;
+
+
+
+ int m2 = k - m1;
+
+ if ( nums1[ m1] < nums2[ m2 - 1 ] ) {
+ left = m1 + 1 ;
+ } else {
+ right = m1;
+ }
+ }
+
+
+ int m1 = left;
+ int m2 = k - left;
+
+
+
+
+ int c1 = Math . max ( m1 <= 0 ? Integer . MIN_VALUE : nums1[ m1 - 1 ] , m2 <= 0 ? Integer . MIN_VALUE : nums2[ m2 - 1 ] ) ;
+
+
+
+ if ( ( n1 + n2) % 2 == 1 ) {
+ return c1;
+ }
+
+ int c2 = Math . min ( m1 >= n1 ? Integer . MAX_VALUE : nums1[ m1] , m2 >= n2 ? Integer . MAX_VALUE : nums2[ m2] ) ;
+ return ( c1 + c2) / 2.0 ;
+ }
+前面考虑的方法还是比较繁琐,考虑了两个数组的各种交叉情况,后面这个参考了一些网上的解法,代码比较简洁,但是可能不容易一下子就搞明白,所以配合了比较多的注释。
]]>
- 生活
- 年终总结
- 2019
+ Java
+ leetcode
- 生活
- 年终总结
- 2019
+ leetcode
+ java
+ 题解
+ Median of Two Sorted Arrays
@@ -3822,72 +3926,6 @@ maxR[ n -Leetcode 42
-
- Leetcode 349 两个数组的交集 ( Intersection of Two Arrays *Easy* ) 题解分析
- /2022/03/07/Leetcode-349-%E4%B8%A4%E4%B8%AA%E6%95%B0%E7%BB%84%E7%9A%84%E4%BA%A4%E9%9B%86-Intersection-of-Two-Arrays-Easy-%E9%A2%98%E8%A7%A3%E5%88%86%E6%9E%90/
- 题目介绍给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
-
-示例
-示例 1:
-输入 :nums1 = [1,2,2,1], nums2 = [2,2]输出 :[2]
-
-
-示例 2:
-输入 :nums1 = [4,9,5], nums2 = [9,4,9,8,4]输出 :[9,4]解释 :[4,9] 也是可通过的
-
-
-提示:
-1 <= nums1.length, nums2.length <= 1000
-0 <= nums1[i], nums2[i] <= 1000
-
-分析与题解 两个数组的交集,最简单就是两层循环了把两个都存在的找出来,不过还有个要去重的问题,稍微思考下可以使用集合 set 来处理,先把一个数组全丢进去,再对比另外一个,如果出现在第一个集合里就丢进一个新的集合,最后转换成数组,这次我稍微取了个巧,因为看到了提示里的条件,两个数组中的元素都是不大于 1000 的,所以就搞了个 1000 长度的数组,如果在第一个数组出现,就在对应的下标设置成 1,如果在第二个数组也出现了就加 1,
-code public int [ ] intersection ( int [ ] nums1, int [ ] nums2) {
-
-
- int [ ] inter = new int [ 1000 ] ;
- int [ ] outer;
- int m = 0 ;
- for ( int j : nums1) {
-
-
- inter[ j] = 1 ;
- }
- for ( int j : nums2) {
- if ( inter[ j] > 0 ) {
-
-
- inter[ j] += 1 ;
- }
- }
- for ( int i = 0 ; i < inter. length; i++ ) {
-
-
- if ( inter[ i] > 1 ) {
- m++ ;
- }
- }
-
- outer = new int [ m] ;
- m = 0 ;
- for ( int i = 0 ; i < inter. length; i++ ) {
- if ( inter[ i] > 1 ) {
-
- outer[ m++ ] = i;
- }
- }
- return outer;
- } ]]>
-
- Java
- leetcode
-
-
- leetcode
- java
- 题解
- Intersection of Two Arrays
-
-
Leetcode 48 旋转图像(Rotate Image) 题解分析
/2021/05/01/Leetcode-48-%E6%97%8B%E8%BD%AC%E5%9B%BE%E5%83%8F-Rotate-Image-%E9%A2%98%E8%A7%A3%E5%88%86%E6%9E%90/
@@ -3947,87 +3985,218 @@ maxR[ n -
- 2021 年中总结
- /2021/07/18/2021-%E5%B9%B4%E4%B8%AD%E6%80%BB%E7%BB%93/
- 又到半年总结时,第一次写总结类型的文章感觉挺好写的,但是后面总觉得这过去的一段时间所做的事情,能力上的成长低于预期,但是是需要总结下,找找问题,顺便展望下未来。
-这一年做的最让自己满意的应该就是看了一些书,由折腾群洋总发起的读书打卡活动,到目前为止已经读完了这几本书,《cUrl 必知必会》,《古董局中局 1》,《古董局中局 2》,《算法图解》,《每天 5 分钟玩转 Kubernetes》《幸福了吗?》《高可用可伸缩微服务架构:基于 Dubbo、Spring Cloud和 Service Mesh》《Rust 权威指南》后面可以写个专题说说看的这些书,虽然每天打卡如果时间安排不好,并且看的书像 rust 这样比较难的话还是会有点小焦虑,不过也是个调整过程,一方面可以在白天就抽空看一会,然后也不必要每次都看很大一章,注重吸收。
-技术上的成长的话,有一些比较小的长进吧,对于一些之前忽视的 synchronized,ThreadLocal 和 AQS 等知识点做了下查漏补缺了,然后多了解了一些 Java 垃圾回收的内容,但是在实操上还是比较欠缺,成型的技术方案,架构上所谓的优化也比较少,一些想法也还有考虑不周全的地方,还需要多花时间和心思去学习加强,特别是在目前已经有的基础上如何做系统深层次的优化,既不要是鸡毛蒜皮的,也不能出现一些不可接受的问题和故障,这是个很重要的课题,需要好好学习,后面考虑定一些周期性目标,两个月左右能有一些成果和总结。
-另外一部分是自己的服务,因为 ucloud 的机器太贵就没续费了,所以都迁移到腾讯云的小机器上了,顺便折腾了一点点 traefik,但是还很不熟练,不太习惯这一套,一方面是 docker 还不习惯,这也加重了对这套环境的不适应,还是习惯裸机部署,另一方面就是 k8s 了,家里的机器还没虚拟化,没有很好的条件可以做实验,这也是读书打卡的一个没做好的点,整体的学习效果受限于深度和实操,后面是看都是用 traefik,也找到了一篇文章可以 traefik 转发到裸机应用,因为主仓库用的是裸机的 gogs。
-还有就是运动减肥上,唉,这又是很大的一个痛点,基本没效果,只是还算稳定,昨天看到一个视频说还需要力量训练来增肌,以此可以提升基础代谢,打算往这个方向尝试下,因为今天没有疫情限制了,在 6 月底完成了 200 公里的跑步小目标,只是有些膝盖跟大腿根外侧不适,抽空得去看下医生,后面打算每天也能做点卷腹跟俯卧撑。
-下半年还希望能继续多看看书,比很多网上各种乱七八糟的文章会好很多,结合豆瓣评分,找一些评价高一些的文章,但也不是说分稍低点的就不行,有些也看人是不是适合,一般 6 分以上评价比较多的就可以试试。
-]]>
-
- 生活
- 年中总结
- 2021
-
-
- 生活
- 2021
- 年中总结
- 技术
- 读书
-
-
-
- Leetcode 4 寻找两个正序数组的中位数 ( Median of Two Sorted Arrays *Hard* ) 题解分析
- /2022/03/27/Leetcode-4-%E5%AF%BB%E6%89%BE%E4%B8%A4%E4%B8%AA%E6%AD%A3%E5%BA%8F%E6%95%B0%E7%BB%84%E7%9A%84%E4%B8%AD%E4%BD%8D%E6%95%B0-Median-of-Two-Sorted-Arrays-Hard-%E9%A2%98%E8%A7%A3%E5%88%86%E6%9E%90/
- 题目介绍给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
-算法的时间复杂度应该为 O(log (m+n)) 。
-示例 1:
-输入:nums1 = [1,3], nums2 = [2] 输出:2.00000 解释:合并数组 = [1,2,3] ,中位数 2
+ Leetcode 698 划分为k个相等的子集 ( Partition to K Equal Sum Subsets *Medium* ) 题解分析
+ /2022/06/19/Leetcode-698-%E5%88%92%E5%88%86%E4%B8%BAk%E4%B8%AA%E7%9B%B8%E7%AD%89%E7%9A%84%E5%AD%90%E9%9B%86-Partition-to-K-Equal-Sum-Subsets-Medium-%E9%A2%98%E8%A7%A3%E5%88%86%E6%9E%90/
+ 题目介绍Given an integer array nums and an integer k, return true if it is possible to divide this array into k non-empty subsets whose sums are all equal.
+示例 Example 1:
+
+Input: nums = [4,3,2,3,5,2,1], k = 4Output: trueExplanation: It is possible to divide it into 4 subsets (5), (1, 4), (2,3), (2,3) with equal sums.
-示例 2:
-输入:nums1 = [1,2], nums2 = [3,4] 输出:2.50000 解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
+Example 2:
+
+Input: nums = [1,2,3,4], k = 3Output: false
-分析与题解 这个题也是我随机出来的,之前都是随机到 easy 的,而且是序号这么靠前的,然后翻一下,之前应该是用 C++做过的,具体的方法其实可以从他的算法时间复杂度要求看出来,大概率是要二分法这种,后面就结合代码来讲了
-public double findMedianSortedArrays ( int [ ] nums1, int [ ] nums2) {
- int n1 = nums1. length;
- int n2 = nums2. length;
- if ( n1 > n2) {
- return findMedianSortedArrays ( nums2, nums1) ;
- }
-
-
- int k = ( n1 + n2 + 1 ) / 2 ;
-
-
- int left = 0 ;
- int right = n1;
- while ( left < right) {
-
- int m1 = left + ( right - left) / 2 ;
-
-
-
- int m2 = k - m1;
-
- if ( nums1[ m1] < nums2[ m2 - 1 ] ) {
- left = m1 + 1 ;
- } else {
- right = m1;
- }
+Constraints:
+
+1 <= k <= nums.length <= 16
+1 <= nums[i] <= 10^4
+The frequency of each element is in the range [1, 4].
+
+解析 看到这个题一开始以为挺简单,但是仔细想想问题还是挺多的,首先是分成 k 组,但是数量不限,应该需要用到回溯的方式,同时对于时间和空间复杂度也有要求,一开始这个代码是超时的,我也试了下 leetcode 上 discussion 里 vote 最高的提交也是超时的,不过看 discussion 里的帖子,貌似是后面加了一些条件,可以帮忙提高执行效率,第三条提示不太清楚意图,具体可以看下代码
+代码 public boolean canPartitionKSubsets ( int [ ] nums, int k) {
+ if ( k == 1 ) {
+ return true ;
+ }
+ int sum = 0 , n;
+ n = nums. length;
+ for ( int num : nums) {
+ sum += num;
+ }
+ if ( sum % k != 0 ) {
+ return false ;
+ }
+
+ int avg = sum / k;
+
+ Arrays . sort ( nums) ;
+
+ if ( nums[ n - 1 ] > avg) {
+ return false ;
+ }
+
+ int calculated = 0 ;
+ for ( int i = n - 1 ; i > 0 ; i-- ) {
+ if ( nums[ i] == avg) {
+ k-- ;
+ calculated++ ;
}
+ }
-
- int m1 = left;
- int m2 = k - left;
-
-
-
-
- int c1 = Math . max ( m1 <= 0 ? Integer . MIN_VALUE : nums1[ m1 - 1 ] , m2 <= 0 ? Integer . MIN_VALUE : nums2[ m2 - 1 ] ) ;
-
-
-
- if ( ( n1 + n2) % 2 == 1 ) {
- return c1;
+ int [ ] bucket = new int [ k] ;
+
+ for ( int i = 0 ; i < k; i++ ) {
+ bucket[ i] = avg;
+ }
+
+
+ if ( nums[ n - 1 ] > avg) {
+ return false ;
+ }
+
+ return backTraversal ( nums, bucket, k, n - 1 - calculated) ;
+}
+
+private boolean backTraversal ( int [ ] nums, int [ ] bucket, int k, int cur) {
+ if ( cur < 0 ) {
+ return true ;
+ }
+ for ( int i = 0 ; i < k; i++ ) {
+ if ( bucket[ i] == nums[ cur] || bucket[ i] >= nums[ cur] + nums[ 0 ] ) {
+
+
+
+
+
+
+
+
+ bucket[ i] -= nums[ cur] ;
+
+
+ if ( backTraversal ( nums, bucket, k, cur - 1 ) ) return true ;
+
+ bucket[ i] += nums[ cur] ;
}
-
- int c2 = Math . min ( m1 >= n1 ? Integer . MAX_VALUE : nums1[ m1] , m2 >= n2 ? Integer . MAX_VALUE : nums2[ m2] ) ;
- return ( c1 + c2) / 2.0 ;
- }
-前面考虑的方法还是比较繁琐,考虑了两个数组的各种交叉情况,后面这个参考了一些网上的解法,代码比较简洁,但是可能不容易一下子就搞明白,所以配合了比较多的注释。
+ }
+ return false ;
+}
+
+最后贴个图
+]]>
+
+ Java
+ leetcode
+
+
+ leetcode
+ java
+
+
+
+ Leetcode 83 删除排序链表中的重复元素 ( Remove Duplicates from Sorted List *Easy* ) 题解分析
+ /2022/03/13/Leetcode-83-%E5%88%A0%E9%99%A4%E6%8E%92%E5%BA%8F%E9%93%BE%E8%A1%A8%E4%B8%AD%E7%9A%84%E9%87%8D%E5%A4%8D%E5%85%83%E7%B4%A0-Remove-Duplicates-from-Sorted-List-Easy-%E9%A2%98%E8%A7%A3%E5%88%86%E6%9E%90/
+ 题目介绍给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。 PS:注意已排序,还有返回也要已排序
+示例 1:
+
+输入:head = [1,1,2] 输出:[1,2]
+
+示例 2:
+
+输入:head = [1,1,2,3,3] 输出:[1,2,3]
+
+提示:
+链表中节点数目在范围 [0, 300] 内
+-100 <= Node.val <= 100
+题目数据保证链表已经按 升序 排列
+
+分析与题解 这题其实是比较正常的 easy 级别的题目,链表已经排好序了,如果还带一个排序就更复杂一点, 只需要前后项做个对比,如果一致则移除后项,因为可能存在多个重复项,所以只有在前后项不同 时才会更新被比较项
+code public ListNode deleteDuplicates ( ListNode head) {
+
+ if ( head == null || head. next == null ) {
+ return head;
+ }
+ ListNode tail = head;
+
+ while ( tail. next != null ) {
+ ListNode temp = tail. next;
+
+
+ if ( temp. val == tail. val) {
+ tail. next = temp. next;
+ } else {
+
+ tail = tail. next;
+ }
+ }
+
+ return head;
+}
+链表应该是个需要反复的训练的数据结构,因为涉及到前后指针,然后更新操作,判空等, 我在这块也是掌握的不太好,需要多练习。
+]]>
+
+ Java
+ leetcode
+
+
+ leetcode
+ java
+ 题解
+ Remove Duplicates from Sorted List
+
+
+
+ Leetcode 885 螺旋矩阵 III ( Spiral Matrix III *Medium* ) 题解分析
+ /2022/08/23/Leetcode-885-%E8%9E%BA%E6%97%8B%E7%9F%A9%E9%98%B5-III-Spiral-Matrix-III-Medium-%E9%A2%98%E8%A7%A3%E5%88%86%E6%9E%90/
+ 题目介绍You start at the cell (rStart, cStart) of an rows x cols grid facing east. The northwest corner is at the first row and column in the grid, and the southeast corner is at the last row and column.
+You will walk in a clockwise spiral shape to visit every position in this grid. Whenever you move outside the grid’s boundary, we continue our walk outside the grid (but may return to the grid boundary later.). Eventually, we reach all rows * cols spaces of the grid.
+Return an array of coordinates representing the positions of the grid in the order you visited them.
+Example 1:
+Input: rows = 1, cols = 4, rStart = 0, cStart = 0Output: [[0,0],[0,1],[0,2],[0,3]]
+
+Example 2:
+Input: rows = 5, cols = 6, rStart = 1, cStart = 4Output: [[1,4],[1,5],[2,5],[2,4],[2,3],[1,3],[0,3],[0,4],[0,5],[3,5],[3,4],[3,3],[3,2],[2,2],[1,2],[0,2],[4,5],[4,4],[4,3],[4,2],[4,1],[3,1],[2,1],[1,1],[0,1],[4,0],[3,0],[2,0],[1,0],[0,0]]
+
+Constraints:
+
+1 <= rows, cols <= 100
+0 <= rStart < rows
+0 <= cStart < cols
+
+简析 这个题主要是要相同螺旋矩阵的转变方向的边界判断,已经相同步长会行进两次这个规律,写代码倒不复杂
+代码 public int [ ] [ ] spiralMatrixIII ( int rows, int cols, int rStart, int cStart) {
+ int size = rows * cols;
+ int x = rStart, y = cStart;
+
+ int [ ] [ ] matrix = new int [ size] [ 2 ] ;
+
+ matrix[ 0 ] [ 0 ] = rStart;
+ matrix[ 0 ] [ 1 ] = cStart;
+
+ int z = 1 ;
+
+ int a = 1 ;
+
+ int dir = 1 ;
+ while ( z < size) {
+ for ( int i = 0 ; i < 2 ; i++ ) {
+ for ( int j= 0 ; j < a; j++ ) {
+
+ if ( dir % 4 == 1 ) {
+ y++ ;
+ } else if ( dir % 4 == 2 ) {
+ x++ ;
+ } else if ( dir % 4 == 3 ) {
+ y-- ;
+ } else {
+ x-- ;
+ }
+
+ if ( x < rows && y < cols && x >= 0 && y >= 0 ) {
+ matrix[ z] [ 0 ] = x;
+ matrix[ z] [ 1 ] = y;
+ z++ ;
+ }
+ }
+
+ dir++ ;
+ }
+
+ a++ ;
+ }
+ return matrix;
+ }
+
+结果
]]>
Java
@@ -4037,7 +4206,6 @@ maxR[ n -leetcode
java
题解
- Median of Two Sorted Arrays
@@ -4284,101 +4452,42 @@ public:
- Leetcode 698 划分为k个相等的子集 ( Partition to K Equal Sum Subsets *Medium* ) 题解分析
- /2022/06/19/Leetcode-698-%E5%88%92%E5%88%86%E4%B8%BAk%E4%B8%AA%E7%9B%B8%E7%AD%89%E7%9A%84%E5%AD%90%E9%9B%86-Partition-to-K-Equal-Sum-Subsets-Medium-%E9%A2%98%E8%A7%A3%E5%88%86%E6%9E%90/
- 题目介绍Given an integer array nums and an integer k, return true if it is possible to divide this array into k non-empty subsets whose sums are all equal.
-示例 Example 1:
-
-Input: nums = [4,3,2,3,5,2,1], k = 4Output: trueExplanation: It is possible to divide it into 4 subsets (5), (1, 4), (2,3), (2,3) with equal sums.
-
-Example 2:
-
-Input: nums = [1,2,3,4], k = 3Output: false
-
-Constraints:
-
-1 <= k <= nums.length <= 16
-1 <= nums[i] <= 10^4
-The frequency of each element is in the range [1, 4].
-
-解析 看到这个题一开始以为挺简单,但是仔细想想问题还是挺多的,首先是分成 k 组,但是数量不限,应该需要用到回溯的方式,同时对于时间和空间复杂度也有要求,一开始这个代码是超时的,我也试了下 leetcode 上 discussion 里 vote 最高的提交也是超时的,不过看 discussion 里的帖子,貌似是后面加了一些条件,可以帮忙提高执行效率,第三条提示不太清楚意图,具体可以看下代码
-代码 public boolean canPartitionKSubsets ( int [ ] nums, int k) {
- if ( k == 1 ) {
- return true ;
- }
- int sum = 0 , n;
- n = nums. length;
- for ( int num : nums) {
- sum += num;
- }
- if ( sum % k != 0 ) {
- return false ;
- }
-
- int avg = sum / k;
-
- Arrays . sort ( nums) ;
-
- if ( nums[ n - 1 ] > avg) {
- return false ;
- }
-
- int calculated = 0 ;
- for ( int i = n - 1 ; i > 0 ; i-- ) {
- if ( nums[ i] == avg) {
- k-- ;
- calculated++ ;
- }
- }
-
- int [ ] bucket = new int [ k] ;
-
- for ( int i = 0 ; i < k; i++ ) {
- bucket[ i] = avg;
- }
-
-
- if ( nums[ n - 1 ] > avg) {
- return false ;
- }
-
- return backTraversal ( nums, bucket, k, n - 1 - calculated) ;
-}
-
-private boolean backTraversal ( int [ ] nums, int [ ] bucket, int k, int cur) {
- if ( cur < 0 ) {
- return true ;
- }
- for ( int i = 0 ; i < k; i++ ) {
- if ( bucket[ i] == nums[ cur] || bucket[ i] >= nums[ cur] + nums[ 0 ] ) {
-
-
-
-
-
-
-
-
- bucket[ i] -= nums[ cur] ;
-
-
- if ( backTraversal ( nums, bucket, k, cur - 1 ) ) return true ;
-
- bucket[ i] += nums[ cur] ;
- }
- }
- return false ;
-}
-
-最后贴个图
+ Redis_分布式锁
+ /2019/12/10/Redis-Part-1/
+ 今天看了一下 redis 分布式锁 redlock 的实现,简单记录下,
+加锁 原先我对 redis 锁的概念就是加锁使用 setnx,解锁使用 lua 脚本,但是 setnx 具体是啥,lua 脚本是啥不是很清楚 首先简单思考下这个问题,首先为啥不是先 get 一下 key 存不存在,然后再 set 一个 key value,因为加锁这个操作我们是要保证两点,一个是不能中途被打断,也就是说要原子性,如果是先 get 一下 key,如果不存在再 set 值的话,那就不是原子操作了;第二个是可不可以直接 set 值呢,显然不行,锁要保证唯一性,有且只能有一个线程或者其他应用单位获得该锁,正好 setnx 给了我们这种原子命令
+然后是 setnx 的键和值分别是啥,键比较容易想到是要锁住的资源,比如 user_id, 这里有个我自己之前比较容易陷进去的误区,但是这个误区后 面再说,这里其实是把user_id 作为要锁住的资源,在我获得锁的时候别的线程不允许操作,以此保证业务的正确性,不会被多个线程同时修改,确定了键,再来看看值是啥,其实原先我认为值是啥都没关系,我只要锁住了,光键就够我用了,但是考虑下多个线程的问题,如果我这个线程加了锁,然后我因为 gc 停顿等原因卡死了,这个时候redis 的锁或者说就是 redis 的缓存已经过期了,这时候另一个线程获得锁成功,然后我这个线程又活过来了,然后我就仍然认为我拿着锁,我去对数据进行修改或者释放锁,是不是就出现问题了,所以是不是我们还需要一个东西来区分这个锁是哪个线程加的,所以我们可以将值设置成为一个线程独有识别的值,至少在相对长的一段时间内不会重复。
+上面其实还有两个问题,一个是当 gc 超时时,我这个线程如何知道我手里的锁已经过期了,一种方法是我在加好锁之后就维护一个超时时间,这里其实还有个问题,不过跟第二个问题相关,就一起说了,就是设置超时时间,有些对于不是锁的 redis 缓存操作可以是先设置好值,然后在设置过期时间,那么这就又有上面说到的不是原子性的问题,那么就需要在同一条指令里把超时时间也设置了,幸好 redis 提供了这种支持
+SET resource_name my_random_value NX PX 30000
+这里借鉴一下解释下,resource_name就是 key,代表要锁住的东西,my_random_value就是识别我这个线程的,NX代表只有在不存在的时候才设置,然后PX 30000表示超时时间是 30秒自动过期
+PS:记录下我原先有的一个误区,是不是要用 key 来区分加锁的线程,这样只有一个用处,就是自身线程可以识别是否是自己加的锁,但是最大的问题是别的线程不知道,其实这个用户的出发点是我在担心前面提过的一个问题,就是当 gc 停顿后,我要去判断当前的这个锁是否是我加的,还有就是当释放锁的时候,如果保证不会错误释放了其他线程加的锁,但是这样附带很多其他问题,最大的就是其他线程怎么知道能不能加这个锁。
+解锁 当线程在锁过期之前就处理完了业务逻辑,那就可以提前释放这个锁,那么提前释放要怎么操作,直接del key显然是不行的,因为这样就是我前面想用线程随机值加资源名作为锁的初衷,我不能去释放别的线程加的锁,那么我要怎么办呢,先 get 一下看是不是我的?那又变成非原子的操作了,幸好redis 也考虑到了这个问题,给了lua 脚本来操作这种
+if redis. call ( "get" , KEYS[ 1 ] ) == ARGV[ 1 ] then
+ return redis. call ( "del" , KEYS[ 1 ] )
+else
+ return 0
+end
+这里的KEYS[1]就是前面加锁的resource_name,ARGV[1]就是线程的随机值my_random_value
+多节点 前面说的其实是单节点 redis 作为分布式锁的情况,那么当我们的 redis 有多节点的情况呢,如果多节点下处于加锁或者解锁或者锁有效情况下redis 的某个节点宕掉了怎么办,这里就有一些需要思考的地方,是否单独搞一个单节点的 redis作为分布式锁专用的,但是如果这个单节点的挂了呢,还有就是成本问题,所以我们需要一个多节点的分布式锁方案 这里就引出了开头说到的redlock,这个可是 redis的作者写的, 他的加锁过程是分以下几步去做这个事情
+
+获取当前时间(毫秒数)。
+按顺序依次向N个Redis节点执行获取锁的操作。这个获取操作跟前面基于单Redis节点的获取锁的过程相同,包含随机字符串my_random_value,也包含过期时间(比如PX 30000,即锁的有效时间)。为了保证在某个Redis节点不可用的时候算法能够继续运行,这个获取锁的操作还有一个超时时间(time out),它要远小于锁的有效时间(几十毫秒量级)。客户端在向某个Redis节点获取锁失败以后,应该立即尝试下一个Redis节点。这里的失败,应该包含任何类型的失败,比如该Redis节点不可用,或者该Redis节点上的锁已经被其它客户端持有(注:Redlock原文中这里只提到了Redis节点不可用的情况,但也应该包含其它的失败情况)。
+计算整个获取锁的过程总共消耗了多长时间,计算方法是用当前时间减去第1步记录的时间。如果客户端从大多数Redis节点(>= N/2+1)成功获取到了锁,并且获取锁总共消耗的时间没有超过锁的有效时间(lock validity time),那么这时客户端才认为最终获取锁成功;否则,认为最终获取锁失败。
+如果最终获取锁成功了,那么这个锁的有效时间应该重新计算,它等于最初的锁的有效时间减去第3步计算出来的获取锁消耗的时间。
+如果最终获取锁失败了(可能由于获取到锁的Redis节点个数少于N/2+1,或者整个获取锁的过程消耗的时间超过了锁的最初有效时间),那么客户端应该立即向所有Redis节点发起释放锁的操作(即前面介绍的Redis Lua脚本)。 释放锁的过程比较简单:客户端向所有Redis节点发起释放锁的操作,不管这些节点当时在获取锁的时候成功与否。这里为什么要向所有的节点发送释放锁的操作呢,这里是因为有部分的节点的失败原因可能是加锁时阻塞,加锁成功的结果没有及时返回,所以为了防止这种情况还是需要向所有发起这个释放锁的操作。 初步记录就先到这。
+
]]>
- Java
- leetcode
+ Redis
+ Distributed Lock
+ C
+ Redis
- leetcode
- java
+ C
+ Redis
+ Distributed Lock
+ 分布式锁
@@ -4408,58 +4517,6 @@ public:
c++
-
- Leetcode 83 删除排序链表中的重复元素 ( Remove Duplicates from Sorted List *Easy* ) 题解分析
- /2022/03/13/Leetcode-83-%E5%88%A0%E9%99%A4%E6%8E%92%E5%BA%8F%E9%93%BE%E8%A1%A8%E4%B8%AD%E7%9A%84%E9%87%8D%E5%A4%8D%E5%85%83%E7%B4%A0-Remove-Duplicates-from-Sorted-List-Easy-%E9%A2%98%E8%A7%A3%E5%88%86%E6%9E%90/
- 题目介绍给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。 PS:注意已排序,还有返回也要已排序
-示例 1:
-
-输入:head = [1,1,2] 输出:[1,2]
-
-示例 2:
-
-输入:head = [1,1,2,3,3] 输出:[1,2,3]
-
-提示:
-链表中节点数目在范围 [0, 300] 内
--100 <= Node.val <= 100
-题目数据保证链表已经按 升序 排列
-
-分析与题解 这题其实是比较正常的 easy 级别的题目,链表已经排好序了,如果还带一个排序就更复杂一点, 只需要前后项做个对比,如果一致则移除后项,因为可能存在多个重复项,所以只有在前后项不同 时才会更新被比较项
-code public ListNode deleteDuplicates ( ListNode head) {
-
- if ( head == null || head. next == null ) {
- return head;
- }
- ListNode tail = head;
-
- while ( tail. next != null ) {
- ListNode temp = tail. next;
-
-
- if ( temp. val == tail. val) {
- tail. next = temp. next;
- } else {
-
- tail = tail. next;
- }
- }
-
- return head;
-}
-链表应该是个需要反复的训练的数据结构,因为涉及到前后指针,然后更新操作,判空等, 我在这块也是掌握的不太好,需要多练习。
-]]>
-
- Java
- leetcode
-
-
- leetcode
- java
- 题解
- Remove Duplicates from Sorted List
-
-
Reverse Integer
/2015/03/13/Reverse-Integer/
@@ -4591,6 +4648,44 @@ public:
cluster
+
+ binary-watch
+ /2016/09/29/binary-watch/
+ problemA binary watch has 4 LEDs on the top which represent the hours (0-11), and the 6 LEDs on the bottom represent the minutes (0-59).
+Each LED represents a zero or one, with the least significant bit on the right.
+
+For example, the above binary watch reads “3:25”.
+Given a non-negative integer n which represents the number of LEDs that are currently on, return all possible times the watch could represent.
+Example: Input: n = 1
+Return: ["1:00", "2:00", "4:00", "8:00", "0:01", "0:02", "0:04", "0:08", "0:16", "0:32"]
+Note:
+The order of output does not matter.
+The hour must not contain a leading zero, for example “01:00” is not valid, it should be “1:00”.
+The minute must be consist of two digits and may contain a leading zero, for example “10:2” is not valid, it should be “10:02”.
+
+题解 又是参(chao)考(xi)别人的代码,嗯,就是这么不要脸,链接
+Code class Solution {
+public:
+ vector<string> readBinaryWatch(int num) {
+ vector<string> res;
+ for (int h = 0; h < 12; ++h) {
+ for (int m = 0; m < 60; ++m) {
+ if (bitset<10>((h << 6) + m).count() == num) {
+ res.push_back(to_string(h) + (m < 10 ? ":0" : ":") + to_string(m));
+ }
+ }
+ }
+ return res;
+ }
+}; ]]>
+
+ leetcode
+
+
+ leetcode
+ c++
+
+
docker-mysql-cluster
/2016/08/14/docker-mysql-cluster/
@@ -4928,77 +5023,52 @@ myusername ALL = 题目介绍You start at the cell (rStart, cStart) of an rows x cols grid facing east. The northwest corner is at the first row and column in the grid, and the southeast corner is at the last row and column.
-You will walk in a clockwise spiral shape to visit every position in this grid. Whenever you move outside the grid’s boundary, we continue our walk outside the grid (but may return to the grid boundary later.). Eventually, we reach all rows * cols spaces of the grid.
-Return an array of coordinates representing the positions of the grid in the order you visited them.
-Example 1:
-Input: rows = 1, cols = 4, rStart = 0, cStart = 0Output: [[0,0],[0,1],[0,2],[0,3]]
-
-Example 2:
-Input: rows = 5, cols = 6, rStart = 1, cStart = 4Output: [[1,4],[1,5],[2,5],[2,4],[2,3],[1,3],[0,3],[0,4],[0,5],[3,5],[3,4],[3,3],[3,2],[2,2],[1,2],[0,2],[4,5],[4,4],[4,3],[4,2],[4,1],[3,1],[2,1],[1,1],[0,1],[4,0],[3,0],[2,0],[1,0],[0,0]]
+ invert-binary-tree
+ /2015/06/22/invert-binary-tree/
+ Invert a binary tree
+ 4
+ / \
+ 2 7
+ / \ / \
+1 3 6 9
+
+to
+ 4
+ / \
+ 7 2
+ / \ / \
+9 6 3 1
+
+Trivia: This problem was inspired by this original tweet by Max Howell :
+
+Google: 90% of our engineers use the software you wrote (Homebrew), but you can’t invert a binary tree on a whiteboard so fuck off.
-Constraints:
-
-1 <= rows, cols <= 100
-0 <= rStart < rows
-0 <= cStart < cols
-
-简析 这个题主要是要相同螺旋矩阵的转变方向的边界判断,已经相同步长会行进两次这个规律,写代码倒不复杂
-代码 public int [ ] [ ] spiralMatrixIII ( int rows, int cols, int rStart, int cStart) {
- int size = rows * cols;
- int x = rStart, y = cStart;
-
- int [ ] [ ] matrix = new int [ size] [ 2 ] ;
-
- matrix[ 0 ] [ 0 ] = rStart;
- matrix[ 0 ] [ 1 ] = cStart;
-
- int z = 1 ;
-
- int a = 1 ;
-
- int dir = 1 ;
- while ( z < size) {
- for ( int i = 0 ; i < 2 ; i++ ) {
- for ( int j= 0 ; j < a; j++ ) {
-
- if ( dir % 4 == 1 ) {
- y++ ;
- } else if ( dir % 4 == 2 ) {
- x++ ;
- } else if ( dir % 4 == 3 ) {
- y-- ;
- } else {
- x-- ;
- }
-
- if ( x < rows && y < cols && x >= 0 && y >= 0 ) {
- matrix[ z] [ 0 ] = x;
- matrix[ z] [ 1 ] = y;
- z++ ;
- }
- }
-
- dir++ ;
- }
-
- a++ ;
- }
- return matrix;
- }
-
-结果
-]]>
+/**
+ * Definition for a binary tree node.
+ * struct TreeNode {
+ * int val;
+ * TreeNode *left;
+ * TreeNode *right;
+ * TreeNode(int x) : val(x), left(NULL), right(NULL) {}
+ * };
+ */
+class Solution {
+public:
+ TreeNode* invertTree(TreeNode* root) {
+ if(root == NULL) return root;
+ TreeNode* temp;
+ temp = invertTree(root->left);
+ root->left = invertTree(root->right);
+ root->right = temp;
+ return root;
+ }
+}; ]]>
- Java
leetcode
leetcode
- java
- 题解
+ c++
@@ -5053,42 +5123,69 @@ public:
- Redis_分布式锁
- /2019/12/10/Redis-Part-1/
- 今天看了一下 redis 分布式锁 redlock 的实现,简单记录下,
-加锁 原先我对 redis 锁的概念就是加锁使用 setnx,解锁使用 lua 脚本,但是 setnx 具体是啥,lua 脚本是啥不是很清楚 首先简单思考下这个问题,首先为啥不是先 get 一下 key 存不存在,然后再 set 一个 key value,因为加锁这个操作我们是要保证两点,一个是不能中途被打断,也就是说要原子性,如果是先 get 一下 key,如果不存在再 set 值的话,那就不是原子操作了;第二个是可不可以直接 set 值呢,显然不行,锁要保证唯一性,有且只能有一个线程或者其他应用单位获得该锁,正好 setnx 给了我们这种原子命令
-然后是 setnx 的键和值分别是啥,键比较容易想到是要锁住的资源,比如 user_id, 这里有个我自己之前比较容易陷进去的误区,但是这个误区后 面再说,这里其实是把user_id 作为要锁住的资源,在我获得锁的时候别的线程不允许操作,以此保证业务的正确性,不会被多个线程同时修改,确定了键,再来看看值是啥,其实原先我认为值是啥都没关系,我只要锁住了,光键就够我用了,但是考虑下多个线程的问题,如果我这个线程加了锁,然后我因为 gc 停顿等原因卡死了,这个时候redis 的锁或者说就是 redis 的缓存已经过期了,这时候另一个线程获得锁成功,然后我这个线程又活过来了,然后我就仍然认为我拿着锁,我去对数据进行修改或者释放锁,是不是就出现问题了,所以是不是我们还需要一个东西来区分这个锁是哪个线程加的,所以我们可以将值设置成为一个线程独有识别的值,至少在相对长的一段时间内不会重复。
-上面其实还有两个问题,一个是当 gc 超时时,我这个线程如何知道我手里的锁已经过期了,一种方法是我在加好锁之后就维护一个超时时间,这里其实还有个问题,不过跟第二个问题相关,就一起说了,就是设置超时时间,有些对于不是锁的 redis 缓存操作可以是先设置好值,然后在设置过期时间,那么这就又有上面说到的不是原子性的问题,那么就需要在同一条指令里把超时时间也设置了,幸好 redis 提供了这种支持
-SET resource_name my_random_value NX PX 30000
-这里借鉴一下解释下,resource_name就是 key,代表要锁住的东西,my_random_value就是识别我这个线程的,NX代表只有在不存在的时候才设置,然后PX 30000表示超时时间是 30秒自动过期
-PS:记录下我原先有的一个误区,是不是要用 key 来区分加锁的线程,这样只有一个用处,就是自身线程可以识别是否是自己加的锁,但是最大的问题是别的线程不知道,其实这个用户的出发点是我在担心前面提过的一个问题,就是当 gc 停顿后,我要去判断当前的这个锁是否是我加的,还有就是当释放锁的时候,如果保证不会错误释放了其他线程加的锁,但是这样附带很多其他问题,最大的就是其他线程怎么知道能不能加这个锁。
-解锁 当线程在锁过期之前就处理完了业务逻辑,那就可以提前释放这个锁,那么提前释放要怎么操作,直接del key显然是不行的,因为这样就是我前面想用线程随机值加资源名作为锁的初衷,我不能去释放别的线程加的锁,那么我要怎么办呢,先 get 一下看是不是我的?那又变成非原子的操作了,幸好redis 也考虑到了这个问题,给了lua 脚本来操作这种
-if redis. call ( "get" , KEYS[ 1 ] ) == ARGV[ 1 ] then
- return redis. call ( "del" , KEYS[ 1 ] )
-else
- return 0
-end
-这里的KEYS[1]就是前面加锁的resource_name,ARGV[1]就是线程的随机值my_random_value
-多节点 前面说的其实是单节点 redis 作为分布式锁的情况,那么当我们的 redis 有多节点的情况呢,如果多节点下处于加锁或者解锁或者锁有效情况下redis 的某个节点宕掉了怎么办,这里就有一些需要思考的地方,是否单独搞一个单节点的 redis作为分布式锁专用的,但是如果这个单节点的挂了呢,还有就是成本问题,所以我们需要一个多节点的分布式锁方案 这里就引出了开头说到的redlock,这个可是 redis的作者写的, 他的加锁过程是分以下几步去做这个事情
-
-获取当前时间(毫秒数)。
-按顺序依次向N个Redis节点执行获取锁的操作。这个获取操作跟前面基于单Redis节点的获取锁的过程相同,包含随机字符串my_random_value,也包含过期时间(比如PX 30000,即锁的有效时间)。为了保证在某个Redis节点不可用的时候算法能够继续运行,这个获取锁的操作还有一个超时时间(time out),它要远小于锁的有效时间(几十毫秒量级)。客户端在向某个Redis节点获取锁失败以后,应该立即尝试下一个Redis节点。这里的失败,应该包含任何类型的失败,比如该Redis节点不可用,或者该Redis节点上的锁已经被其它客户端持有(注:Redlock原文中这里只提到了Redis节点不可用的情况,但也应该包含其它的失败情况)。
-计算整个获取锁的过程总共消耗了多长时间,计算方法是用当前时间减去第1步记录的时间。如果客户端从大多数Redis节点(>= N/2+1)成功获取到了锁,并且获取锁总共消耗的时间没有超过锁的有效时间(lock validity time),那么这时客户端才认为最终获取锁成功;否则,认为最终获取锁失败。
-如果最终获取锁成功了,那么这个锁的有效时间应该重新计算,它等于最初的锁的有效时间减去第3步计算出来的获取锁消耗的时间。
-如果最终获取锁失败了(可能由于获取到锁的Redis节点个数少于N/2+1,或者整个获取锁的过程消耗的时间超过了锁的最初有效时间),那么客户端应该立即向所有Redis节点发起释放锁的操作(即前面介绍的Redis Lua脚本)。 释放锁的过程比较简单:客户端向所有Redis节点发起释放锁的操作,不管这些节点当时在获取锁的时候成功与否。这里为什么要向所有的节点发送释放锁的操作呢,这里是因为有部分的节点的失败原因可能是加锁时阻塞,加锁成功的结果没有及时返回,所以为了防止这种情况还是需要向所有发起这个释放锁的操作。 初步记录就先到这。
-
-]]>
+ mybatis 的 foreach 使用的注意点
+ /2022/07/09/mybatis-%E7%9A%84-foreach-%E4%BD%BF%E7%94%A8%E7%9A%84%E6%B3%A8%E6%84%8F%E7%82%B9/
+ mybatis 在作为轻量级 orm 框架,如果要使用类似于 in 查询的语句,除了直接替换字符串,还可以使用 foreach 标签 在mybatis的 dtd 文件中可以看到可以配置这些字段,
+<!ELEMENT foreach (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
+<!ATTLIST foreach
+collection CDATA #REQUIRED
+item CDATA #IMPLIED
+index CDATA #IMPLIED
+open CDATA #IMPLIED
+close CDATA #IMPLIED
+separator CDATA #IMPLIED
+>
+collection 表示需要使用 foreach 的集合,item 表示进行迭代的变量名,index 就是索引值,而 open 跟 close 代表拼接的起始和结束符号,一般就是左右括号,separator 则是每个 item 直接的分隔符
+例如写了一个简单的 sql 查询
+< select id = " search" parameterType = " list" resultMap = " StudentMap" >
+ select * from student
+ < where>
+ id in
+ < foreach collection = " list" item = " item" open = " (" close = " )" separator = " ," >
+ #{item}
+ </ foreach>
+ </ where>
+</ select>
+这里就发现了一个问题,collection 对应的这个值,如果传入的参数是个 HashMap,collection 的这个值就是以此作为 key 从这个 HashMap 获取对应的集合,但是这里有几个特殊的小技巧, 在上面的这个方法对应的接口方法定义中
+public List < Student > search ( List < Long > userIds) ;
+我是这么定义的,而 collection 的值是list,这里就有一点不能理解了,但其实是 mybatis 考虑到使用的方便性, 帮我们做了一点点小转换,我们翻一下 mybatis 的DefaultSqlSession 中的代码可以看到
+@Override
+public < E > List < E > selectList ( String statement, Object parameter, RowBounds rowBounds) {
+ try {
+ MappedStatement ms = configuration. getMappedStatement ( statement) ;
+ return executor. query ( ms, wrapCollection ( parameter) , rowBounds, Executor . NO_RESULT_HANDLER) ;
+ } catch ( Exception e) {
+ throw ExceptionFactory . wrapException ( "Error querying database. Cause: " + e, e) ;
+ } finally {
+ ErrorContext . instance ( ) . reset ( ) ;
+ }
+}
+
+ private Object wrapCollection ( final Object object) {
+ if ( object instanceof Collection ) {
+ StrictMap < Object > map = new StrictMap < Object > ( ) ;
+ map. put ( "collection" , object) ;
+ if ( object instanceof List ) {
+
+ map. put ( "list" , object) ;
+ }
+ return map;
+ } else if ( object != null && object. getClass ( ) . isArray ( ) ) {
+ StrictMap < Object > map = new StrictMap < Object > ( ) ;
+ map. put ( "array" , object) ;
+ return map;
+ }
+ return object;
+ } ]]>
- Redis
- Distributed Lock
- C
- Redis
+ Java
+ Mybatis
+ Mysql
- C
- Redis
- Distributed Lock
- 分布式锁
+ Java
+ Mysql
+ Mybatis
@@ -5431,41 +5528,48 @@ public class DynamicSqlSource implements SqlSource {
- binary-watch
- /2016/09/29/binary-watch/
- problemA binary watch has 4 LEDs on the top which represent the hours (0-11), and the 6 LEDs on the bottom represent the minutes (0-59).
-Each LED represents a zero or one, with the least significant bit on the right.
-
-For example, the above binary watch reads “3:25”.
-Given a non-negative integer n which represents the number of LEDs that are currently on, return all possible times the watch could represent.
-Example: Input: n = 1
-Return: ["1:00", "2:00", "4:00", "8:00", "0:01", "0:02", "0:04", "0:08", "0:16", "0:32"]
-Note:
-The order of output does not matter.
-The hour must not contain a leading zero, for example “01:00” is not valid, it should be “1:00”.
-The minute must be consist of two digits and may contain a leading zero, for example “10:2” is not valid, it should be “10:02”.
-
-题解 又是参(chao)考(xi)别人的代码,嗯,就是这么不要脸,链接
-Code class Solution {
-public:
- vector<string> readBinaryWatch(int num) {
- vector<string> res;
- for (int h = 0; h < 12; ++h) {
- for (int m = 0; m < 60; ++m) {
- if (bitset<10>((h << 6) + m).count() == num) {
- res.push_back(to_string(h) + (m < 10 ? ":0" : ":") + to_string(m));
- }
- }
- }
- return res;
- }
-}; ]]>
+ nginx 日志小记
+ /2022/04/17/nginx-%E6%97%A5%E5%BF%97%E5%B0%8F%E8%AE%B0/
+ nginx 默认的日志有特定的格式,我们也可以自定义,
+默认的格式是预定义的 combined
+log_format combined '$remote_addr - $remote_user [$time_local] '
+ '"$request" $status $body_bytes_sent '
+ '"$http_referer" "$http_user_agent"' ;
+
+配置的日志可以使用这个默认的,如果满足需求的话
+Syntax: access_log path [ format [ buffer= size] [ gzip[ = level] ] [ flush= time] [ if= condition] ] ;
+ access_log off;
+Default: access_log logs/access.log combined;
+Context: http, server, location, if in location, limit_except
+
+而如果需要额外的一些配置的话可以自己定义 log_format ,比如我想要给日志里加上请求时间,那就可以自己定义一个 log_format 比如添加下
+$request_time
+request processing time in seconds with a milliseconds resolution;
+time elapsed between the first bytes were read from the client and the log write after the last bytes were sent to the client
+
+log_format combined_extend '$remote_addr - $remote_user [$time_local] '
+ '"$request" $status $body_bytes_sent '
+ '"$http_referer" "$http_user_agent" "$request_time"' ;
+
+然后其他的比如还有 gzip 压缩,可以设置压缩级别,flush 刷盘时间还有根据条件控制
+这里的条件控制简单看了下还比较厉害
+比如我想对2xx 跟 3xx 的访问不记录日志
+map $status $loggable {
+ ~^[ 23 ] 0 ;
+ default 1 ;
+}
+
+access_log /path/to/access.log combined if = $loggable ;
+
+当 $loggable 是 0 或者空时表示 if 条件为否,上面的默认就是 1,只有当请求状态 status 是 2xx 或 3xx 时才是 0,代表不用记录,有了这个特性就可以更灵活地配置日志
+文章主要参考了 nginx 的 log 模块的文档
+]]>
- leetcode
+ nginx
- leetcode
- c++
+ nginx
+ 日志
@@ -5529,8 +5633,8 @@ location ~ *nginx
- openresty
nginx
+ openresty
@@ -5583,48 +5687,55 @@ int pcre_exec(const pcre *code, const pcre_extra *extra, const char *subject, in
- nginx 日志小记
- /2022/04/17/nginx-%E6%97%A5%E5%BF%97%E5%B0%8F%E8%AE%B0/
- nginx 默认的日志有特定的格式,我们也可以自定义,
-默认的格式是预定义的 combined
-log_format combined '$remote_addr - $remote_user [$time_local] '
- '"$request" $status $body_bytes_sent '
- '"$http_referer" "$http_user_agent"' ;
+ php-abstract-class-and-interface
+ /2016/11/10/php-abstract-class-and-interface/
+ PHP抽象类和接口
+抽象类与接口
+抽象类内可以包含非抽象函数,即可实现函数
+抽象类内必须包含至少一个抽象方法,抽象类和接口均不能实例化
+抽象类可以设置访问级别,接口默认都是public
+类可以实现多个接口但不能继承多个抽象类
+类必须实现抽象类和接口里的抽象方法,不一定要实现抽象类的非抽象方法
+接口内不能定义变量,但是可以定义常量
+
+示例代码 <?php
+interface int1{
+ const INTER1 = 111;
+ function inter1();
+}
+interface int2{
+ const INTER1 = 222;
+ function inter2();
+}
+abstract class abst1{
+ public function abstr1(){
+ echo 1111;
+ }
+ abstract function abstra1(){
+ echo 'ahahahha';
+ }
+}
+abstract class abst2{
+ public function abstr2(){
+ echo 1111;
+ }
+ abstract function abstra2();
+}
+class normal1 extends abst1{
+ protected function abstr2(){
+ echo 222;
+ }
+}
-配置的日志可以使用这个默认的,如果满足需求的话
-Syntax: access_log path [ format [ buffer= size] [ gzip[ = level] ] [ flush= time] [ if= condition] ] ;
- access_log off;
-Default: access_log logs/access.log combined;
-Context: http, server, location, if in location, limit_except
+result PHP Fatal error: Abstract function abst1::abstra1() cannot contain body in new.php on line 17
-而如果需要额外的一些配置的话可以自己定义 log_format ,比如我想要给日志里加上请求时间,那就可以自己定义一个 log_format 比如添加下
-$request_time
-request processing time in seconds with a milliseconds resolution;
-time elapsed between the first bytes were read from the client and the log write after the last bytes were sent to the client
-
-log_format combined_extend '$remote_addr - $remote_user [$time_local] '
- '"$request" $status $body_bytes_sent '
- '"$http_referer" "$http_user_agent" "$request_time"' ;
-
-然后其他的比如还有 gzip 压缩,可以设置压缩级别,flush 刷盘时间还有根据条件控制
-这里的条件控制简单看了下还比较厉害
-比如我想对2xx 跟 3xx 的访问不记录日志
-map $status $loggable {
- ~^[ 23 ] 0 ;
- default 1 ;
-}
-
-access_log /path/to/access.log combined if = $loggable ;
-
-当 $loggable 是 0 或者空时表示 if 条件为否,上面的默认就是 1,只有当请求状态 status 是 2xx 或 3xx 时才是 0,代表不用记录,有了这个特性就可以更灵活地配置日志
-文章主要参考了 nginx 的 log 模块的文档
+Fatal error: Abstract function abst1::abstra1() cannot contain body in php on line 17
]]>
- nginx
+ php
- nginx
- 日志
+ php
@@ -5653,8 +5764,8 @@ Starting node rabbit@rabbit2 node rabbit@rabbit2 PHP抽象类和接口
-抽象类与接口
-抽象类内可以包含非抽象函数,即可实现函数
-抽象类内必须包含至少一个抽象方法,抽象类和接口均不能实例化
-抽象类可以设置访问级别,接口默认都是public
-类可以实现多个接口但不能继承多个抽象类
-类必须实现抽象类和接口里的抽象方法,不一定要实现抽象类的非抽象方法
-接口内不能定义变量,但是可以定义常量
-
-示例代码 <?php
-interface int1{
- const INTER1 = 111;
- function inter1();
-}
-interface int2{
- const INTER1 = 222;
- function inter2();
-}
-abstract class abst1{
- public function abstr1(){
- echo 1111;
- }
- abstract function abstra1(){
- echo 'ahahahha';
- }
-}
-abstract class abst2{
- public function abstr2(){
- echo 1111;
- }
- abstract function abstra2();
-}
-class normal1 extends abst1{
- protected function abstr2(){
- echo 222;
- }
-}
+ redis数据结构介绍-第一部分 SDS,链表,字典
+ /2019/12/26/redis%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%BB%8B%E7%BB%8D/
+ redis是现在服务端很常用的缓存中间件,其实原来还有memcache之类的竞品,但是现在貌似 redis 快一统江湖,这里当然不是在吹,只是个人角度的一个感觉,不权威只是主观感觉。 redis 主要有五种数据结构,Strings,Lists,Sets,Hashes,Sorted Sets,这五种数据结构先简单介绍下,Strings类型的其实就是我们最常用的 key-value,实际开发中也会用的最多;Lists是列表,这个有些会用来做队列,因为 redis 目前常用的版本支持丰富的列表操作;还有是Sets集合,这个主要的特点就是集合中元素不重复,可以用在有这类需求的场景里;Hashes是叫散列,类似于 Python 中的字典结构;还有就是Sorted Sets这个是个有序集合;一眼看这些其实没啥特别的,除了最后这个有序集合,不过去了解背后的实现方式还是比较有意思的。
+SDS 简单动态字符串 先从Strings开始说,了解过 C 语言的应该知道,C 语言中的字符串其实是个 char[] 字符数组,redis 也不例外,只是最开始的版本就对这个做了一丢丢的优化,而正是这一丢丢的优化,让这个 redis 的使用效率提升了数倍
+struct sdshdr {
+ // 字符串长度
+ int len;
+ // 字符串空余字符数
+ int free;
+ // 字符串内容
+ char buf[];
+};
+这里引用了 redis 在 github 上最早的 2.2 版本的代码,代码路径是https://github.com/antirez/redis/blob/2.2/src/sds.h,可以看到这个结构体里只有仨元素,两个 int 型和一个 char 型数组,两个 int 型其实就是我说的优化,因为 C 语言本身的字符串数组,有两个问题,一个是要知道它实际已被占用的长度,需要去遍历这个数组,第二个就是比较容易踩坑的是遍历的时候要注意它有个以\0作为结尾的特点;通过上面的两个 int 型参数,一个是知道字符串目前的长度,一个是知道字符串还剩余多少位空间,这样子坐着两个操作从 O(N)简化到了O(1)了,还有第二个 free 还有个比较重要的作用就是能防止 C 字符串的溢出问题,在存储之前可以先判断 free 长度,如果长度不够就先扩容了,先介绍到这,这个系列可以写蛮多的,慢慢介绍吧
+链表 链表是比较常见的数据结构了,但是因为 redis 是用 C 写的,所以在不依赖第三方库的情况下只能自己写一个了,redis 的链表是个有头的链表,而且是无环的,具体的结构我也找了 github 上最早版本的代码
+typedef struct listNode {
+ // 前置节点
+ struct listNode *prev;
+ // 后置节点
+ struct listNode *next;
+ // 值
+ void *value;
+} listNode;
-result PHP Fatal error: Abstract function abst1::abstra1() cannot contain body in new.php on line 17
+typedef struct list {
+ // 链表表头
+ listNode *head;
+ // 当前节点,也可以说是最后节点
+ listNode *tail;
+ // 节点复制函数
+ void *(*dup)(void *ptr);
+ // 节点值释放函数
+ void (*free)(void *ptr);
+ // 节点值比较函数
+ int (*match)(void *ptr, void *key);
+ // 链表包含的节点数量
+ unsigned int len;
+} list;
+代码地址是这个https://github.com/antirez/redis/blob/2.2/src/adlist.h 可以看下节点是由listNode承载的,包括值和一个指向前节点跟一个指向后一节点的两个指针,然后值是 void 指针类型,所以可以承载不同类型的值 然后是 list结构用来承载一个链表,包含了表头,和表尾,复制函数,释放函数和比较函数,还有链表长度,因为包含了前两个节点,找到表尾节点跟表头都是 O(1)的时间复杂度,还有节点数量,其实这个跟 SDS 是同一个做法,就是空间换时间,这也是写代码里比较常见的做法,以此让一些高频的操作提速。
+字典 字典也是个常用的数据结构,其实只是叫法不同,数据结构中叫 hash 散列,Java 中叫 Map,PHP 中是数组 array,Python 中也叫字典 dict,因为纯 C 语言本身不带这些数据结构,所以这也是个痛并快乐着的过程,享受 C 语言的高性能的同时也要接受它只提供了语言的基本功能的现实,各种轮子都需要自己造,redis 同样实现了自己的字典 下面来看看代码
+typedef struct dictEntry {
+ void *key;
+ void *val;
+ struct dictEntry *next;
+} dictEntry;
-Fatal error: Abstract function abst1::abstra1() cannot contain body in php on line 17
+typedef struct dictType {
+ unsigned int (*hashFunction)(const void *key);
+ void *(*keyDup)(void *privdata, const void *key);
+ void *(*valDup)(void *privdata, const void *obj);
+ int (*keyCompare)(void *privdata, const void *key1, const void *key2);
+ void (*keyDestructor)(void *privdata, void *key);
+ void (*valDestructor)(void *privdata, void *obj);
+} dictType;
+
+/* This is our hash table structure. Every dictionary has two of this as we
+ * implement incremental rehashing, for the old to the new table. */
+typedef struct dictht {
+ dictEntry **table;
+ unsigned long size;
+ unsigned long sizemask;
+ unsigned long used;
+} dictht;
+
+typedef struct dict {
+ dictType *type;
+ void *privdata;
+ dictht ht[2];
+ int rehashidx; /* rehashing not in progress if rehashidx == -1 */
+ int iterators; /* number of iterators currently running */
+} dict;
+看了下这个 2.2 版本的代码跟最新版的其实也差的不是很多,所以还是照旧用老代码,可以看到上面四个结构体中,其实只有三个是存储数据用的,dictType 是用来放操作函数的,那么三个存放数据的结构体分别是干嘛的,这时候感觉需要一个图来说明比较好,稍等,我去画个图~ 这个图看着应该比较清楚这些都是用来干嘛的了,dict 是我们的主体结构,它有一个指向 dictType 的指针,这里面包含了字典的操作函数,然后是一个私有数据指针,接下来是一个 dictht 的数组,包含两个dictht,这个就是用来存数据的了,然后是 rehashidx 表示重哈希的状态,当是-1 的时候表示当前没有重哈希,iterators 表示正在遍历的迭代器的数量。 首先说说为啥需要有两个 dictht,这是因为字典 dict 这个数据结构随着数据量的增减,会需要在中途做扩容或者缩容操作,如果只有一个的话,对它进行扩容缩容时会影响正常的访问和修改操作,或者说保证正常查询,修改的正确性会比较复杂,并且因为需要高效利用空间,不能一下子申请一个非常大的空间来存很少的数据。当 dict 中 dictht 中的数据量超过 size 的时候负载就超过了 1,就需要进行扩容,这里的其实跟 Java 中的 HashMap 比较类似,超过一定的负载之后进行扩容。这里为啥 size 会超过 1 呢,可能有部分不了解这类结构的同学会比较奇怪,其实就是上图中画的,在数据结构中对于散列的冲突有几类解决方法,比如转换成链表,二次散列,找下个空槽等,这里就使用了链表法,或者说拉链法。当一个新元素通过 hashFunction 得出的 key 跟 sizemask 取模之后的值相同了,那就将其放在原来的节点之前,变成链表挂在数组 dictht.table下面,放在原有节点前是考虑到可能会优先访问。 忘了说明下 dictht 跟 dictEntry 的关系了,dictht 就是个哈希表,它里面是个dictEntry 的二维数组,而 dictEntry 是个包含了 key-value 结构之外还有一个 next 指针,因此可以将哈希冲突的以链表的形式保存下来。 在重点说下重哈希,可能同样写 Java 的同学对这个比较有感觉,跟 HashMap 一样,会以 2 的 N 次方进行扩容,那么扩容的方法就会比较简单,每个键重哈希要不就在原来这个槽,要不就在原来的槽加原 dictht.size 的位置;然后是重头戏,具体是怎么做扩容呢,其实这里就把第二个 ht 用上了,其实这两个hashtable 的具体作用有点类似于 jvm 中的两个 survival 区,但是又不全一样,因为 redis 在扩容的时候是采用的渐进式地重哈希,什么叫渐进式的呢,就是它不是像 jvm 那种标记复制的模式直接将一个 eden 区和原来的 survival 区存活的对象复制到另一个 survival 区,而是在每一次添加,删除,查找或者更新操作时,都会额外的帮忙搬运一部分的原 dictht 中的数据,这里会根据 rehashidx 的值来判断,如果是-1 表示并没有在重哈希中,如果是 0 表示开始重哈希了,然后rehashidx 还会随着每次的帮忙搬运往上加,但全部被搬运完成后 rehashidx 又变回了-1,又可以扯到Java 中的 Concurrent HashMap, 他在扩容的时候也使用了类似的操作。
]]>
- php
+ Redis
+ 数据结构
+ C
+ 源码
+ Redis
- php
+ redis
+ 数据结构
+ 源码
@@ -5767,69 +5912,51 @@ Fatal error: Abstract function abst1::abstra1() cannot contain body in php on li
- mybatis 的 foreach 使用的注意点
- /2022/07/09/mybatis-%E7%9A%84-foreach-%E4%BD%BF%E7%94%A8%E7%9A%84%E6%B3%A8%E6%84%8F%E7%82%B9/
- mybatis 在作为轻量级 orm 框架,如果要使用类似于 in 查询的语句,除了直接替换字符串,还可以使用 foreach 标签 在mybatis的 dtd 文件中可以看到可以配置这些字段,
-<!ELEMENT foreach (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
-<!ATTLIST foreach
-collection CDATA #REQUIRED
-item CDATA #IMPLIED
-index CDATA #IMPLIED
-open CDATA #IMPLIED
-close CDATA #IMPLIED
-separator CDATA #IMPLIED
->
-collection 表示需要使用 foreach 的集合,item 表示进行迭代的变量名,index 就是索引值,而 open 跟 close 代表拼接的起始和结束符号,一般就是左右括号,separator 则是每个 item 直接的分隔符
-例如写了一个简单的 sql 查询
-< select id = " search" parameterType = " list" resultMap = " StudentMap" >
- select * from student
- < where>
- id in
- < foreach collection = " list" item = " item" open = " (" close = " )" separator = " ," >
- #{item}
- </ foreach>
- </ where>
-</ select>
-这里就发现了一个问题,collection 对应的这个值,如果传入的参数是个 HashMap,collection 的这个值就是以此作为 key 从这个 HashMap 获取对应的集合,但是这里有几个特殊的小技巧, 在上面的这个方法对应的接口方法定义中
-public List < Student > search ( List < Long > userIds) ;
-我是这么定义的,而 collection 的值是list,这里就有一点不能理解了,但其实是 mybatis 考虑到使用的方便性, 帮我们做了一点点小转换,我们翻一下 mybatis 的DefaultSqlSession 中的代码可以看到
-@Override
-public < E > List < E > selectList ( String statement, Object parameter, RowBounds rowBounds) {
- try {
- MappedStatement ms = configuration. getMappedStatement ( statement) ;
- return executor. query ( ms, wrapCollection ( parameter) , rowBounds, Executor . NO_RESULT_HANDLER) ;
- } catch ( Exception e) {
- throw ExceptionFactory . wrapException ( "Error querying database. Cause: " + e, e) ;
- } finally {
- ErrorContext . instance ( ) . reset ( ) ;
- }
-}
-
- private Object wrapCollection ( final Object object) {
- if ( object instanceof Collection ) {
- StrictMap < Object > map = new StrictMap < Object > ( ) ;
- map. put ( "collection" , object) ;
- if ( object instanceof List ) {
-
- map. put ( "list" , object) ;
- }
- return map;
- } else if ( object != null && object. getClass ( ) . isArray ( ) ) {
- StrictMap < Object > map = new StrictMap < Object > ( ) ;
- map. put ( "array" , object) ;
- return map;
- }
- return object;
- } ]]>
+ redis数据结构介绍二-第二部分 跳表
+ /2020/01/04/redis%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%BB%8B%E7%BB%8D%E4%BA%8C/
+ 跳表 skiplist跳表是个在我们日常的代码中不太常用到的数据结构,相对来讲就没有像数组,链表,字典,散列,树等结构那么熟悉,所以就从头开始分析下,首先是链表,跳表跟链表都有个表字(太硬扯了我🤦♀️),注意这是个有序链表 如上图,在这个链表里如果我要找到 23,是不是我需要从3,5,9开始一直往后找直到找到 23,也就是说时间复杂度是 O(N),N 的一次幂复杂度,那么我们来看看第二个 这个结构跟原先有点不一样,它给链表中偶数位的节点又加了一个指针把它们链接起来,这样子当我们要寻找 23 的时候就可以从原来的一个个往下找变成跳着找,先找到 5,然后是 10,接着是 19,然后是 28,这时候发现 28 比 23 大了,那我在退回到 19,然后从下一层原来的链表往前找, 这里毛估估是不是前面的节点我就少找了一半,有那么点二分法的意思。 前面的其实是跳表的引子,真正的跳表其实不是这样,因为上面的其实有个比较大的问题,就是插入一个元素后需要调整每个元素的指针,在 redis 中的跳表其实是做了个随机层数的优化,因为沿着前面的例子,其实当数据量很大的时候,是不是层数越多,其查询效率越高,但是随着层数变多,要保持这种严格的层数规则其实也会增大处理复杂度,所以 redis 插入每个元素的时候都是使用随机的方式,看一眼代码
+/* ZSETs use a specialized version of Skiplists */
+typedef struct zskiplistNode {
+ sds ele;
+ double score;
+ struct zskiplistNode *backward;
+ struct zskiplistLevel {
+ struct zskiplistNode *forward;
+ unsigned long span;
+ } level[];
+} zskiplistNode;
+
+typedef struct zskiplist {
+ struct zskiplistNode *header, *tail;
+ unsigned long length;
+ int level;
+} zskiplist;
+
+typedef struct zset {
+ dict *dict;
+ zskiplist *zsl;
+} zset;
+忘了说了,redis 是把 skiplist 跳表用在 zset 里,zset 是个有序的集合,可以看到 zskiplist 就是个跳表的结构,里面用 header 保存跳表的表头,tail 保存表尾,还有长度和最大层级,具体的跳表节点元素使用 zskiplistNode 表示,里面包含了 sds 类型的元素值,double 类型的分值,用来排序,一个 backward 后向指针和一个 zskiplistLevel 数组,每个 level 包含了一个前向指针,和一个 span,span 表示的是跳表前向指针的跨度,这里再补充一点,前面说了为了灵活这个跳表的新增修改,redis 使用了随机层高的方式插入新节点,但是如果所有节点都随机到很高的层级或者所有都很低的话,跳表的效率优势就会减小,所以 redis 使用了个小技巧,贴下代码
+#define ZSKIPLIST_P 0.25 /* Skiplist P = 1/4 */
+int zslRandomLevel(void) {
+ int level = 1;
+ while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
+ level += 1;
+ return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
+}
+当随机值跟0xFFFF进行与操作小于ZSKIPLIST_P * 0xFFFF时才会增大 level 的值,因此保持了一个相对递减的概率 可以简单分析下,当 random() 的值小于 0xFFFF 的 1/4,才会 level + 1,就意味着当有 1 - 1/4也就是3/4的概率是直接跳出,所以一层的概率是3/4,也就是 1-P,二层的概率是 P*(1-P),三层的概率是 P² * (1-P) 依次递推。
+]]>
- Java
- Mybatis
- Mysql
+ Redis
+ 数据结构
+ C
+ 源码
+ Redis
- Java
- Mysql
- Mybatis
+ redis
+ 数据结构
+ 源码
@@ -6223,54 +6350,6 @@ REDIS_STATIC void __quicklistInsertNode(quicklist *quicklist,
Evict
-
- redis数据结构介绍二-第二部分 跳表
- /2020/01/04/redis%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%BB%8B%E7%BB%8D%E4%BA%8C/
- 跳表 skiplist跳表是个在我们日常的代码中不太常用到的数据结构,相对来讲就没有像数组,链表,字典,散列,树等结构那么熟悉,所以就从头开始分析下,首先是链表,跳表跟链表都有个表字(太硬扯了我🤦♀️),注意这是个有序链表 如上图,在这个链表里如果我要找到 23,是不是我需要从3,5,9开始一直往后找直到找到 23,也就是说时间复杂度是 O(N),N 的一次幂复杂度,那么我们来看看第二个 这个结构跟原先有点不一样,它给链表中偶数位的节点又加了一个指针把它们链接起来,这样子当我们要寻找 23 的时候就可以从原来的一个个往下找变成跳着找,先找到 5,然后是 10,接着是 19,然后是 28,这时候发现 28 比 23 大了,那我在退回到 19,然后从下一层原来的链表往前找, 这里毛估估是不是前面的节点我就少找了一半,有那么点二分法的意思。 前面的其实是跳表的引子,真正的跳表其实不是这样,因为上面的其实有个比较大的问题,就是插入一个元素后需要调整每个元素的指针,在 redis 中的跳表其实是做了个随机层数的优化,因为沿着前面的例子,其实当数据量很大的时候,是不是层数越多,其查询效率越高,但是随着层数变多,要保持这种严格的层数规则其实也会增大处理复杂度,所以 redis 插入每个元素的时候都是使用随机的方式,看一眼代码
-/* ZSETs use a specialized version of Skiplists */
-typedef struct zskiplistNode {
- sds ele;
- double score;
- struct zskiplistNode *backward;
- struct zskiplistLevel {
- struct zskiplistNode *forward;
- unsigned long span;
- } level[];
-} zskiplistNode;
-
-typedef struct zskiplist {
- struct zskiplistNode *header, *tail;
- unsigned long length;
- int level;
-} zskiplist;
-
-typedef struct zset {
- dict *dict;
- zskiplist *zsl;
-} zset;
-忘了说了,redis 是把 skiplist 跳表用在 zset 里,zset 是个有序的集合,可以看到 zskiplist 就是个跳表的结构,里面用 header 保存跳表的表头,tail 保存表尾,还有长度和最大层级,具体的跳表节点元素使用 zskiplistNode 表示,里面包含了 sds 类型的元素值,double 类型的分值,用来排序,一个 backward 后向指针和一个 zskiplistLevel 数组,每个 level 包含了一个前向指针,和一个 span,span 表示的是跳表前向指针的跨度,这里再补充一点,前面说了为了灵活这个跳表的新增修改,redis 使用了随机层高的方式插入新节点,但是如果所有节点都随机到很高的层级或者所有都很低的话,跳表的效率优势就会减小,所以 redis 使用了个小技巧,贴下代码
-#define ZSKIPLIST_P 0.25 /* Skiplist P = 1/4 */
-int zslRandomLevel(void) {
- int level = 1;
- while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
- level += 1;
- return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
-}
-当随机值跟0xFFFF进行与操作小于ZSKIPLIST_P * 0xFFFF时才会增大 level 的值,因此保持了一个相对递减的概率 可以简单分析下,当 random() 的值小于 0xFFFF 的 1/4,才会 level + 1,就意味着当有 1 - 1/4也就是3/4的概率是直接跳出,所以一层的概率是3/4,也就是 1-P,二层的概率是 P*(1-P),三层的概率是 P² * (1-P) 依次递推。
-]]>
-
- Redis
- 数据结构
- C
- 源码
- Redis
-
-
- redis
- 数据结构
- 源码
-
-
redis系列介绍七-过期策略
/2020/04/12/redis%E7%B3%BB%E5%88%97%E4%BB%8B%E7%BB%8D%E4%B8%83/
@@ -7331,6 +7410,43 @@ hz 10 这里有两个重点:
+s 在进入作用域后才变得有效
+它会保持自己的有效性直到自己离开作用域为止
+
+然后看个案例
+let x = 5 ;
+let y = x;
+这个其实有两种,一般可以认为比较多实现的会使用 copy on write 之类的,先让两个都指向同一个快 5 的存储,在发生变更后开始正式拷贝,但是涉及到内存处理的便利性,对于这类简单类型,可以直接拷贝 但是对于非基础类型
+let s1 = String :: from ( "hello" ) ;
+let s2 = s1;
+
+println! ( "{}, world!" , s1) ;
+有可能认为有两种内存分布可能 先看下 string 的内存结构 第一种可能是 第二种是 我们来尝试编译下 发现有这个错误,其实在 rust 中let y = x这个行为的实质是移动,在赋值给 y 之后 x 就无效了 这样子就不会造成脱离作用域时,对同一块内存区域的二次释放,如果需要复制,可以使用 clone 方法
+let s1 = String :: from ( "hello" ) ;
+let s2 = s1. clone ( ) ;
+
+println! ( "s1 = {}, s2 = {}" , s1, s2) ;
+这里其实会有点疑惑,为什么前面的x, y 的行为跟 s1, s2 的不一样,其实主要是基本类型和 string 这类的不定大小的类型的内存分配方式不同,x, y这类整型可以直接确定大小,可以直接在栈上分配,而像 string 和其他的变体结构体,其大小都是不能在编译时确定,所以需要在堆上进行分配
+]]>
+
+ 语言
+ Rust
+
+
+ Rust
+ 所有权
+ 内存分布
+ 新语言
+
+
rust学习笔记-所有权二
/2021/04/18/rust%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E6%89%80%E6%9C%89%E6%9D%83%E4%BA%8C/
@@ -7418,43 +7534,6 @@ hz 10 这里有两个重点:
-s 在进入作用域后才变得有效
-它会保持自己的有效性直到自己离开作用域为止
-
-然后看个案例
-let x = 5 ;
-let y = x;
-这个其实有两种,一般可以认为比较多实现的会使用 copy on write 之类的,先让两个都指向同一个快 5 的存储,在发生变更后开始正式拷贝,但是涉及到内存处理的便利性,对于这类简单类型,可以直接拷贝 但是对于非基础类型
-let s1 = String :: from ( "hello" ) ;
-let s2 = s1;
-
-println! ( "{}, world!" , s1) ;
-有可能认为有两种内存分布可能 先看下 string 的内存结构 第一种可能是 第二种是 我们来尝试编译下 发现有这个错误,其实在 rust 中let y = x这个行为的实质是移动,在赋值给 y 之后 x 就无效了 这样子就不会造成脱离作用域时,对同一块内存区域的二次释放,如果需要复制,可以使用 clone 方法
-let s1 = String :: from ( "hello" ) ;
-let s2 = s1. clone ( ) ;
-
-println! ( "s1 = {}, s2 = {}" , s1, s2) ;
-这里其实会有点疑惑,为什么前面的x, y 的行为跟 s1, s2 的不一样,其实主要是基本类型和 string 这类的不定大小的类型的内存分配方式不同,x, y这类整型可以直接确定大小,可以直接在栈上分配,而像 string 和其他的变体结构体,其大小都是不能在编译时确定,所以需要在堆上进行分配
-]]>
-
- 语言
- Rust
-
-
- Rust
- 所有权
- 内存分布
- 新语言
-
-
spark-little-tips
/2017/03/28/spark-little-tips/
@@ -7772,52 +7851,37 @@ user3:
- invert-binary-tree
- /2015/06/22/invert-binary-tree/
- Invert a binary tree
- 4
- / \
- 2 7
- / \ / \
-1 3 6 9
-
-to
- 4
- / \
- 7 2
- / \ / \
-9 6 3 1
-
-Trivia: This problem was inspired by this original tweet by Max Howell :
-
-Google: 90% of our engineers use the software you wrote (Homebrew), but you can’t invert a binary tree on a whiteboard so fuck off.
-
-/**
- * Definition for a binary tree node.
- * struct TreeNode {
- * int val;
- * TreeNode *left;
- * TreeNode *right;
- * TreeNode(int x) : val(x), left(NULL), right(NULL) {}
- * };
- */
-class Solution {
-public:
- TreeNode* invertTree(TreeNode* root) {
- if(root == NULL) return root;
- TreeNode* temp;
- temp = invertTree(root->left);
- root->left = invertTree(root->right);
- root->right = temp;
- return root;
- }
-}; ]]>
+ 《长安的荔枝》读后感
+ /2022/07/17/%E3%80%8A%E9%95%BF%E5%AE%89%E7%9A%84%E8%8D%94%E6%9E%9D%E3%80%8B%E8%AF%BB%E5%90%8E%E6%84%9F/
+ 断断续续地看完了马伯庸老师的《长安的荔枝》,一开始是看这本书在排行榜排得很高,又是马伯庸的,之前看过他的《古董局中局》,还是很有意思的,而且正好是比较短的,不过前后也拖了蛮久才看完,看完后读了下马老师自己写的后记,就特别有感触。 整个故事是围绕一个上林署监事李善德被委任一项给贵妃送荔枝的差事展开,“长安回望绣成堆,山顶千门次第开,一骑红尘妃子笑,无人知是荔枝来”,以前没细究过这个送荔枝的过程,但是以以前的运输速度和保鲜条件,感觉也不是太现实,所以主人公一开始就以为只是像以往一样是送荔枝干这种,能比较方便运输,不容易变质的,结果发现其实是同僚在坑他,这次是要在贵妃生辰的时候给贵妃送来新鲜的岭南荔枝,用比较时兴的词来说,这就是个送命题啊,鲜荔枝一日色变,两日香变,三日味变,同僚的还有杜甫跟韩承,都觉得老李可以直接写休书了,保全家人,不然就是全家送命,李善德也觉得基本算是判刑了,而且其实是这事被转了几次,最后到老李所在的上林署,主管为了骗他接下这个活还特意在文书上把荔枝鲜的“鲜”字贴住,那会叫做“贴黄”,变成了荔枝“煎”,所以说官场险恶,大家都想把这烫手山芋丢出去,结果丢到了我们老实的老李头上,但是从接到这个通知到贵妃的生辰六月初一还有挺长的时间,其实这个活虽然送命,但是在前期这个“荔枝使”也基本就是类似带着尚方宝剑,御赐黄马褂的职位,随便申请经费,不必像常规的部门费用需要定预算,申请后再层层审批,而是特事特批特办的耍赖做法,所以在这段时间是能够潇洒挥霍一下的。其实可以好好地捞一波给妻女,然后写下和离,在自己死后能让她们过的好一些,但最后还是在杜甫的一番劝导下做出了尝试一番的决定,因为也没其他办法,既是退无可退,何不向前拼死一搏,其实说到这,我觉得看这本书感觉有所收获的第一点,有时候总觉得事情没戏了,想躺平放弃了,但是这样其实这个结果是不会变好的,尝试努力,拼尽全力搏一搏,说不定会有所改观,至少不会变更坏了。
+]]>
- leetcode
+ 读后感
+ 生活
- leetcode
- c++
+ 生活
+ 读后感
+
+
+
+ 村上春树《1Q84》读后感
+ /2019/12/18/1Q84%E8%AF%BB%E5%90%8E%E6%84%9F/
+ 看完了村上春树的《1Q84》,这应该是第五本看的他的书了,继 跑步,挪威的森林,刺杀骑士团长,海边的卡夫卡之后,不是其中最长的,好像是海边的卡夫卡还是刺杀骑士团长比较长一点,都是在微信读书上看的,比较方便,最开始在上面看的是高晓松的《鱼羊野史》,不知道为啥取这个名字,但是还是满吸引我的,不过由于去年的种种,没有很多心思把它看完,而且本身的组织形式就是比较松散的,看到哪算哪,其实一些野史部分是我比较喜欢,有些谈到人物的就不太有兴趣,而且类似于大祥哥吃的东西,反正都是哇,怎么这么好吃,嗯,太爱(niu)你(bi)了,高晓松就是这个人是我最喜欢的 xxx 家,我也没去细究过他有没有说重复过,反正是不太爱,后来因为这书还一度对战争史有了浓厚的兴趣,然而事实告诉我,大部头的战争史,其实正史我是真的啃不下去,我可能只对其中 10%的内容感兴趣,不过终于也在今年把它看完了,好像高晓松的晓说也最终季了,貌似其中讲朝鲜战争的还被和谐了,看样子是说出了一些故事(truth)。
+本来只是想把 《1Q84》的读后感写下,现在觉得还是把这篇当成我今年的读书总结吧,不过先从《1Q84》说起。
+严格来讲,这不是很书面化的读后感,可能我想写的也只是像聊天一样的说下我读过的书,包括的技术博客其实也是类似的,以后或许会转变,但是目前水平如此吧,写多了可能会变好,也可能不会。
+开始正文吧,这书有点类似于海边的卡夫卡,一开始是通过两条故事线,穿插着叙述,一条是青豆的,不算是个职业杀手的女杀手,要去解决一个经常家暴的斯文败类,穿着描述得比较性感吧,杀人方式是通过比较长的细针,从脖子后面一个精巧的位置插入,可以造成是未知原因死亡的假象,可能会推断成心梗之类的,这里有个前置的细节,就是青豆是乘坐一辆很高级的出租车,内饰什么的都非常有质感,有点不像一辆出租车,然后车里放了一首比较小众的歌,雅纳切克的《小交响曲》,但是青豆知道它,这跟后面的情节也有些许关系,这是女主人公青豆的出场;相应的男主的出场印象不是太深刻,男主叫天吾,是个不知名的作家,跟一个叫小松的编辑有比较好的关系,虽然天吾还没有拿到比较有分量的奖项,但是小松很看好他,也让他帮忙审校一个新作家奖的投稿文章,虽然天吾自身还没获得过这个奖,天吾还有个正式工作,是当数学老师,天吾在学生时代是个数学天才,但后面有对文学产生了兴趣,文学还不足以养活自己,靠着教课还是能保持温饱;
+接下来是正式故事的起点了,就是小松收到了一部小说投稿,名叫《空气蛹》,是个叫深绘里的女孩子投的稿,小松对他赋予了很高的评价,这里好像记岔了,好像是天吾对这部小说很有好感,但是小松比较怀疑,然后小松看了之后也有了浓厚的兴趣,这里就是开端了,小松想让天吾来重写润色这部《空气蛹》,因为故事本身很有分量,但是描写手法叙事方式等都很拙劣,而天吾正好擅长这个,小松对天吾的评价是,描写技巧无可挑剔,就是故事主体的火花还没际遇迸发,需要一个导火索,这个就可以类比我们程序员,很多比较初中级的程序员主要擅长在原来的代码上修修改改或者给他分配个小功能,比较高级的程序员就需要能做一些项目的架构设计,核心的技术方案设计,以前我也觉得写文档这个比较无聊,但是当一个项目真的比较庞大,复杂的时候,整体和核心部分的架构设计和方案还是需要有文档沉淀的,不然别人不知道没法接受,自己过段时间也会忘记。
+对于小松的这个建议,他的初衷是想搅一搅这个死气沉沉套路颇深的文坛,因为本身《空气蛹》这部小说的内容很吸引人,小松想通过天吾的润色补充让这部小说冲击新人奖,有种恶作剧的意图,天吾对此表示很多担心和顾虑,小松的这个建议其实也是一种文学作假,有两方面的担心,一方面是原作者深绘里是否同意如此操作,一方面是外界如果发现了这个事实会有什么样的后果,但是小松表示不用担心,前一步由小松牵线,让天吾跟原作者深绘里当面沟通这个代写是否被允许,结果当然是被允许了,这里有了对深绘里的初步描写,按我的理解是比较仙的感觉,然后语言沟通有些吃力,或者说有她自己的特色,当面沟通时貌似是让深绘里回去再考虑下,然后后面再由天吾去深绘里寄宿的戎野老师家沟通具体的细节。
+2019年12月18日23:37:19 更新 去到戎野老师家之后,天吾知道了关于深绘里的一些事情,深绘里的父亲与戎野老师应该是老友,深绘里的父亲在当初成立了一个叫”先驱”的公社,一个独立运行的社会组织,以运营农场作为物资来源,追求更为松散的共同体,即不过分激进地公有制,进行松散的共同生活,承认私有财产,简而言之就是这样一个能稳定存活下来的独立社会组织,但是随着稳定运行,内部的激进派和稳健派开始出现分歧,不可磨合,后来两派就分裂了,深绘里的父亲,深田保留在了稳健派,但是此时其实深田保内心是矛盾的,以为一开始其实是他倡导的独立革命才组织起了这群人,然而现在他又认清了现实社会已经不太相信能通过革命来独立的可能性,后来激进派便开始越加封闭,而且进行军事训练和思想教育,而后这个先驱的激进派别便有了新的名字”黎明”,深绘里也是在此时从先驱逃离来投靠戎野老师 暂时先写到这,未完待续~
+]]>
+
+ 生活
+ 读后感
+ 村上春树
+
+
+ 读后感
@@ -7833,6 +7897,41 @@ public:
生活
+
+ 一个 nginx 的简单记忆点
+ /2022/08/21/%E4%B8%80%E4%B8%AA-nginx-%E7%9A%84%E7%AE%80%E5%8D%95%E8%AE%B0%E5%BF%86%E7%82%B9/
+ 上周在处理一个 nginx 配置的时候,发现了一个之前不理解的小点,说一个场景,就是我们一般的处理方式就是一个 ip 端口只能配置一个域名的服务,比如 https://nicksxs.me 对应配置到 127.0.0.1:443,如果我想要把 https://nicksxs.com 也解析到这个服务器,并转发到不同的下游,这里就需要借助所谓的 SNI 的功能
+Server Name Indication A more generic solution for running several HTTPS servers on a single IP address is TLS Server Name Indication extension (SNI, RFC 6066), which allows a browser to pass a requested server name during the SSL handshake and, therefore, the server will know which certificate it should use for the connection. SNI is currently supported by most modern browsers, though may not be used by some old or special clients.来源 机翻一下:在单个 IP 地址上运行多个 HTTPS 服务器的更通用的解决方案是 TLS 服务器名称指示扩展(SNI,RFC 6066),它允许浏览器在 SSL 握手期间传递请求的服务器名称,因此,服务器将知道哪个 它应该用于连接的证书。 目前大多数现代浏览器都支持 SNI,但某些旧的或特殊的客户端可能不使用 SNI。
+首先我们需要确认 sni 已被支持 在实际的配置中就可以这样
+stream {
+ map $ssl_preread_server_name $stream_map {
+ nicksxs.me nme;
+ nicksxs.com ncom;
+ }
+
+ upstream nme {
+ server 127.0.0.1:8000;
+ }
+ upstream ncom {
+ server 127.0.0.1:8001;
+ }
+
+ server {
+ listen 443 reuseport;
+ proxy_pass $stream_map ;
+ ssl_preread on ;
+ }
+}
+类似这样,但是这个理解是非常肤浅和不完善的,只是简单记忆下,后续再进行补充完整
+还有一点就是我们在配置的时候经常配置就是 server_name,但是会看到直接在使用 ssl_server_name, 其实在listen 标识了 ssl, 对应的 ssl_server_name 就等于 server_name,不需要额外处理了。
+]]>
+
+ nginx
+
+
+ nginx
+
+
介绍一下 RocketMQ
/2020/06/21/%E4%BB%8B%E7%BB%8D%E4%B8%80%E4%B8%8B-RocketMQ/
@@ -7902,31 +8001,6 @@ public:
中间件
-
- 介绍下最近比较实用的端口转发
- /2021/11/14/%E4%BB%8B%E7%BB%8D%E4%B8%8B%E6%9C%80%E8%BF%91%E6%AF%94%E8%BE%83%E5%AE%9E%E7%94%A8%E7%9A%84%E7%AB%AF%E5%8F%A3%E8%BD%AC%E5%8F%91/
- vscode 扩展转发在日常使用云服务器的时候,如果要访问上面自建的 mysql,一般要不直接开对应的端口,然后需要对本地 ip 进行授权,但是这个方案会有比较多的限制,比如本地 ip 变了,比如是非固定出口 ip 的家用宽带,或者要在家里跟公司都要访问,如果对所有 ip 都授权的话会不安全,这个时候其实是用 ssh 端口转发是个比较安全方便的方式。 原来在这个之前其实对这块内容不太了解,后面是听朋友说的,vscode 的 Remote - SSH 扩展可以很方便的使用端口转发,在使用该扩展的时候,会在控制台位置里都出现一个”端口” tab 如图中所示,我就是将一个服务器上的 mysql 的 3306 端口转发到本地的 3307 端口,至于为什么不用 3306 是因为本地我也有个 mysql 已经使用了 3306 端口,这个方法是使用的 vscode 的这个扩展,
-ssh 命令转发 还有个方式是直接使用 ssh 命令 命令可以如此
-ssh -CfNg -L 3307 :127.0.0.1:3306 user1@199.199.199.199
-简单介绍下这个命令-C 表示的是压缩数据包-f 表示后台执行命令-N 是表示不执行具体命令只用于端口转发-g 表示允许远程主机连接本地转发端口-L 则是具体端口转发的映射配置 上面的命令就是将远程主机的 127.0.0.1:3306 对应转发到本地 3307 而后面的用户则就是登录主机的用户名user1和ip地址199.199.199.199,当然这个配置也不是唯一的
-ssh config 配置转发 还可以在ssh 的 config 配置中加对应的配置
-Host host1
- HostName 199.199 .199.199
- User user1
- IdentityFile /Users/user1/.ssh/id_rsa
- ServerAliveInterval 60
- LocalForward 3310 127.0 .0.1:3306
-然后通过 ssh host1 连接服务器的时候就能顺带做端口转发
-]]>
-
- ssh
- 技巧
-
-
- ssh
- 端口转发
-
-
从丁仲礼被美国制裁聊点啥
/2020/12/20/%E4%BB%8E%E4%B8%81%E4%BB%B2%E7%A4%BC%E8%A2%AB%E7%BE%8E%E5%9B%BD%E5%88%B6%E8%A3%81%E8%81%8A%E7%82%B9%E5%95%A5/
@@ -8001,8 +8075,8 @@ public:
幸福了吗
- 读后感
读书
+ 读后感
打卡
幸福了吗
足球
@@ -8047,22 +8121,20 @@ public:
- 周末我在老丈人家打了天小工
- /2020/08/16/%E5%91%A8%E6%9C%AB%E6%88%91%E5%9C%A8%E8%80%81%E4%B8%88%E4%BA%BA%E5%AE%B6%E6%89%93%E4%BA%86%E5%A4%A9%E5%B0%8F%E5%B7%A5/
- 这周回家提前约好了要去老丈人家帮下忙,因为在翻修下老房子,活不是特别整的那种,所以大部分都是自己干,或者找个大工临时干几天(我们这那种比较专业的泥工匠叫做大工),像我这样去帮忙的,就是干点小工(把给大工帮忙的,干些偏体力活的叫做小工)的活。从大学毕业以后真的蛮少帮家里干活了,以前上学的时候放假还是帮家里淘个米,简单的扫地拖地啥的,当然刚高考完的时候,还去我爸厂里帮忙干了几天的活,实在是比较累,不过现在想着是觉得自己那时候比较牛,而不是特别排斥这个活,相对于现在的工作来说,导致了一系列的职业病,颈椎腰背都很僵硬,眼镜也不好,还有反流,像我爸那种活反而是脑力加体力的比较好的结合。 这一天的活前半部分主要是在清理厨房,瓷砖上的油污和墙上天花板上即将脱落的石灰或者白色涂料层,这种活特别是瓷砖上的油污,之前在自己家里也干活,还是比较熟悉的,不过前面主要是LD 在干,我主要是先搞墙上和天花板上的,干活还是很需要技巧的,如果直接去铲,那基本我会变成一个灰人,而且吸一鼻子灰,老丈人比较专业,先接上软管用水冲,一冲效果特别好,有些石灰涂料层直接就冲掉了,冲完之后先用带加长杆的刀片铲铲了一圈墙面,说实话因为老房子之前租出去了,所以墙面什么的被糟蹋的比较难看,一层一层的,不过这还算还好,后面主要是天花板上的,这可难倒我了,从小我爸妈是比较把我当小孩管着,爬上爬下的基本都是我爸搞定,但是到了老丈人家也只得硬着头皮上了,爬到跳(一种建筑工地用的架子)上,还有点晃,小心脏扑通扑通跳,而且带加长杆的铲子还是比较重的,铲一会手也有点累,不过坚持着铲完了,上面还是比较平整的,不过下来的时候又把我难住了🤦♂️,往下爬的时候有根杆子要跨过去,由于裤子比较紧,强行一把跨过去怕抽筋,所以以一个非常尴尬的姿势停留休息了一会,再跨了过去,幸好事后问 LD,他们都没看到,哈哈哈,然后就是帮忙一起搞瓷砖上的油污,这个太有经验了,不过老丈人更有意思,一会试试啤酒,一会用用沙子,后面在午饭前基本就弄的比较干净了,就坐着等吃饭了,下午午休了会,就继续干活了。 下午是我这次体验的重点了,因为要清理以前贴的墙纸,真的是个很麻烦的活,只能说贴墙纸的师傅活干得太好了,基本不可能整个撕下来,想用铲子一点点铲下来也不行,太轻了就只铲掉表面一层,太重了就把墙纸跟墙面的石灰啥的整个铲下来了,而且手又累又酸,后来想着是不是继续用水冲一下,对着一小面墙试验了下,效果还不错,但是又发现了个问题,那一面墙又有一块是后面糊上去的,铲掉外层的石灰后不平,然后就是最最重头的,也是让我后遗症持续到第二天的,要把那一块糊上去的水泥敲下来,毛估下大概是敲了80%左右,剩下的我的手已经不会用力了,因为那一块应该是要糊上去的始作俑者,就一块里面凹进去的,我拿着榔头敲到我手已经没法使劲了,而且大下午,感觉没五分钟,我的汗已经糊满脸,眼睛也睁不开,不然就流到眼睛里了,此处获得成就一:用榔头敲墙壁,也是个技术加体力的活,而且需要非常好的技巧,否则手马上就废了,敲下去的反作用力,没一会就不行了,然后是看着老丈人兄弟帮忙拆一个柜子,在我看来是个几天都搞不定的活,他轻轻松松在我敲墙的那会就搞定了,以前总觉得我干的活非常有技术含量,可是这个事情真的也是很有技巧啊,它是个把一间房间分隔开的柜子,从底到顶上,还带着门,我还在旁边帮忙撬一下脚踢,一根木条撬半天,唉,成就二:专业的人就是不一样。 最后就是成就三了:我之前沾沾自喜的跑了多少步,做了什么锻炼,其实都是渣渣,像这样干一天活,没经历过的,基本大半天就废了,反过来说,如果能经常去这么干一天活,跑步啥的都是渣渣,消耗的能量远远超过跑个十公里啥的。
-]]>
-
- 生活
- 运动
- 跑步
- 干活
+ 2021 年终总结
+ /2022/01/22/2021-%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/
+ 又是一年年终总结,本着极度讨厌实时需求的理念,我还是 T+N 发布这个年终总结
+工作篇 工作没什么大变化,有了些微的提升,可能因为是来了之后做了些项目对比公司与来还算是比较重要的,但是技术难度上没有特别突出的点,可能最开始用 openresty+lua 做了个 ab 测的工具,还是让我比较满意的,后面一般都是业务型的需求,今年可能在业务相关的技术逻辑上有了一些深度的了解,而原来一直想做的业务架构升级和通用型技术中间件这样的优化还是停留在想象中,前面说的 ab 测应该算是个半成品,还是没能多走出这一步,得需要多做一些实在的事情,比如轻量级的业务框架,能够对原先不熟悉的业务逻辑,代码逻辑有比较深入的理解,而不是一直都是让特定的同学负责特定的逻辑,很多时候还是在偷懒,习惯以一些简单安全的方案去做事情,在技术上还是要有所追求,还有就是能够在新语言,主要是 rust,swift 这类的能有些小玩具可以做,rust 的话是因为今年看了一本相关的书,后面三分之一其实消化得不好,这本书整体来说是很不错的,只是 rust 本身在所有权这块,还有引用包装等方面是设计得比较难懂,也可能是我基础差,所以还是想在复习下,可以做一个简单的命令行工具这种,然后 swift 是想说可以做点 mac 的小软件,原生的毕竟性能好点,又小。基于 web 做的客户端大部分都是又丑又大,极少数能好看点,但也是很重,起码 7~80M 的大小,原生的估计能除以 10。 整体的职业规划貌似陷入了比较大的困惑期,在目前公司发展前景不是很大,但是出去貌似也没有比较适合我的机会,总的来说还是杭州比较卷,个人觉得有自己的时间是非常重要的,而且这个不光是用来自我提升的,还是让自己有足够的时间做缓冲,有足够的时间锻炼减肥,时间少的情况下,不光会在仅有的时间里暴饮暴食,还没空锻炼,身体是革命的本钱,现在其实能特别明显地感觉到身体状态下滑,容易疲劳,焦虑。所以是否也许有可能以后要往外企这类的方向去发展。 工作上其实还是有个不大不小的缺点,就是容易激动,容易焦虑,前一点可能有稍稍地改观,因为工作中的很多现状其实是我个人难以改变的,即使觉得不合理,但是结构在那里,还不如自己放宽心,尽量做好事情就行。第二点的话还是做得比较差,一直以来抗压能力都比较差,跟成长环境,家庭环境都有比较大的关系,而且说实在的特别是父母,基本也没有在这方面给我正向的帮助,比较擅长给我施压,从小就是通过压力让我好好读书,当个乖学生,考个好学校,并没有能真正地理解我的压力,教我或者帮助我解压,只会在那说着不着边际的空话,甚至经常反过来对我施压。还是希望能慢慢解开,这点可能对我身体也有影响,也许需要看一些心理疏导相关的书籍。工作篇暂时到这,后续还有其他篇,未完待续哈哈😀
+]]>
+
+ 生活
+ 年终总结
生活
- 运动
- 减肥
- 跑步
- 干活
+ 年终总结
+ 2021
+ 拖更
@@ -8147,89 +8219,22 @@ public:
- redis数据结构介绍-第一部分 SDS,链表,字典
- /2019/12/26/redis%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%BB%8B%E7%BB%8D/
- redis是现在服务端很常用的缓存中间件,其实原来还有memcache之类的竞品,但是现在貌似 redis 快一统江湖,这里当然不是在吹,只是个人角度的一个感觉,不权威只是主观感觉。 redis 主要有五种数据结构,Strings,Lists,Sets,Hashes,Sorted Sets,这五种数据结构先简单介绍下,Strings类型的其实就是我们最常用的 key-value,实际开发中也会用的最多;Lists是列表,这个有些会用来做队列,因为 redis 目前常用的版本支持丰富的列表操作;还有是Sets集合,这个主要的特点就是集合中元素不重复,可以用在有这类需求的场景里;Hashes是叫散列,类似于 Python 中的字典结构;还有就是Sorted Sets这个是个有序集合;一眼看这些其实没啥特别的,除了最后这个有序集合,不过去了解背后的实现方式还是比较有意思的。
-SDS 简单动态字符串 先从Strings开始说,了解过 C 语言的应该知道,C 语言中的字符串其实是个 char[] 字符数组,redis 也不例外,只是最开始的版本就对这个做了一丢丢的优化,而正是这一丢丢的优化,让这个 redis 的使用效率提升了数倍
-struct sdshdr {
- // 字符串长度
- int len;
- // 字符串空余字符数
- int free;
- // 字符串内容
- char buf[];
-};
-这里引用了 redis 在 github 上最早的 2.2 版本的代码,代码路径是https://github.com/antirez/redis/blob/2.2/src/sds.h,可以看到这个结构体里只有仨元素,两个 int 型和一个 char 型数组,两个 int 型其实就是我说的优化,因为 C 语言本身的字符串数组,有两个问题,一个是要知道它实际已被占用的长度,需要去遍历这个数组,第二个就是比较容易踩坑的是遍历的时候要注意它有个以\0作为结尾的特点;通过上面的两个 int 型参数,一个是知道字符串目前的长度,一个是知道字符串还剩余多少位空间,这样子坐着两个操作从 O(N)简化到了O(1)了,还有第二个 free 还有个比较重要的作用就是能防止 C 字符串的溢出问题,在存储之前可以先判断 free 长度,如果长度不够就先扩容了,先介绍到这,这个系列可以写蛮多的,慢慢介绍吧
-链表 链表是比较常见的数据结构了,但是因为 redis 是用 C 写的,所以在不依赖第三方库的情况下只能自己写一个了,redis 的链表是个有头的链表,而且是无环的,具体的结构我也找了 github 上最早版本的代码
-typedef struct listNode {
- // 前置节点
- struct listNode *prev;
- // 后置节点
- struct listNode *next;
- // 值
- void *value;
-} listNode;
-
-typedef struct list {
- // 链表表头
- listNode *head;
- // 当前节点,也可以说是最后节点
- listNode *tail;
- // 节点复制函数
- void *(*dup)(void *ptr);
- // 节点值释放函数
- void (*free)(void *ptr);
- // 节点值比较函数
- int (*match)(void *ptr, void *key);
- // 链表包含的节点数量
- unsigned int len;
-} list;
-代码地址是这个https://github.com/antirez/redis/blob/2.2/src/adlist.h 可以看下节点是由listNode承载的,包括值和一个指向前节点跟一个指向后一节点的两个指针,然后值是 void 指针类型,所以可以承载不同类型的值 然后是 list结构用来承载一个链表,包含了表头,和表尾,复制函数,释放函数和比较函数,还有链表长度,因为包含了前两个节点,找到表尾节点跟表头都是 O(1)的时间复杂度,还有节点数量,其实这个跟 SDS 是同一个做法,就是空间换时间,这也是写代码里比较常见的做法,以此让一些高频的操作提速。
-字典 字典也是个常用的数据结构,其实只是叫法不同,数据结构中叫 hash 散列,Java 中叫 Map,PHP 中是数组 array,Python 中也叫字典 dict,因为纯 C 语言本身不带这些数据结构,所以这也是个痛并快乐着的过程,享受 C 语言的高性能的同时也要接受它只提供了语言的基本功能的现实,各种轮子都需要自己造,redis 同样实现了自己的字典 下面来看看代码
-typedef struct dictEntry {
- void *key;
- void *val;
- struct dictEntry *next;
-} dictEntry;
-
-typedef struct dictType {
- unsigned int (*hashFunction)(const void *key);
- void *(*keyDup)(void *privdata, const void *key);
- void *(*valDup)(void *privdata, const void *obj);
- int (*keyCompare)(void *privdata, const void *key1, const void *key2);
- void (*keyDestructor)(void *privdata, void *key);
- void (*valDestructor)(void *privdata, void *obj);
-} dictType;
-
-/* This is our hash table structure. Every dictionary has two of this as we
- * implement incremental rehashing, for the old to the new table. */
-typedef struct dictht {
- dictEntry **table;
- unsigned long size;
- unsigned long sizemask;
- unsigned long used;
-} dictht;
-
-typedef struct dict {
- dictType *type;
- void *privdata;
- dictht ht[2];
- int rehashidx; /* rehashing not in progress if rehashidx == -1 */
- int iterators; /* number of iterators currently running */
-} dict;
-看了下这个 2.2 版本的代码跟最新版的其实也差的不是很多,所以还是照旧用老代码,可以看到上面四个结构体中,其实只有三个是存储数据用的,dictType 是用来放操作函数的,那么三个存放数据的结构体分别是干嘛的,这时候感觉需要一个图来说明比较好,稍等,我去画个图~ 这个图看着应该比较清楚这些都是用来干嘛的了,dict 是我们的主体结构,它有一个指向 dictType 的指针,这里面包含了字典的操作函数,然后是一个私有数据指针,接下来是一个 dictht 的数组,包含两个dictht,这个就是用来存数据的了,然后是 rehashidx 表示重哈希的状态,当是-1 的时候表示当前没有重哈希,iterators 表示正在遍历的迭代器的数量。 首先说说为啥需要有两个 dictht,这是因为字典 dict 这个数据结构随着数据量的增减,会需要在中途做扩容或者缩容操作,如果只有一个的话,对它进行扩容缩容时会影响正常的访问和修改操作,或者说保证正常查询,修改的正确性会比较复杂,并且因为需要高效利用空间,不能一下子申请一个非常大的空间来存很少的数据。当 dict 中 dictht 中的数据量超过 size 的时候负载就超过了 1,就需要进行扩容,这里的其实跟 Java 中的 HashMap 比较类似,超过一定的负载之后进行扩容。这里为啥 size 会超过 1 呢,可能有部分不了解这类结构的同学会比较奇怪,其实就是上图中画的,在数据结构中对于散列的冲突有几类解决方法,比如转换成链表,二次散列,找下个空槽等,这里就使用了链表法,或者说拉链法。当一个新元素通过 hashFunction 得出的 key 跟 sizemask 取模之后的值相同了,那就将其放在原来的节点之前,变成链表挂在数组 dictht.table下面,放在原有节点前是考虑到可能会优先访问。 忘了说明下 dictht 跟 dictEntry 的关系了,dictht 就是个哈希表,它里面是个dictEntry 的二维数组,而 dictEntry 是个包含了 key-value 结构之外还有一个 next 指针,因此可以将哈希冲突的以链表的形式保存下来。 在重点说下重哈希,可能同样写 Java 的同学对这个比较有感觉,跟 HashMap 一样,会以 2 的 N 次方进行扩容,那么扩容的方法就会比较简单,每个键重哈希要不就在原来这个槽,要不就在原来的槽加原 dictht.size 的位置;然后是重头戏,具体是怎么做扩容呢,其实这里就把第二个 ht 用上了,其实这两个hashtable 的具体作用有点类似于 jvm 中的两个 survival 区,但是又不全一样,因为 redis 在扩容的时候是采用的渐进式地重哈希,什么叫渐进式的呢,就是它不是像 jvm 那种标记复制的模式直接将一个 eden 区和原来的 survival 区存活的对象复制到另一个 survival 区,而是在每一次添加,删除,查找或者更新操作时,都会额外的帮忙搬运一部分的原 dictht 中的数据,这里会根据 rehashidx 的值来判断,如果是-1 表示并没有在重哈希中,如果是 0 表示开始重哈希了,然后rehashidx 还会随着每次的帮忙搬运往上加,但全部被搬运完成后 rehashidx 又变回了-1,又可以扯到Java 中的 Concurrent HashMap, 他在扩容的时候也使用了类似的操作。
+ 2020年中总结
+ /2020/07/11/2020%E5%B9%B4%E4%B8%AD%E6%80%BB%E7%BB%93/
+ 很快2020 年就过了一半了,而且是今年这么特殊的一年,很多事情都发生的出乎意料,疫情这个绕不过去的话题,之前写了点比较愤青的文字,感觉不太适合发出来就烂在草稿箱里吧,这个目前一大影响估计是今年都没办法完全摘下口罩了,前面几个月来回杭州都开车,因为彭埠大桥不通行了,实在是非常不方便,每条路都灰常堵,心累,吐槽下杭州的交通规划和交警同志,工作实在做的不咋地。
+另外一件是就是蜗壳,从前不知道黝黑蜗壳是啥意思,只是经常会在他的视频里看到,大学的时候在缘网下了一个集锦,炒鸡帅气,各种空接扣篮,越来越能明白那句“你永远不知道意外和明天不知道哪个会先来,且行且珍惜”的含义,只是听了很多道理,依然活不好这一生,知易行难,王阳明真的是这方面的大师,有空可以看看这方面的书,一直想写写我跟篮球跟蜗壳的这十几年,争取能早日写好吧,不过得找个静得下来的时候写。
+正事方面上半年还是挺让人失望的,没有达成一些目标,应该还是能力不足吧,技术方面分析一下还是停留在看的表面层,有些实操的,或者结合业务场景的能力不太行,算是在坚持写写 blog,主要是被这个每周一篇的目标推着走,有时会比较焦虑,内容产出也还比较差,希望能在后面有些改善,可能会降低频率,只是觉得降低了也不一定能有比较好的提升,无法战胜自己的惰性,所以暂时还是坚持下这个目标吧,还有就是 coding 能力,有时候也应该刷刷题,提升思维敏捷度,大脑用太少可能生锈了,况且本来就不是很有优势,虽然失望也只能继续努力吧,日拱一卒,来日方长,加油吧~😔
+还有就是跑步减肥了,截止今天,上半年跑了 136 公里了,因为疫情影响,农历年后是从 4 月 17 号开始跑的,去年跑到了 300 公里,奖励自己了一个手表(真的挺后悔的,还不如 200 块买个手表),今年希望可以能在这个基础上再进一步,一直跟领导说,跑步算是我坚持下来的唯一一个好习惯了,618 买了个跑步机,周末回家了可以不受天气影响的多跑跑,不过如果天气好可能还是会出去跑跑,跑步机跑道多少还是有点拘束,只是感觉可能是我还是吃得太多了🤦♂️,效果不是很明显,还在 80 这个坎徘徊,等于浪费了大半年,可能是年初的项目太费心力,压力比较大,吃得更多,是不是可以算工伤😄,这方面也需要好好调整,可以放得开一点,虽然不太可能一下子到位,但是总要去努力下,随着年龄成长总要承担更多,也要看得开一点,没法事事如愿,尽力就好了,减肥这个事情还在结合一些俯卧撑啥的,希望也能坚持下去,加油吧,不知道原话怎么说的,意思是人类最大的勇敢就是看透了人世间的苦难,仍然热爱生活。我当然没可能让内心变得这么强大,试着去努力吧,奥力给!
]]>
- Redis
- 数据结构
- C
- 源码
- Redis
+ 生活
+ 年中总结
+ 2020
- redis
- 数据结构
- 源码
+ 生活
+ 2020
+ 年中总结
@@ -8254,65 +8259,132 @@ typedef struct dict {
- 《长安的荔枝》读后感
- /2022/07/17/%E3%80%8A%E9%95%BF%E5%AE%89%E7%9A%84%E8%8D%94%E6%9E%9D%E3%80%8B%E8%AF%BB%E5%90%8E%E6%84%9F/
- 断断续续地看完了马伯庸老师的《长安的荔枝》,一开始是看这本书在排行榜排得很高,又是马伯庸的,之前看过他的《古董局中局》,还是很有意思的,而且正好是比较短的,不过前后也拖了蛮久才看完,看完后读了下马老师自己写的后记,就特别有感触。 整个故事是围绕一个上林署监事李善德被委任一项给贵妃送荔枝的差事展开,“长安回望绣成堆,山顶千门次第开,一骑红尘妃子笑,无人知是荔枝来”,以前没细究过这个送荔枝的过程,但是以以前的运输速度和保鲜条件,感觉也不是太现实,所以主人公一开始就以为只是像以往一样是送荔枝干这种,能比较方便运输,不容易变质的,结果发现其实是同僚在坑他,这次是要在贵妃生辰的时候给贵妃送来新鲜的岭南荔枝,用比较时兴的词来说,这就是个送命题啊,鲜荔枝一日色变,两日香变,三日味变,同僚的还有杜甫跟韩承,都觉得老李可以直接写休书了,保全家人,不然就是全家送命,李善德也觉得基本算是判刑了,而且其实是这事被转了几次,最后到老李所在的上林署,主管为了骗他接下这个活还特意在文书上把荔枝鲜的“鲜”字贴住,那会叫做“贴黄”,变成了荔枝“煎”,所以说官场险恶,大家都想把这烫手山芋丢出去,结果丢到了我们老实的老李头上,但是从接到这个通知到贵妃的生辰六月初一还有挺长的时间,其实这个活虽然送命,但是在前期这个“荔枝使”也基本就是类似带着尚方宝剑,御赐黄马褂的职位,随便申请经费,不必像常规的部门费用需要定预算,申请后再层层审批,而是特事特批特办的耍赖做法,所以在这段时间是能够潇洒挥霍一下的。其实可以好好地捞一波给妻女,然后写下和离,在自己死后能让她们过的好一些,但最后还是在杜甫的一番劝导下做出了尝试一番的决定,因为也没其他办法,既是退无可退,何不向前拼死一搏,其实说到这,我觉得看这本书感觉有所收获的第一点,有时候总觉得事情没戏了,想躺平放弃了,但是这样其实这个结果是不会变好的,尝试努力,拼尽全力搏一搏,说不定会有所改观,至少不会变更坏了。
+ 搬运两个 StackOverflow 上的 Mysql 编码相关的问题解答
+ /2022/01/16/%E6%90%AC%E8%BF%90%E4%B8%A4%E4%B8%AA-StackOverflow-%E4%B8%8A%E7%9A%84-Mysql-%E7%BC%96%E7%A0%81%E7%9B%B8%E5%85%B3%E7%9A%84%E9%97%AE%E9%A2%98%E8%A7%A3%E7%AD%94/
+ Mysql 字符编码和排序规则这个一直是属于一知半解的状态,知道 utf8 跟 utf8mb4 的区别主要是能不能支持 emoji,但是具体后面配置的排序规则是用来干嘛,或者有什么区别,应该使用哪个,所以在 stackoverflow 上找了下,有两个比较不错的解答,就搬过来并且配合机翻做了点修改
+For those people still arriving at this question in 2020 or later, there are newer options that may be better than both of these. For example, utf8mb4_0900_ai_ci.
+All these collations are for the UTF-8 character encoding. The differences are in how text is sorted and compared.
+_unicode_ci and _general_ci are two different sets of rules for sorting and comparing text according to the way we expect. Newer versions of MySQL introduce new sets of rules, too, such as _0900_ai_ci for equivalent rules based on Unicode 9.0 - and with no equivalent _general_ci variant. People reading this now should probably use one of these newer collations instead of either _unicode_ci or _general_ci. The description of those older collations below is provided for interest only.
+MySQL is currently transitioning away from an older, flawed UTF-8 implementation. For now, you need to use utf8mb4 instead of utf8 for the character encoding part, to ensure you are getting the fixed version. The flawed version remains for backward compatibility, though it is being deprecated.
+Key differences
+utf8mb4_unicode_ci is based on the official Unicode rules for universal sorting and comparison, which sorts accurately in a wide range of languages.
+utf8mb4_general_ci is a simplified set of sorting rules which aims to do as well as it can while taking many short-cuts designed to improve speed. It does not follow the Unicode rules and will result in undesirable sorting or comparison in some situations, such as when using particular languages or characters.
+On modern servers, this performance boost will be all but negligible. It was devised in a time when servers had a tiny fraction of the CPU performance of today’s computers.
+Benefits of utf8mb4_unicode_ci over utf8mb4_general_ci
+utf8mb4_unicode_ci, which uses the Unicode rules for sorting and comparison, employs a fairly complex algorithm for correct sorting in a wide range of languages and when using a wide range of special characters. These rules need to take into account language-specific conventions; not everybody sorts their characters in what we would call ‘alphabetical order’.
+As far as Latin (ie “European”) languages go, there is not much difference between the Unicode sorting and the simplified utf8mb4_general_cisorting in MySQL, but there are still a few differences:
+For examples, the Unicode collation sorts “ß” like “ss”, and “Œ” like “OE” as people using those characters would normally want, whereas utf8mb4_general_cisorts them as single characters (presumably like “s” and “e” respectively).
+Some Unicode characters are defined as ignorable, which means they shouldn’t count toward the sort order and the comparison should move on to the next character instead. utf8mb4_unicode_cihandles these properly.
+In non-latin languages, such as Asian languages or languages with different alphabets, there may be a lot more differences between Unicode sorting and the simplified utf8mb4_general_cisorting. The suitability of utf8mb4_general_ciwill depend heavily on the language used. For some languages, it’ll be quite inadequate.
+What should you use?
+There is almost certainly no reason to use utf8mb4_general_cianymore, as we have left behind the point where CPU speed is low enough that the performance difference would be important. Your database will almost certainly be limited by other bottlenecks than this.
+In the past, some people recommended to use utf8mb4_general_ciexcept when accurate sorting was going to be important enough to justify the performance cost. Today, that performance cost has all but disappeared, and developers are treating internationalization more seriously.
+There’s an argument to be made that if speed is more important to you than accuracy, you may as well not do any sorting at all. It’s trivial to make an algorithm faster if you do not need it to be accurate. So, utf8mb4_general_ciis a compromise that’s probably not needed for speed reasons and probably also not suitable for accuracy reasons.
+One other thing I’ll add is that even if you know your application only supports the English language, it may still need to deal with people’s names, which can often contain characters used in other languages in which it is just as important to sort correctly. Using the Unicode rules for everything helps add peace of mind that the very smart Unicode people have worked very hard to make sorting work properly.
+What the parts mean
+Firstly, ci is for case-insensitive sorting and comparison. This means it’s suitable for textual data, and case is not important. The other types of collation are cs (case-sensitive) for textual data where case is important, and bin, for where the encoding needs to match, bit for bit, which is suitable for fields which are really encoded binary data (including, for example, Base64). Case-sensitive sorting leads to some weird results and case-sensitive comparison can result in duplicate values differing only in letter case, so case-sensitive collations are falling out of favor for textual data - if case is significant to you, then otherwise ignorable punctuation and so on is probably also significant, and a binary collation might be more appropriate.
+Next, unicode or general refers to the specific sorting and comparison rules - in particular, the way text is normalized or compared. There are many different sets of rules for the utf8mb4 character encoding, with unicode and general being two that attempt to work well in all possible languages rather than one specific one. The differences between these two sets of rules are the subject of this answer. Note that unicode uses rules from Unicode 4.0. Recent versions of MySQL add the rulesets unicode_520 using rules from Unicode 5.2, and 0900 (dropping the “unicode_” part) using rules from Unicode 9.0.
+And lastly, utf8mb4 is of course the character encoding used internally. In this answer I’m talking only about Unicode based encodings.
+翻译 对于那些在 2020 年或之后仍会遇到这个问题的人,有可能比这两个更好的新选项。例如,utf8mb4_0900_ai_ci。
+所有这些排序规则都用于 UTF-8 字符编码。不同之处在于文本的排序和比较方式。
+_unicode_ci和 _general_ci是两组不同的规则,用于按照我们期望的方式对文本进行排序和比较。较新版本的 MySQL 也引入了新的规则集,例如 _0900_ai_ci用于基于 Unicode 9.0 的等效规则 - 并且没有等效的 _general_ci变体。现在阅读本文的人可能应该使用这些较新的排序规则之一,而不是 _unicode_ci或 _general_ci。下面对那些较旧的排序规则的描述仅供参考。
+MySQL 目前正在从旧的、有缺陷的 UTF-8 实现过渡。现在,您需要使用 utf8mb4 而不是 utf8作为字符编码部分,以确保您获得的是固定版本。有缺陷的版本仍然是为了向后兼容,尽管它已被弃用。
+主要区别
+utf8mb4_unicode_ci基于官方 Unicode 规则进行通用排序和比较,可在多种语言中准确排序。
+utf8mb4_general_ci是一组简化的排序规则,旨在尽其所能,同时采用许多旨在提高速度的捷径。它不遵循 Unicode 规则,并且在某些情况下会导致不希望的排序或比较,例如在使用特定语言或字符时。
+在现代服务器上,这种性能提升几乎可以忽略不计。它是在服务器的 CPU 性能只有当今计算机的一小部分时设计的。
+utf8mb4_unicode_ci 相对于 utf8mb4_general_ci的优势
+utf8mb4_unicode_ci使用 Unicode 规则进行排序和比较,采用相当复杂的算法在多种语言中以及在使用多种特殊字符时进行正确排序。这些规则需要考虑特定语言的约定;不是每个人都按照我们所说的“字母顺序”对他们的字符进行排序。
+就拉丁语(即“欧洲”)语言而言,Unicode 排序和 MySQL 中简化的 utf8mb4_general_ci排序没有太大区别,但仍有一些区别:
+例如,Unicode 排序规则将“ß”排序为“ss”,将“Œ”排序为“OE”,因为使用这些字符的人通常需要这些字符,而 utf8mb4_general_ci将它们排序为单个字符(大概分别像“s”和“e” )。
+一些 Unicode 字符被定义为可忽略,这意味着它们不应该计入排序顺序,并且比较应该转到下一个字符。 utf8mb4_unicode_ci正确处理这些。
+在非拉丁语言中,例如亚洲语言或具有不同字母的语言,Unicode 排序和简化的 utf8mb4_general_ci排序之间可能存在更多差异。 utf8mb4_general_ci的适用性在很大程度上取决于所使用的语言。对于某些语言,这将是非常不充分的。
+你应该用什么?
+几乎可以肯定没有理由再使用 utf8mb4_general_ci,因为我们已经将 CPU 速度低到会严重影响性能表现的时代远抛在脑后了。您的数据库几乎肯定会受到除此之外的其他瓶颈的限制。
+过去,有些人建议使用 utf8mb4_general_ci,除非准确排序足够重要以证明性能成本是合理的。如今,这种性能成本几乎消失了,开发人员正在更加认真地对待国际化。
+有一个论点是,如果速度对您来说比准确性更重要,那么您可能根本不进行任何排序。如果您不需要准确的算法,那么使算法更快是微不足道的。因此,utf8mb4_general_ci是一种折衷方案,出于速度原因可能不需要,也可能出于准确性原因也不适合。
+我要补充的另一件事是,即使您知道您的应用程序仅支持英语,它可能仍需要处理人名,这些人名通常包含其他语言中使用的字符,在这些语言中正确排序同样重要.对所有事情都使用 Unicode 规则有助于让您更加安心,因为非常聪明的 Unicode 人员已经非常努力地工作以使排序正常工作。
+其余各个部分是什么意思
+首先, ci 用于不区分大小写的排序和比较。这意味着它适用于文本数据,大小写并不重要。其他类型的排序规则是 cs(区分大小写),用于区分大小写的文本数据,以及 bin,用于编码需要匹配的地方,逐位匹配,适用于真正编码二进制数据的字段(包括,用于例如,Base64)。区分大小写的排序会导致一些奇怪的结果,区分大小写的比较可能会导致重复值仅在字母大小写上有所不同,因此区分大小写的排序规则对文本数据不受欢迎 - 如果大小写对您很重要,那么标点符号就可以忽略等等可能也很重要,二进制排序规则可能更合适。
+接下来,unicode 或general 指的是具体的排序和比较规则——特别是文本被规范化或比较的方式。 utf8mb4 字符编码有许多不同的规则集,其中 unicode 和 general 是两种,它们试图在所有可能的语言中都很好地工作,而不是在一种特定的语言中。这两组规则之间的差异是此答案的主题。请注意,unicode 使用 Unicode 4.0 中的规则。 MySQL 的最新版本使用 Unicode 5.2 的规则添加规则集 unicode_520,使用 Unicode 9.0 的规则添加 0900(删除“unicode_”部分)。
+最后,utf8mb4 当然是内部使用的字符编码。在这个答案中,我只谈论基于 Unicode 的编码。
+utf8 和 utf8mb4 编码有什么区别 UTF-8 is a variable-length encoding. In the case of UTF-8, this means that storing one code point requires one to four bytes. However, MySQL’s encoding called “utf8” (alias of “utf8mb3”) only stores a maximum of three bytes per code point.
+So the character set “utf8”/“utf8mb3” cannot store all Unicode code points: it only supports the range 0x000 to 0xFFFF, which is called the “Basic Multilingual Plane “. See also Comparison of Unicode encodings .
+This is what (a previous version of the same page at)the MySQL documentation has to say about it:
+
+The character set named utf8[/utf8mb3] uses a maximum of three bytes per character and contains only BMP characters. As of MySQL 5.5.3, the utf8mb4 character set uses a maximum of four bytes per character supports supplemental characters:
+
+For a BMP character, utf8[/utf8mb3] and utf8mb4 have identical storage characteristics: same code values, same encoding, same length.
+For a supplementary character, utf8[/utf8mb3] cannot store the character at all , while utf8mb4 requires four bytes to store it. Since utf8[/utf8mb3] cannot store the character at all, you do not have any supplementary characters in utf8[/utf8mb3] columns and you need not worry about converting characters or losing data when upgrading utf8[/utf8mb3] data from older versions of MySQL.
+
+
+So if you want your column to support storing characters lying outside the BMP (and you usually want to), such as emoji , use “utf8mb4”. See also What are the most common non-BMP Unicode characters in actual use? .
+译文 UTF-8 是一种可变长度编码。对于 UTF-8,这意味着存储一个代码点需要一到四个字节。但是,MySQL 的编码称为“utf8”(“utf8mb3”的别名)每个代码点最多只能存储三个字节。
+所以字符集“utf8”/“utf8mb3”不能存储所有的Unicode码位:它只支持0x000到0xFFFF的范围,被称为“基本多语言平面 ”。另请参阅 Unicode 编码比较 。
+这就是(同一页面的先前版本)MySQL 文档 不得不说的:
+
+名为 utf8[/utf8mb3] 的字符集每个字符最多使用三个字节,并且仅包含 BMP 字符。从 MySQL 5.5.3 开始,utf8mb4 字符集每个字符最多使用四个字节,支持补充字符:
+
+对于 BMP 字符,utf8[/utf8mb3] 和 utf8mb4 具有相同的存储特性:相同的代码值、相同的编码、相同的长度。
+对于补充字符,utf8[/utf8mb3] 根本无法存储该字符 ,而 utf8mb4 需要四个字节来存储它。由于 utf8[/utf8mb3] 根本无法存储字符,因此您在 utf8[/utf8mb3] 列中没有任何补充字符,您不必担心从旧版本升级 utf8[/utf8mb3] 数据时转换字符或丢失数据mysql。
+
+
+因此,如果您希望您的列支持存储位于 BMP 之外的字符(并且您通常希望这样做),例如 emoji ,请使用“utf8mb4”。另请参阅
+实际使用中最常见的非 BMP Unicode 字符是什么? 。
+]]>
+
+ Mysql
+
+
+ mysql
+ 字符集
+ 编码
+ utf8
+ utf8mb4
+ utf8mb4_0900_ai_ci
+ utf8mb4_unicode_ci
+ utf8mb4_general_ci
+
+
+
+ 屯菜惊魂记
+ /2022/04/24/%E5%B1%AF%E8%8F%9C%E6%83%8A%E9%AD%82%E8%AE%B0/
+ 因某国际大都市的给力表现,昨儿旁边行政区启动应急响应,同事早上就在群里说要去超市买菜了,到了超市人还特别多,由于来的就是我们经常去的那家超市,一方面为了安全,另一方面是怕已经抢不到了,就去了另一家比较远的超市,开车怕没车位就骑了小电驴,还下着小雨,结果到了超市差不多 12 点多,超市里出来的人都是推着一整车一整车的物资,有些比较像我,整箱的泡面,好几提纸巾,还有各种吃的,都是整箱整箱的,进了超市发现结账包括自助结账的都排很长的队,到了蔬菜货架附近,差点哼起那首歌“空空如也~”,新鲜蔬菜基本已被抢空,只剩下一些卖相不太好的土豆番薯之类的,也算是意料之外情理之中了,本来以为这家超市稍微离封控区远一些会空一点,结果就是所谓的某大都市封控了等物资,杭州市是屯了物资等封控,新鲜蔬菜没了我们也只能买点其他的,神奇的是水果基本都在,可能困难时期水果不算必需品了?还是水果基本人人都已经储备了很多,不太能理解,虽然水果还在,但是称重的地方也还有好多人排队,我们采取了并行策略,LD 在那排队,遥控指挥我去拿其他物资,拿了点碱水面,黑米,那黑米的时候还闹了个乌龙,因为前面就是散装鸡蛋的堆货的地方,结果我们以为是在那后面排队,结果称重那个在那散步了,我们还在那排队,看到后面排队,那几个挑的人也该提醒下吧,几个鸡蛋挑了半天,看看人家大妈,直接拿了四盘,看了下牛奶货架也比较空,不过还有致优跟优倍,不过不算很实惠,本来想买,只是后来赶着去结账,就给忘了,称好了黑米去看了下肉,结果肉也没了,都在买猪蹄,我们也不太爱吃猪蹄,就买了点鸡胸肉,整体看起来我们买的东西真的有点格格不入,不买泡面(因为 LD 不让买了),也不屯啥米和鸡蛋,其实鸡蛋已经买了,米也买了,其他的本身冰箱小也放不下太多东西,我是觉得还可能在屯一点这那的,LD 觉得太多了,基本的米面油有了,其他调味品什么也有了。后面就是排队结账,我去排的时候刚好前面一个小伙子跟大妈在争执,大妈说我们差不多时间来的,你要排前面就前面,小伙子有点不高兴,觉得她就是插队,哈哈,平时一般这种剧情都是发生在我身上的,这会看着前面的吵起来还是很开心的,终于有跟我一样较真的人了,有时候总觉得我是个很纠结,很较真的人,但是我现在慢慢认可了这种较真,如果没有人指出来这种是插队行为,是不对的,就会有越来越多的人觉得是可以随意插队的,正确的事应该要坚持,很多情况大家总是觉得多一事不如少一事,鸡毛蒜皮的没什么好计较的,正是这种想法,那么多人才不管任何规则,反而搞得像遵守规则都是傻 X 似的。回到屯物资,后面结账排到队了也没来得及买原来想买的花生牛奶什么的,毕竟那么多人排着队,回家后因为没有蔬菜,结果就只能吃干菜汤和饭了
]]>
- 读后感
生活
生活
- 读后感
+ 囤物资
- 一个 nginx 的简单记忆点
- /2022/08/21/%E4%B8%80%E4%B8%AA-nginx-%E7%9A%84%E7%AE%80%E5%8D%95%E8%AE%B0%E5%BF%86%E7%82%B9/
- 上周在处理一个 nginx 配置的时候,发现了一个之前不理解的小点,说一个场景,就是我们一般的处理方式就是一个 ip 端口只能配置一个域名的服务,比如 https://nicksxs.me 对应配置到 127.0.0.1:443,如果我想要把 https://nicksxs.com 也解析到这个服务器,并转发到不同的下游,这里就需要借助所谓的 SNI 的功能
-Server Name Indication A more generic solution for running several HTTPS servers on a single IP address is TLS Server Name Indication extension (SNI, RFC 6066), which allows a browser to pass a requested server name during the SSL handshake and, therefore, the server will know which certificate it should use for the connection. SNI is currently supported by most modern browsers, though may not be used by some old or special clients.来源 机翻一下:在单个 IP 地址上运行多个 HTTPS 服务器的更通用的解决方案是 TLS 服务器名称指示扩展(SNI,RFC 6066),它允许浏览器在 SSL 握手期间传递请求的服务器名称,因此,服务器将知道哪个 它应该用于连接的证书。 目前大多数现代浏览器都支持 SNI,但某些旧的或特殊的客户端可能不使用 SNI。
-首先我们需要确认 sni 已被支持 在实际的配置中就可以这样
-stream {
- map $ssl_preread_server_name $stream_map {
- nicksxs.me nme;
- nicksxs.com ncom;
- }
-
- upstream nme {
- server 127.0.0.1:8000;
- }
- upstream ncom {
- server 127.0.0.1:8001;
- }
-
- server {
- listen 443 reuseport;
- proxy_pass $stream_map ;
- ssl_preread on ;
- }
-}
-类似这样,但是这个理解是非常肤浅和不完善的,只是简单记忆下,后续再进行补充完整
-还有一点就是我们在配置的时候经常配置就是 server_name,但是会看到直接在使用 ssl_server_name, 其实在listen 标识了 ssl, 对应的 ssl_server_name 就等于 server_name,不需要额外处理了。
+ 看完了扫黑风暴,聊聊感想
+ /2021/10/24/%E7%9C%8B%E5%AE%8C%E4%BA%86%E6%89%AB%E9%BB%91%E9%A3%8E%E6%9A%B4-%E8%81%8A%E8%81%8A%E6%84%9F%E6%83%B3/
+ 一直在想这篇怎么写,看了这部剧其实对我的一些观念是有影响的,应该是在 9 月份就看完了,到现在可能才会稍微平静一点,一开始是没有想看这部剧,因为同期有一部差不多同名的电影,被投诉了对湖南埋尸案家属伤害很大,我以为就是投诉的这部电视剧,后来同事跟我说不是,所以就想着看一下,但是没有马上看,因为一直不喜欢追这种比较纠结的剧,当时看人民的名义,就是后面等不了了直接看了小说,所以差不多是等到更完了才看的。
+尝试保持一个比较冷静的状态来聊聊,在看的时候有一点感想就是如果要剧里的坏人排个名,因为明眼看都是孙兴是个穷凶极恶的坏人,干尽了坏事,而且可能是演员表演地好,让人真的恨的牙痒痒,但是更多地还是停留在那些剧情中的表现和他的表情,其实对应的真实案例有更多的,这里尽量不展开,有兴趣可以自行搜索关键字,所以其实我想排个名的话,孙兴的母亲应该是我心目中是造成这个结果的比较大占比的始作俑者,因为是方方面面的,包括对林汉的栽赃迫害,最后串起来是因为他看到了孙兴又出来了,就是那句老话,撒了一个谎以后就要用无数个谎来圆,贺芸为了孙兴,作了第一个恶以后就用了一系列的丧心病狂的操作来保护孙兴,而且这之后所做的事情一件比一件可怕,并且如果不是督导组各种想方设法地去破解谜题,这个事情还可以一直被通过各种操作瞒下去,而孙兴还可以继续地为虎作伥,当然其他的包括高明远以及后面的王政,当然是为了这个操作也提供的各种方式的帮助,甚至是主导了这些操作,但是这里贺芸还是在这个位子上能够通过权力做出非常关键的动作,包括栽赃林汉,并且搞掉了李成阳。其中还有一点是我对剧情设计的质疑,也是我前面提到过一点,因为里面孙兴好像是很爱他的母亲贺芸,似乎想表达的是孙兴作的恶是因为得不到母爱,并且个人感觉如果是一个比较敬爱自己母亲的儿子,似乎应该有所畏惧,对他的行为也会有所限制,不应该变成这样一个无恶不作的恶霸,这也是我一直以来的观点,很多人作恶太多可能是因为没有信仰,不管是信基督耶稣还是信道教佛教,总归有一些制约,当然不是说就绝对不会作恶,只是偏向于有所畏惧敬畏,除了某绿哈。
+而对于其他的人感觉演技都不错,只是最后有一些虎头蛇尾吧,不知道是不是审核的原因,也不细说了怕被请喝茶,还有提一点就是麦佳的这个事情,她其实是里面很惨的一个人,把高明远当成最亲近的人,而其实真相令人感觉不寒而栗,杀父杀母的仇人,对于麦佳这个演员,一直觉得印象深刻,后来才想起来就是在爱情公寓里演被关谷救了要以身相遇的那个女孩,长相其实蛮令人印象深刻的,但好像也一直不温不火,不过也不能说演技很好吧,只是在这里演的任务真的是很可怜了,剧情设计里也应该是个很重要的串联人物,最终被高明远献给了大佬,这里扯开一点,好像有的观点说贺芸之前也是这样的,只是一种推测了。
+看完这部剧其实有很多想说的,但是也为了不被请喝茶,尽量少说了,只想说珍爱生命,还是自己小心吧
]]>
- nginx
+ 生活
- nginx
+ 生活
+ 影评
- 屯菜惊魂记
- /2022/04/24/%E5%B1%AF%E8%8F%9C%E6%83%8A%E9%AD%82%E8%AE%B0/
- 因某国际大都市的给力表现,昨儿旁边行政区启动应急响应,同事早上就在群里说要去超市买菜了,到了超市人还特别多,由于来的就是我们经常去的那家超市,一方面为了安全,另一方面是怕已经抢不到了,就去了另一家比较远的超市,开车怕没车位就骑了小电驴,还下着小雨,结果到了超市差不多 12 点多,超市里出来的人都是推着一整车一整车的物资,有些比较像我,整箱的泡面,好几提纸巾,还有各种吃的,都是整箱整箱的,进了超市发现结账包括自助结账的都排很长的队,到了蔬菜货架附近,差点哼起那首歌“空空如也~”,新鲜蔬菜基本已被抢空,只剩下一些卖相不太好的土豆番薯之类的,也算是意料之外情理之中了,本来以为这家超市稍微离封控区远一些会空一点,结果就是所谓的某大都市封控了等物资,杭州市是屯了物资等封控,新鲜蔬菜没了我们也只能买点其他的,神奇的是水果基本都在,可能困难时期水果不算必需品了?还是水果基本人人都已经储备了很多,不太能理解,虽然水果还在,但是称重的地方也还有好多人排队,我们采取了并行策略,LD 在那排队,遥控指挥我去拿其他物资,拿了点碱水面,黑米,那黑米的时候还闹了个乌龙,因为前面就是散装鸡蛋的堆货的地方,结果我们以为是在那后面排队,结果称重那个在那散步了,我们还在那排队,看到后面排队,那几个挑的人也该提醒下吧,几个鸡蛋挑了半天,看看人家大妈,直接拿了四盘,看了下牛奶货架也比较空,不过还有致优跟优倍,不过不算很实惠,本来想买,只是后来赶着去结账,就给忘了,称好了黑米去看了下肉,结果肉也没了,都在买猪蹄,我们也不太爱吃猪蹄,就买了点鸡胸肉,整体看起来我们买的东西真的有点格格不入,不买泡面(因为 LD 不让买了),也不屯啥米和鸡蛋,其实鸡蛋已经买了,米也买了,其他的本身冰箱小也放不下太多东西,我是觉得还可能在屯一点这那的,LD 觉得太多了,基本的米面油有了,其他调味品什么也有了。后面就是排队结账,我去排的时候刚好前面一个小伙子跟大妈在争执,大妈说我们差不多时间来的,你要排前面就前面,小伙子有点不高兴,觉得她就是插队,哈哈,平时一般这种剧情都是发生在我身上的,这会看着前面的吵起来还是很开心的,终于有跟我一样较真的人了,有时候总觉得我是个很纠结,很较真的人,但是我现在慢慢认可了这种较真,如果没有人指出来这种是插队行为,是不对的,就会有越来越多的人觉得是可以随意插队的,正确的事应该要坚持,很多情况大家总是觉得多一事不如少一事,鸡毛蒜皮的没什么好计较的,正是这种想法,那么多人才不管任何规则,反而搞得像遵守规则都是傻 X 似的。回到屯物资,后面结账排到队了也没来得及买原来想买的花生牛奶什么的,毕竟那么多人排着队,回家后因为没有蔬菜,结果就只能吃干菜汤和饭了
+ 是何原因竟让两人深夜奔袭十公里
+ /2022/06/05/%E6%98%AF%E4%BD%95%E5%8E%9F%E5%9B%A0%E7%AB%9F%E8%AE%A9%E4%B8%A4%E4%BA%BA%E6%B7%B1%E5%A4%9C%E5%A5%94%E8%A2%AD%E5%8D%81%E5%85%AC%E9%87%8C/
+ 偶尔来个标题党,不过也是一次比较神奇的经历 上周五下班后跟 LD 约好去吃牛蛙,某个朋友好像对这类都不太能接受,我以前小时候也不常吃,但是这类其实都是口味比较重,没有那种肉本身的腥味,而且肉质比较特殊,吃过几次以后就有点爱上了,这次刚好是 LD 买的新店开业券,比较优惠(我们俩都是有点勤俭持家的,想着小电驴还有三格电,这家店又有点远,骑车单趟大概要 10 公里左右,有点担心,LD 说应该可以的,就一起骑了过去(跟她轮换着骑电驴和共享单车),结果大概离吃牛蛙的店还有一辆公里的时候,电量就报警了,只有最后一个红色的了,一共是五格,最后一格是红色的,提示我们该充电了,这样子是真的有点慌了,之前开了几个月都是还有一两格电的时候就充电了,没有试验过究竟这最后一格电能开多远,总之先到了再说。 这家牛蛙没想到还挺热闹的,我们到那已经快八点了,还有十几个排队的,有个人还想插队(向来是不惯着这种,一边去),旁边刚好是有些商店就逛了下,就跟常规的商业中心差不多,开业的比较早也算是这一边比较核心的商业综合体了,各种品牌都有,而且还有彩票售卖点的,只是不太理解现在的彩票都是兑图案的,而且要 10 块钱一张,我的概念里还是以前 2 块钱一张的双色球,偶尔能中个五块十块的。排队还剩四五个的时候我们就去门口坐着等了,又等了大概二十分钟才排到我们,靠近我们等的里面的位置,好像好几个小女生在那还叫了外卖奶茶,然后各种拍照,小朋友的生活还是丰富多彩的,我们到了就点了蒜蓉的,没有点传说中紫苏的,菜单上画了 N 个🌶,LD 还是想体验下说下次人多点可以试试,我们俩吃怕太辣了吃不消,口味还是不错的,这家貌似是 LD 闺蜜推荐的,口碑有保证。两个人光吃一个蛙锅就差不多了,本来还想再点个其他的,后面实在吃不下了就没点,吃完还是惯例点了个奶茶,不过是真的不好找,太大了。 本来是就回个家的事了,结果就因为前面铺垫的小电驴已经只有一格电了,标题的深夜奔袭十公里就出现了,这个电驴估计续航也虚标挺严重的,电量也是这样,骑的时候显示只有一格电,关掉再开起来又有三格,然后我们回去骑了没一公里就没电了,这下是真的完球了,觉得车子也比较新,直接停外面也不放心,就开始了深夜的十公里推电驴奔袭,LD 看我太累还帮我中间推了一段,虽然是跑过十公里的,但是推着个没电的电驴,还是着实不容易的,LD 也是陪我推着车走,中间好几次说我们把电驴停着打车回去,把电池带回去充满了明天再过来骑车,可能是心态已经转变了,这应该算是一次很特殊的体验,从我们吃完出来大概十点,到最后我们推到小区,大概是过了两个小时的样子,说句深夜也不太过分,把这次这么推车看成了一种意志力的考验,很多事情也都是怕坚持,或者说怕不能坚持,想走得远,没有持续的努力坚持肯定是不行的,所以还是坚持着把车推回来(好吧,我其实主要是怕车被偷,毕竟刚来杭州上学没多久就被偷了自行车留下了阴影),中间感谢 LD,跟我轮着推了一段路,有些下坡的时候还在那坐着用脚蹬一下,离家里大概还有一公里的时候,有个骑电瓶车的大叔还停下来问我们是车破了还是没电了,应该是出于好意吧,最后快到的时候真的非常渴,买了2.5 升的水被我一口气喝了大半瓶,奶茶已经不能起到解渴的作用了,本来以为这样能消耗很多,结果第二天一称还重了,(我的称一定有问题 233
]]>
生活
生活
- 囤物资
@@ -9736,76 +9808,17 @@ typedef struct dict {
- 看完了扫黑风暴,聊聊感想
- /2021/10/24/%E7%9C%8B%E5%AE%8C%E4%BA%86%E6%89%AB%E9%BB%91%E9%A3%8E%E6%9A%B4-%E8%81%8A%E8%81%8A%E6%84%9F%E6%83%B3/
- 一直在想这篇怎么写,看了这部剧其实对我的一些观念是有影响的,应该是在 9 月份就看完了,到现在可能才会稍微平静一点,一开始是没有想看这部剧,因为同期有一部差不多同名的电影,被投诉了对湖南埋尸案家属伤害很大,我以为就是投诉的这部电视剧,后来同事跟我说不是,所以就想着看一下,但是没有马上看,因为一直不喜欢追这种比较纠结的剧,当时看人民的名义,就是后面等不了了直接看了小说,所以差不多是等到更完了才看的。
-尝试保持一个比较冷静的状态来聊聊,在看的时候有一点感想就是如果要剧里的坏人排个名,因为明眼看都是孙兴是个穷凶极恶的坏人,干尽了坏事,而且可能是演员表演地好,让人真的恨的牙痒痒,但是更多地还是停留在那些剧情中的表现和他的表情,其实对应的真实案例有更多的,这里尽量不展开,有兴趣可以自行搜索关键字,所以其实我想排个名的话,孙兴的母亲应该是我心目中是造成这个结果的比较大占比的始作俑者,因为是方方面面的,包括对林汉的栽赃迫害,最后串起来是因为他看到了孙兴又出来了,就是那句老话,撒了一个谎以后就要用无数个谎来圆,贺芸为了孙兴,作了第一个恶以后就用了一系列的丧心病狂的操作来保护孙兴,而且这之后所做的事情一件比一件可怕,并且如果不是督导组各种想方设法地去破解谜题,这个事情还可以一直被通过各种操作瞒下去,而孙兴还可以继续地为虎作伥,当然其他的包括高明远以及后面的王政,当然是为了这个操作也提供的各种方式的帮助,甚至是主导了这些操作,但是这里贺芸还是在这个位子上能够通过权力做出非常关键的动作,包括栽赃林汉,并且搞掉了李成阳。其中还有一点是我对剧情设计的质疑,也是我前面提到过一点,因为里面孙兴好像是很爱他的母亲贺芸,似乎想表达的是孙兴作的恶是因为得不到母爱,并且个人感觉如果是一个比较敬爱自己母亲的儿子,似乎应该有所畏惧,对他的行为也会有所限制,不应该变成这样一个无恶不作的恶霸,这也是我一直以来的观点,很多人作恶太多可能是因为没有信仰,不管是信基督耶稣还是信道教佛教,总归有一些制约,当然不是说就绝对不会作恶,只是偏向于有所畏惧敬畏,除了某绿哈。
-而对于其他的人感觉演技都不错,只是最后有一些虎头蛇尾吧,不知道是不是审核的原因,也不细说了怕被请喝茶,还有提一点就是麦佳的这个事情,她其实是里面很惨的一个人,把高明远当成最亲近的人,而其实真相令人感觉不寒而栗,杀父杀母的仇人,对于麦佳这个演员,一直觉得印象深刻,后来才想起来就是在爱情公寓里演被关谷救了要以身相遇的那个女孩,长相其实蛮令人印象深刻的,但好像也一直不温不火,不过也不能说演技很好吧,只是在这里演的任务真的是很可怜了,剧情设计里也应该是个很重要的串联人物,最终被高明远献给了大佬,这里扯开一点,好像有的观点说贺芸之前也是这样的,只是一种推测了。
-看完这部剧其实有很多想说的,但是也为了不被请喝茶,尽量少说了,只想说珍爱生命,还是自己小心吧
+ 给小电驴上牌
+ /2022/03/20/%E7%BB%99%E5%B0%8F%E7%94%B5%E9%A9%B4%E4%B8%8A%E7%89%8C/
+ 三八节活动的时候下决心买了个小电驴,主要是上下班路上现在通勤条件越来越恶劣了,之前都是觉得坐公交就行了,实际路程就比较短,但是现在或者说大概是年前那两个月差不多就开始了,基本是堵一路,个人感觉是天目山路那边在修地铁,而且蚂蚁的几个空间都在那,上班的时间点都差不多,前一个修地铁感觉挺久了,机动车保有量也越来越多,总体是古墩路就越来越堵,还有个原因就是早上上班的点共享单车都被骑走了,有时候整整走一路都没一辆,有时候孤零零地有一辆基本都是破的;走路其实也是一种选择,但是因为要赶着上班,走得太慢就要很久,可能要 45 分钟这样,走得比较快就一身汗挺难受的。所以考虑自行车和电动车,这里还有一点就是不管是乘公交还是骑共享单车,其实都要从楼下走出去蛮远,公司回来也是,也就是这种通勤方式在准备阶段就花了比较多时间,比如总的从下班到到家的时间是半小时,可能在骑共享单车和公交车上的时间都不到十分钟,就比较难受。觉得这种比例太浪费时间,如果能有这种比较点对点的方式,估计能省时省力不少,前面说的骑共享单车的方式其实在之前是比较可行的,但是后来越来越少车,基本都是每周的前几天,周一到周三都是没有车,走路到公司再冷的天都是走出一身的汗,下雨天就更难受,本来下雨天应该是优先选择坐公交,但是一般下雨天堵车会更严重,而且车子到我上车的那个站,下雨天就挤得不行,总体说下来感觉事情都不打,但是几年下来,还是会挺不爽的。
+电驴看的比较草率,主要是考虑续航,然后锂电池外加 48v 和 24AH,这样一般来讲还是价格比较高的,只是原来没预料到这个限速,以为现在的车子都比较快,但是现在的新国标车子都是 25km/h 的限速,然后 15km/h 都是会要提醒,虽然说有一些特殊的解除限速的方法,但是解了也就 35km/h ,差距不是特别大,而且现在的车子都是比较小,也不太能载东西,特别是上下班路程也不远的情况下,其实不是那么需要速度,就像我朋友说的,可能骑车的时间还不如等红绿灯多,所以就还好,也不打算解除限速,只是品牌上也仔细看,后来选了绿源,目前大部分还是雅迪,爱玛,台羚,绿源,小牛等,路上看的话还是雅迪比较多,不过价格也比较贵一点,还有就是小牛了,是比较新兴的品牌,手机 App 什么的做得比较好,而且也比较贵,最后以相对比较便宜的价格买了个锂电 48V24AH 的小车子,后来发现还是有点不方便的点就是没有比较大的筐,也不好装,这样就是下雨天雨衣什么的比较不方便放。
+聊回来主题上牌这个事情,这个事情也是颇费心力,提车的时候店里的让我跟他早上一起去,但是因为不确定时间,也比较远就没跟着去,因为我是线上买的,线下自提,线下的店可能没啥利润可以拿,就不肯帮忙代上牌,朋友说在线下店里买是可以代上的,自己上牌过程也比较曲折,一开始是头盔没到,然后是等开发票,主要的东西就是需要骑着车子去车管所,不能只自己去,然后需要预约,附近比较近的都是提前一周就预约完了号了,要提前在支付宝上进行预约,比较空的就是店里推荐的景区大队,但是随之而来就是比较蛋疼的,这个景区大队太远了,看下骑车距离有十几公里,所以就有点拖延症,但是总归要上的,不然一直不能开是白买了,上牌的材料主要是车辆合格证,发票,然后车子上的浙品码,在车架上和电池上,然后车架号什么的都要跟合格证上完全对应,整体车子要跟合格证上一毛一样,如果有额外的反光镜,后面副座都需要拆掉,脚踏板要装上,到了那其实还比较顺利,就是十几公里外加那天比较冷,吹得头疼。
]]>
生活
生活
- 影评
-
-
-
- 聊一下 RocketMQ 的消息存储之 MMAP
- /2021/09/04/%E8%81%8A%E4%B8%80%E4%B8%8B-RocketMQ-%E7%9A%84%E6%B6%88%E6%81%AF%E5%AD%98%E5%82%A8/
- 这是个很大的话题了,可能会分成两部分说,第一部分就是所谓的零拷贝 ( zero-copy ),这一块其实也不新鲜,我对零拷贝的概念主要来自这篇文章 ,个人感觉写得非常好,在 rocketmq 中,最大的一块存储就是消息存储,也就是 CommitLog ,当然还有 ConsumeQueue 和 IndexFile,以及其他一些文件,CommitLog 的存储是以一个 1G 大小的文件作为存储单位,写完了就再建一个,那么如何提高这 1G 文件的读写效率呢,就是 mmap,传统意义的读写文件,read,write 都需要由系统调用,来回地在用户态跟内核态进行拷贝切换,
-read ( file, tmp_buf, len) ;
-write ( socket, tmp_buf, len) ;
-
-
-
-
-如上面的图显示的,要在用户态跟内核态进行切换,数据还需要在内核缓冲跟用户缓冲之间拷贝多次,
-
-
-第一步是调用 read,需要在用户态切换成内核态,DMA 模块从磁盘中读取文件,并存储在内核缓冲区,相当于是第一次复制
-数据从内核缓冲区被拷贝到用户缓冲区,read 调用返回,伴随着内核态又切换成用户态,完成了第二次复制
-然后是write 写入,这里也会伴随着用户态跟内核态的切换,数据从用户缓冲区被复制到内核空间缓冲区,完成了第三次复制,这次有点不一样的是数据不是在内核缓冲区了,会复制到 socket buffer 中。
-write 系统调用返回,又切换回了用户态,然后数据由 DMA 拷贝到协议引擎。
-
-
-如此就能看出其实默认的读写操作代价是非常大的,而在 rocketmq 等高性能中间件中都有使用的零拷贝技术,其中 rocketmq 使用的是 mmap
-mmap mmap基于 OS 的 mmap 的内存映射技术,通过MMU 映射文件,将文件直接映射到用户态的内存地址,使得对文件的操作不再是 write/read,而转化为直接对内存地址的操作,使随机读写文件和读写内存相似的速度。
-
-mmap 把文件映射到用户空间里的虚拟内存,省去了从内核缓冲区复制到用户空间的过程,文件中的位置在虚拟内存中有了对应的地址,可以像操作内存一样操作这个文件,这样的文件读写文件方式少了数据从内核缓存到用户空间的拷贝,效率很高。
-
-tmp_buf = mmap ( file, len) ;
-write ( socket, tmp_buf, len) ;
-
-
-
-第一步:mmap系统调用使得文件内容被DMA引擎复制到内核缓冲区。然后该缓冲区与用户进程共享,在内核和用户内存空间之间不进行任何拷贝。
-第二步:写系统调用使得内核将数据从原来的内核缓冲区复制到与套接字相关的内核缓冲区。
-第三步:第三次拷贝发生在DMA引擎将数据从内核套接字缓冲区传递给协议引擎时。
-通过使用mmap而不是read,我们将内核需要拷贝的数据量减少了一半。当大量的数据被传输时,这将有很好的效果。然而,这种改进并不是没有代价的;在使用mmap+write方法时,有一些隐藏的陷阱。例如当你对一个文件进行内存映射,然后在另一个进程截断同一文件时调用写。你的写系统调用将被总线错误信号SIGBUS打断,因为你执行了一个错误的内存访问。该信号的默认行为是杀死进程并dumpcore–这对网络服务器来说不是最理想的操作。
-有两种方法可以解决这个问题。
-第一种方法是为SIGBUS信号安装一个信号处理程序,然后在处理程序中简单地调用返回。通过这样做,写系统调用会返回它在被打断之前所写的字节数,并将errno设置为成功。让我指出,这将是一个糟糕的解决方案,一个治标不治本的解决方案。因为SIGBUS预示着进程出了严重的问题,所以不鼓励使用这种解决方案。
-第二个解决方案涉及内核的文件租赁(在Windows中称为 “机会锁”)。这是解决这个问题的正确方法。通过在文件描述符上使用租赁,你与内核在一个特定的文件上达成租约。然后你可以向内核请求一个读/写租约。当另一个进程试图截断你正在传输的文件时,内核会向你发送一个实时信号,即RT_SIGNAL_LEASE信号。它告诉你内核即将终止你对该文件的写或读租约。在你的程序访问一个无效的地址和被SIGBUS信号杀死之前,你的写调用会被打断了。写入调用的返回值是中断前写入的字节数,errno将被设置为成功。下面是一些示例代码,显示了如何从内核中获得租约。
-if ( fcntl ( fd, F_SETSIG, RT_SIGNAL_LEASE) == - 1 ) {
- perror ( "kernel lease set signal" ) ;
- return - 1 ;
-}
-
-if ( fcntl ( fd, F_SETLEASE, l_type) ) {
- perror ( "kernel lease set type" ) ;
- return - 1 ;
-}
-]]>
-
- MQ
- RocketMQ
- 消息队列
-
-
- MQ
- 消息队列
- RocketMQ
@@ -10738,105 +10751,185 @@ typedef struct dict {
- 给小电驴上牌
- /2022/03/20/%E7%BB%99%E5%B0%8F%E7%94%B5%E9%A9%B4%E4%B8%8A%E7%89%8C/
- 三八节活动的时候下决心买了个小电驴,主要是上下班路上现在通勤条件越来越恶劣了,之前都是觉得坐公交就行了,实际路程就比较短,但是现在或者说大概是年前那两个月差不多就开始了,基本是堵一路,个人感觉是天目山路那边在修地铁,而且蚂蚁的几个空间都在那,上班的时间点都差不多,前一个修地铁感觉挺久了,机动车保有量也越来越多,总体是古墩路就越来越堵,还有个原因就是早上上班的点共享单车都被骑走了,有时候整整走一路都没一辆,有时候孤零零地有一辆基本都是破的;走路其实也是一种选择,但是因为要赶着上班,走得太慢就要很久,可能要 45 分钟这样,走得比较快就一身汗挺难受的。所以考虑自行车和电动车,这里还有一点就是不管是乘公交还是骑共享单车,其实都要从楼下走出去蛮远,公司回来也是,也就是这种通勤方式在准备阶段就花了比较多时间,比如总的从下班到到家的时间是半小时,可能在骑共享单车和公交车上的时间都不到十分钟,就比较难受。觉得这种比例太浪费时间,如果能有这种比较点对点的方式,估计能省时省力不少,前面说的骑共享单车的方式其实在之前是比较可行的,但是后来越来越少车,基本都是每周的前几天,周一到周三都是没有车,走路到公司再冷的天都是走出一身的汗,下雨天就更难受,本来下雨天应该是优先选择坐公交,但是一般下雨天堵车会更严重,而且车子到我上车的那个站,下雨天就挤得不行,总体说下来感觉事情都不打,但是几年下来,还是会挺不爽的。
-电驴看的比较草率,主要是考虑续航,然后锂电池外加 48v 和 24AH,这样一般来讲还是价格比较高的,只是原来没预料到这个限速,以为现在的车子都比较快,但是现在的新国标车子都是 25km/h 的限速,然后 15km/h 都是会要提醒,虽然说有一些特殊的解除限速的方法,但是解了也就 35km/h ,差距不是特别大,而且现在的车子都是比较小,也不太能载东西,特别是上下班路程也不远的情况下,其实不是那么需要速度,就像我朋友说的,可能骑车的时间还不如等红绿灯多,所以就还好,也不打算解除限速,只是品牌上也仔细看,后来选了绿源,目前大部分还是雅迪,爱玛,台羚,绿源,小牛等,路上看的话还是雅迪比较多,不过价格也比较贵一点,还有就是小牛了,是比较新兴的品牌,手机 App 什么的做得比较好,而且也比较贵,最后以相对比较便宜的价格买了个锂电 48V24AH 的小车子,后来发现还是有点不方便的点就是没有比较大的筐,也不好装,这样就是下雨天雨衣什么的比较不方便放。
-聊回来主题上牌这个事情,这个事情也是颇费心力,提车的时候店里的让我跟他早上一起去,但是因为不确定时间,也比较远就没跟着去,因为我是线上买的,线下自提,线下的店可能没啥利润可以拿,就不肯帮忙代上牌,朋友说在线下店里买是可以代上的,自己上牌过程也比较曲折,一开始是头盔没到,然后是等开发票,主要的东西就是需要骑着车子去车管所,不能只自己去,然后需要预约,附近比较近的都是提前一周就预约完了号了,要提前在支付宝上进行预约,比较空的就是店里推荐的景区大队,但是随之而来就是比较蛋疼的,这个景区大队太远了,看下骑车距离有十几公里,所以就有点拖延症,但是总归要上的,不然一直不能开是白买了,上牌的材料主要是车辆合格证,发票,然后车子上的浙品码,在车架上和电池上,然后车架号什么的都要跟合格证上完全对应,整体车子要跟合格证上一毛一样,如果有额外的反光镜,后面副座都需要拆掉,脚踏板要装上,到了那其实还比较顺利,就是十几公里外加那天比较冷,吹得头疼。
+ 聊一下 SpringBoot 中使用的 cglib 作为动态代理中的一个注意点
+ /2021/09/19/%E8%81%8A%E4%B8%80%E4%B8%8B-SpringBoot-%E4%B8%AD%E4%BD%BF%E7%94%A8%E7%9A%84-cglib-%E4%BD%9C%E4%B8%BA%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E4%B8%AD%E7%9A%84%E4%B8%80%E4%B8%AA%E6%B3%A8%E6%84%8F%E7%82%B9/
+ 这个话题是由一次组内同学分享引出来的,首先在 springboot 2.x 开始默认使用了 cglib 作为 aop 的实现,这里也稍微讲一下,在一个 1.x 的老项目里,可以看到AopAutoConfiguration 是这样的
+@Configuration
+@ConditionalOnClass ( { EnableAspectJAutoProxy . class , Aspect . class , Advice . class } )
+@ConditionalOnProperty ( prefix = "spring.aop" , name = "auto" , havingValue = "true" , matchIfMissing = true )
+public class AopAutoConfiguration {
+
+ @Configuration
+ @EnableAspectJAutoProxy ( proxyTargetClass = false )
+ @ConditionalOnProperty ( prefix = "spring.aop" , name = "proxy-target-class" , havingValue = "false" , matchIfMissing = true )
+ public static class JdkDynamicAutoProxyConfiguration {
+ }
+
+ @Configuration
+ @EnableAspectJAutoProxy ( proxyTargetClass = true )
+ @ConditionalOnProperty ( prefix = "spring.aop" , name = "proxy-target-class" , havingValue = "true" , matchIfMissing = false )
+ public static class CglibAutoProxyConfiguration {
+ }
+
+}
+
+而在 2.x 中变成了这样
+@Configuration ( proxyBeanMethods = false )
+@ConditionalOnProperty ( prefix = "spring.aop" , name = "auto" , havingValue = "true" , matchIfMissing = true )
+public class AopAutoConfiguration {
+
+ @Configuration ( proxyBeanMethods = false )
+ @ConditionalOnClass ( Advice . class )
+ static class AspectJAutoProxyingConfiguration {
+
+ @Configuration ( proxyBeanMethods = false )
+ @EnableAspectJAutoProxy ( proxyTargetClass = false )
+ @ConditionalOnProperty ( prefix = "spring.aop" , name = "proxy-target-class" , havingValue = "false" )
+ static class JdkDynamicAutoProxyConfiguration {
+
+ }
+
+ @Configuration ( proxyBeanMethods = false )
+ @EnableAspectJAutoProxy ( proxyTargetClass = true )
+ @ConditionalOnProperty ( prefix = "spring.aop" , name = "proxy-target-class" , havingValue = "true" ,
+ matchIfMissing = true )
+ static class CglibAutoProxyConfiguration {
+
+ }
+
+ }
+
+为何会加载 AopAutoConfiguration 在前面的文章聊聊 SpringBoot 自动装配 里已经介绍过,有兴趣的可以看下,可以发现 springboot 在 2.x 版本开始使用 cglib 作为默认的动态代理实现。
+然后就是出现的问题了,代码是这样的,一个简单的基于 springboot 的带有数据库的插入,对插入代码加了事务注解,
+@Mapper
+public interface StudentMapper {
+
+ @Insert ( "insert into student(name, age)" + "values ('nick', '18')" )
+ public Long insert ( ) ;
+}
+
+@Component
+public class StudentManager {
+
+ @Resource
+ private StudentMapper studentMapper;
+
+ public Long createStudent ( ) {
+ return studentMapper. insert ( ) ;
+ }
+}
+
+@Component
+public class StudentServiceImpl implements StudentService {
+
+ @Resource
+ private StudentManager studentManager;
+
+
+ @Resource
+ private StudentServiceImpl studentService;
+
+ @Override
+ @Transactional
+ public Long createStudent ( ) {
+ Long id = studentManager. createStudent ( ) ;
+ Long id2 = studentService. createStudent2 ( ) ;
+ return 1L ;
+ }
+
+ @Transactional
+ private Long createStudent2 ( ) {
+
+ return studentManager. createStudent ( ) ;
+ }
+}
+
+第一个公有方法 createStudent 首先调用了 manager 层的创建方法,然后再通过引入的 studentService 调用了createStudent2,我们先跑一下看看会出现啥情况,果不其然报错了,正是这个报错让我纠结了很久
+
+报了个空指针,而且是在 createStudent2 已经被调用到了,在它的内部,报的 studentManager 是 null,首先 cglib 作为动态代理它是通过继承的方式来实现的,相当于是会在调用目标对象的代理方法时调用 cglib 生成的子类,具体的代理切面逻辑在子类实现,然后在调用目标对象的目标方法,但是继承的方式对于 final 和私有方法其实是没法进行代理的,因为没法继承,所以我最开始的想法是应该通过 studentService 调用 createStudent2 的时候就报错了,也就是不会进入这个方法内部,后面才发现犯了个特别二的错误,继承的方式去调用父类的私有方法,对于 Java 来说是可以调用到的,父类的私有方法并不由子类的InstanceKlass维护,只能通过子类的InstanceKlass找到Java类对应的_super,这样间接地访问。也就是说子类其实是可以访问的,那为啥访问了会报空指针呢,这里报的是studentManager 是空的,可以往依赖注入方面去想,如果忽略依赖注入,我这个studentManager 的确是 null,那是不是就没有被依赖注入呢,但是为啥前面那个可以呢
+这个问题着实查了很久,不废话来看代码
+@Override
+ protected Object invokeJoinpoint ( ) throws Throwable {
+ if ( this . methodProxy != null ) {
+
+ return this . methodProxy. invoke ( this . target, this . arguments) ;
+ }
+ else {
+ return super . invokeJoinpoint ( ) ;
+ }
+ }
+
+
+
+这个是org.springframework.aop.framework.CglibAopProxy.CglibMethodInvocation的代码,其实它在这里不是直接调用 super 也就是父类的方法,而是通过 methodProxy 调用 target 目标对象的方法,也就是原始的 studentService bean 的方法,这样子 spring 管理的已经做好依赖注入的 bean 就能正常起作用,否则就会出现上面的问题,因为 cglib 其实是通过继承来实现,通过将调用转移到子类上加入代理逻辑,我们在简单使用的时候会直接 invokeSuper() 调用父类的方法,但是在这里 spring 的场景里需要去支持 spring 的功能逻辑,所以上面的问题就可以开始来解释了,因为 createStudent 是公共方法,cglib 可以对其进行继承代理,但是在执行逻辑的时候其实是通过调用目标对象,也就是 spring 管理的被代理的目标对象的 bean 调用的 createStudent,而对于下面的 createStudent2 方法因为是私有方法,不会走代理逻辑,也就不会有调用回目标对象的逻辑,只是通过继承关系,在子类中没有这个方法,所以会通过子类的InstanceKlass找到这个类对应的_super,然后调用父类的这个私有方法,这里要搞清楚一个点,从这个代理类直接找到其父类然后调用这个私有方法,这个类是由 cglib 生成的,不是被 spring 管理起来经过依赖注入的 bean,所以是没有 studentManager 这个依赖的,也就出现了前面的问题
+而在前面提到的cglib通过methodProxy调用到目标对象,目标对象是在什么时候设置的呢,其实是在bean的生命周期中,org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization这个接口的在bean的初始化过程中,会调用实现了这个接口的方法,
+@Override
+public Object postProcessAfterInitialization ( @Nullable Object bean, String beanName) {
+ if ( bean != null ) {
+ Object cacheKey = getCacheKey ( bean. getClass ( ) , beanName) ;
+ if ( this . earlyProxyReferences. remove ( cacheKey) != bean) {
+ return wrapIfNecessary ( bean, beanName, cacheKey) ;
+ }
+ }
+ return bean;
+}
+
+具体的逻辑在 org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary这个方法里
+protected Object getCacheKey ( Class < ? > beanClass, @Nullable String beanName) {
+ if ( StringUtils . hasLength ( beanName) ) {
+ return ( FactoryBean . class . isAssignableFrom ( beanClass) ?
+ BeanFactory . FACTORY_BEAN_PREFIX + beanName : beanName) ;
+ }
+ else {
+ return beanClass;
+ }
+ }
+
+
+ protected Object wrapIfNecessary ( Object bean, String beanName, Object cacheKey) {
+ if ( StringUtils . hasLength ( beanName) && this . targetSourcedBeans. contains ( beanName) ) {
+ return bean;
+ }
+ if ( Boolean . FALSE. equals ( this . advisedBeans. get ( cacheKey) ) ) {
+ return bean;
+ }
+ if ( isInfrastructureClass ( bean. getClass ( ) ) || shouldSkip ( bean. getClass ( ) , beanName) ) {
+ this . advisedBeans. put ( cacheKey, Boolean . FALSE) ;
+ return bean;
+ }
+
+
+ Object [ ] specificInterceptors = getAdvicesAndAdvisorsForBean ( bean. getClass ( ) , beanName, null ) ;
+ if ( specificInterceptors != DO_NOT_PROXY) {
+ this . advisedBeans. put ( cacheKey, Boolean . TRUE) ;
+ Object proxy = createProxy (
+ bean. getClass ( ) , beanName, specificInterceptors, new SingletonTargetSource ( bean) ) ;
+ this . proxyTypes. put ( cacheKey, proxy. getClass ( ) ) ;
+ return proxy;
+ }
+
+ this . advisedBeans. put ( cacheKey, Boolean . FALSE) ;
+ return bean;
+ }
+
+然后在 org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy 中创建了代理类
]]>
- 生活
+ Java
+ SpringBoot
- 生活
-
-
-
- 搬运两个 StackOverflow 上的 Mysql 编码相关的问题解答
- /2022/01/16/%E6%90%AC%E8%BF%90%E4%B8%A4%E4%B8%AA-StackOverflow-%E4%B8%8A%E7%9A%84-Mysql-%E7%BC%96%E7%A0%81%E7%9B%B8%E5%85%B3%E7%9A%84%E9%97%AE%E9%A2%98%E8%A7%A3%E7%AD%94/
- Mysql 字符编码和排序规则这个一直是属于一知半解的状态,知道 utf8 跟 utf8mb4 的区别主要是能不能支持 emoji,但是具体后面配置的排序规则是用来干嘛,或者有什么区别,应该使用哪个,所以在 stackoverflow 上找了下,有两个比较不错的解答,就搬过来并且配合机翻做了点修改
-For those people still arriving at this question in 2020 or later, there are newer options that may be better than both of these. For example, utf8mb4_0900_ai_ci.
-All these collations are for the UTF-8 character encoding. The differences are in how text is sorted and compared.
-_unicode_ci and _general_ci are two different sets of rules for sorting and comparing text according to the way we expect. Newer versions of MySQL introduce new sets of rules, too, such as _0900_ai_ci for equivalent rules based on Unicode 9.0 - and with no equivalent _general_ci variant. People reading this now should probably use one of these newer collations instead of either _unicode_ci or _general_ci. The description of those older collations below is provided for interest only.
-MySQL is currently transitioning away from an older, flawed UTF-8 implementation. For now, you need to use utf8mb4 instead of utf8 for the character encoding part, to ensure you are getting the fixed version. The flawed version remains for backward compatibility, though it is being deprecated.
-Key differences
-utf8mb4_unicode_ci is based on the official Unicode rules for universal sorting and comparison, which sorts accurately in a wide range of languages.
-utf8mb4_general_ci is a simplified set of sorting rules which aims to do as well as it can while taking many short-cuts designed to improve speed. It does not follow the Unicode rules and will result in undesirable sorting or comparison in some situations, such as when using particular languages or characters.
-On modern servers, this performance boost will be all but negligible. It was devised in a time when servers had a tiny fraction of the CPU performance of today’s computers.
-Benefits of utf8mb4_unicode_ci over utf8mb4_general_ci
-utf8mb4_unicode_ci, which uses the Unicode rules for sorting and comparison, employs a fairly complex algorithm for correct sorting in a wide range of languages and when using a wide range of special characters. These rules need to take into account language-specific conventions; not everybody sorts their characters in what we would call ‘alphabetical order’.
-As far as Latin (ie “European”) languages go, there is not much difference between the Unicode sorting and the simplified utf8mb4_general_cisorting in MySQL, but there are still a few differences:
-For examples, the Unicode collation sorts “ß” like “ss”, and “Œ” like “OE” as people using those characters would normally want, whereas utf8mb4_general_cisorts them as single characters (presumably like “s” and “e” respectively).
-Some Unicode characters are defined as ignorable, which means they shouldn’t count toward the sort order and the comparison should move on to the next character instead. utf8mb4_unicode_cihandles these properly.
-In non-latin languages, such as Asian languages or languages with different alphabets, there may be a lot more differences between Unicode sorting and the simplified utf8mb4_general_cisorting. The suitability of utf8mb4_general_ciwill depend heavily on the language used. For some languages, it’ll be quite inadequate.
-What should you use?
-There is almost certainly no reason to use utf8mb4_general_cianymore, as we have left behind the point where CPU speed is low enough that the performance difference would be important. Your database will almost certainly be limited by other bottlenecks than this.
-In the past, some people recommended to use utf8mb4_general_ciexcept when accurate sorting was going to be important enough to justify the performance cost. Today, that performance cost has all but disappeared, and developers are treating internationalization more seriously.
-There’s an argument to be made that if speed is more important to you than accuracy, you may as well not do any sorting at all. It’s trivial to make an algorithm faster if you do not need it to be accurate. So, utf8mb4_general_ciis a compromise that’s probably not needed for speed reasons and probably also not suitable for accuracy reasons.
-One other thing I’ll add is that even if you know your application only supports the English language, it may still need to deal with people’s names, which can often contain characters used in other languages in which it is just as important to sort correctly. Using the Unicode rules for everything helps add peace of mind that the very smart Unicode people have worked very hard to make sorting work properly.
-What the parts mean
-Firstly, ci is for case-insensitive sorting and comparison. This means it’s suitable for textual data, and case is not important. The other types of collation are cs (case-sensitive) for textual data where case is important, and bin, for where the encoding needs to match, bit for bit, which is suitable for fields which are really encoded binary data (including, for example, Base64). Case-sensitive sorting leads to some weird results and case-sensitive comparison can result in duplicate values differing only in letter case, so case-sensitive collations are falling out of favor for textual data - if case is significant to you, then otherwise ignorable punctuation and so on is probably also significant, and a binary collation might be more appropriate.
-Next, unicode or general refers to the specific sorting and comparison rules - in particular, the way text is normalized or compared. There are many different sets of rules for the utf8mb4 character encoding, with unicode and general being two that attempt to work well in all possible languages rather than one specific one. The differences between these two sets of rules are the subject of this answer. Note that unicode uses rules from Unicode 4.0. Recent versions of MySQL add the rulesets unicode_520 using rules from Unicode 5.2, and 0900 (dropping the “unicode_” part) using rules from Unicode 9.0.
-And lastly, utf8mb4 is of course the character encoding used internally. In this answer I’m talking only about Unicode based encodings.
-翻译 对于那些在 2020 年或之后仍会遇到这个问题的人,有可能比这两个更好的新选项。例如,utf8mb4_0900_ai_ci。
-所有这些排序规则都用于 UTF-8 字符编码。不同之处在于文本的排序和比较方式。
-_unicode_ci和 _general_ci是两组不同的规则,用于按照我们期望的方式对文本进行排序和比较。较新版本的 MySQL 也引入了新的规则集,例如 _0900_ai_ci用于基于 Unicode 9.0 的等效规则 - 并且没有等效的 _general_ci变体。现在阅读本文的人可能应该使用这些较新的排序规则之一,而不是 _unicode_ci或 _general_ci。下面对那些较旧的排序规则的描述仅供参考。
-MySQL 目前正在从旧的、有缺陷的 UTF-8 实现过渡。现在,您需要使用 utf8mb4 而不是 utf8作为字符编码部分,以确保您获得的是固定版本。有缺陷的版本仍然是为了向后兼容,尽管它已被弃用。
-主要区别
-utf8mb4_unicode_ci基于官方 Unicode 规则进行通用排序和比较,可在多种语言中准确排序。
-utf8mb4_general_ci是一组简化的排序规则,旨在尽其所能,同时采用许多旨在提高速度的捷径。它不遵循 Unicode 规则,并且在某些情况下会导致不希望的排序或比较,例如在使用特定语言或字符时。
-在现代服务器上,这种性能提升几乎可以忽略不计。它是在服务器的 CPU 性能只有当今计算机的一小部分时设计的。
-utf8mb4_unicode_ci 相对于 utf8mb4_general_ci的优势
-utf8mb4_unicode_ci使用 Unicode 规则进行排序和比较,采用相当复杂的算法在多种语言中以及在使用多种特殊字符时进行正确排序。这些规则需要考虑特定语言的约定;不是每个人都按照我们所说的“字母顺序”对他们的字符进行排序。
-就拉丁语(即“欧洲”)语言而言,Unicode 排序和 MySQL 中简化的 utf8mb4_general_ci排序没有太大区别,但仍有一些区别:
-例如,Unicode 排序规则将“ß”排序为“ss”,将“Œ”排序为“OE”,因为使用这些字符的人通常需要这些字符,而 utf8mb4_general_ci将它们排序为单个字符(大概分别像“s”和“e” )。
-一些 Unicode 字符被定义为可忽略,这意味着它们不应该计入排序顺序,并且比较应该转到下一个字符。 utf8mb4_unicode_ci正确处理这些。
-在非拉丁语言中,例如亚洲语言或具有不同字母的语言,Unicode 排序和简化的 utf8mb4_general_ci排序之间可能存在更多差异。 utf8mb4_general_ci的适用性在很大程度上取决于所使用的语言。对于某些语言,这将是非常不充分的。
-你应该用什么?
-几乎可以肯定没有理由再使用 utf8mb4_general_ci,因为我们已经将 CPU 速度低到会严重影响性能表现的时代远抛在脑后了。您的数据库几乎肯定会受到除此之外的其他瓶颈的限制。
-过去,有些人建议使用 utf8mb4_general_ci,除非准确排序足够重要以证明性能成本是合理的。如今,这种性能成本几乎消失了,开发人员正在更加认真地对待国际化。
-有一个论点是,如果速度对您来说比准确性更重要,那么您可能根本不进行任何排序。如果您不需要准确的算法,那么使算法更快是微不足道的。因此,utf8mb4_general_ci是一种折衷方案,出于速度原因可能不需要,也可能出于准确性原因也不适合。
-我要补充的另一件事是,即使您知道您的应用程序仅支持英语,它可能仍需要处理人名,这些人名通常包含其他语言中使用的字符,在这些语言中正确排序同样重要.对所有事情都使用 Unicode 规则有助于让您更加安心,因为非常聪明的 Unicode 人员已经非常努力地工作以使排序正常工作。
-其余各个部分是什么意思
-首先, ci 用于不区分大小写的排序和比较。这意味着它适用于文本数据,大小写并不重要。其他类型的排序规则是 cs(区分大小写),用于区分大小写的文本数据,以及 bin,用于编码需要匹配的地方,逐位匹配,适用于真正编码二进制数据的字段(包括,用于例如,Base64)。区分大小写的排序会导致一些奇怪的结果,区分大小写的比较可能会导致重复值仅在字母大小写上有所不同,因此区分大小写的排序规则对文本数据不受欢迎 - 如果大小写对您很重要,那么标点符号就可以忽略等等可能也很重要,二进制排序规则可能更合适。
-接下来,unicode 或general 指的是具体的排序和比较规则——特别是文本被规范化或比较的方式。 utf8mb4 字符编码有许多不同的规则集,其中 unicode 和 general 是两种,它们试图在所有可能的语言中都很好地工作,而不是在一种特定的语言中。这两组规则之间的差异是此答案的主题。请注意,unicode 使用 Unicode 4.0 中的规则。 MySQL 的最新版本使用 Unicode 5.2 的规则添加规则集 unicode_520,使用 Unicode 9.0 的规则添加 0900(删除“unicode_”部分)。
-最后,utf8mb4 当然是内部使用的字符编码。在这个答案中,我只谈论基于 Unicode 的编码。
-utf8 和 utf8mb4 编码有什么区别 UTF-8 is a variable-length encoding. In the case of UTF-8, this means that storing one code point requires one to four bytes. However, MySQL’s encoding called “utf8” (alias of “utf8mb3”) only stores a maximum of three bytes per code point.
-So the character set “utf8”/“utf8mb3” cannot store all Unicode code points: it only supports the range 0x000 to 0xFFFF, which is called the “Basic Multilingual Plane “. See also Comparison of Unicode encodings .
-This is what (a previous version of the same page at)the MySQL documentation has to say about it:
-
-The character set named utf8[/utf8mb3] uses a maximum of three bytes per character and contains only BMP characters. As of MySQL 5.5.3, the utf8mb4 character set uses a maximum of four bytes per character supports supplemental characters:
-
-For a BMP character, utf8[/utf8mb3] and utf8mb4 have identical storage characteristics: same code values, same encoding, same length.
-For a supplementary character, utf8[/utf8mb3] cannot store the character at all , while utf8mb4 requires four bytes to store it. Since utf8[/utf8mb3] cannot store the character at all, you do not have any supplementary characters in utf8[/utf8mb3] columns and you need not worry about converting characters or losing data when upgrading utf8[/utf8mb3] data from older versions of MySQL.
-
-
-So if you want your column to support storing characters lying outside the BMP (and you usually want to), such as emoji , use “utf8mb4”. See also What are the most common non-BMP Unicode characters in actual use? .
-译文 UTF-8 是一种可变长度编码。对于 UTF-8,这意味着存储一个代码点需要一到四个字节。但是,MySQL 的编码称为“utf8”(“utf8mb3”的别名)每个代码点最多只能存储三个字节。
-所以字符集“utf8”/“utf8mb3”不能存储所有的Unicode码位:它只支持0x000到0xFFFF的范围,被称为“基本多语言平面 ”。另请参阅 Unicode 编码比较 。
-这就是(同一页面的先前版本)MySQL 文档 不得不说的:
-
-名为 utf8[/utf8mb3] 的字符集每个字符最多使用三个字节,并且仅包含 BMP 字符。从 MySQL 5.5.3 开始,utf8mb4 字符集每个字符最多使用四个字节,支持补充字符:
-
-对于 BMP 字符,utf8[/utf8mb3] 和 utf8mb4 具有相同的存储特性:相同的代码值、相同的编码、相同的长度。
-对于补充字符,utf8[/utf8mb3] 根本无法存储该字符 ,而 utf8mb4 需要四个字节来存储它。由于 utf8[/utf8mb3] 根本无法存储字符,因此您在 utf8[/utf8mb3] 列中没有任何补充字符,您不必担心从旧版本升级 utf8[/utf8mb3] 数据时转换字符或丢失数据mysql。
-
-
-因此,如果您希望您的列支持存储位于 BMP 之外的字符(并且您通常希望这样做),例如 emoji ,请使用“utf8mb4”。另请参阅
-实际使用中最常见的非 BMP Unicode 字符是什么? 。
-]]>
-
- Mysql
-
-
- mysql
- 字符集
- 编码
- utf8
- utf8mb4
- utf8mb4_0900_ai_ci
- utf8mb4_unicode_ci
- utf8mb4_general_ci
+ Java
+ Spring
+ SpringBoot
+ cglib
+ 事务
@@ -11047,6 +11140,31 @@ typedef struct dict {
数据源动态切换
+
+ 介绍下最近比较实用的端口转发
+ /2021/11/14/%E4%BB%8B%E7%BB%8D%E4%B8%8B%E6%9C%80%E8%BF%91%E6%AF%94%E8%BE%83%E5%AE%9E%E7%94%A8%E7%9A%84%E7%AB%AF%E5%8F%A3%E8%BD%AC%E5%8F%91/
+ vscode 扩展转发在日常使用云服务器的时候,如果要访问上面自建的 mysql,一般要不直接开对应的端口,然后需要对本地 ip 进行授权,但是这个方案会有比较多的限制,比如本地 ip 变了,比如是非固定出口 ip 的家用宽带,或者要在家里跟公司都要访问,如果对所有 ip 都授权的话会不安全,这个时候其实是用 ssh 端口转发是个比较安全方便的方式。 原来在这个之前其实对这块内容不太了解,后面是听朋友说的,vscode 的 Remote - SSH 扩展可以很方便的使用端口转发,在使用该扩展的时候,会在控制台位置里都出现一个”端口” tab 如图中所示,我就是将一个服务器上的 mysql 的 3306 端口转发到本地的 3307 端口,至于为什么不用 3306 是因为本地我也有个 mysql 已经使用了 3306 端口,这个方法是使用的 vscode 的这个扩展,
+ssh 命令转发 还有个方式是直接使用 ssh 命令 命令可以如此
+ssh -CfNg -L 3307 :127.0.0.1:3306 user1@199.199.199.199
+简单介绍下这个命令-C 表示的是压缩数据包-f 表示后台执行命令-N 是表示不执行具体命令只用于端口转发-g 表示允许远程主机连接本地转发端口-L 则是具体端口转发的映射配置 上面的命令就是将远程主机的 127.0.0.1:3306 对应转发到本地 3307 而后面的用户则就是登录主机的用户名user1和ip地址199.199.199.199,当然这个配置也不是唯一的
+ssh config 配置转发 还可以在ssh 的 config 配置中加对应的配置
+Host host1
+ HostName 199.199 .199.199
+ User user1
+ IdentityFile /Users/user1/.ssh/id_rsa
+ ServerAliveInterval 60
+ LocalForward 3310 127.0 .0.1:3306
+然后通过 ssh host1 连接服务器的时候就能顺带做端口转发
+]]>
+
+ ssh
+ 技巧
+
+
+ ssh
+ 端口转发
+
+
聊在东京奥运会闭幕式这天-二
/2021/08/19/%E8%81%8A%E5%9C%A8%E4%B8%9C%E4%BA%AC%E5%A5%A5%E8%BF%90%E4%BC%9A%E9%97%AD%E5%B9%95%E5%BC%8F%E8%BF%99%E5%A4%A9-%E4%BA%8C/
@@ -11236,282 +11354,235 @@ typedef struct dict {
- 聊一下 SpringBoot 中使用的 cglib 作为动态代理中的一个注意点
- /2021/09/19/%E8%81%8A%E4%B8%80%E4%B8%8B-SpringBoot-%E4%B8%AD%E4%BD%BF%E7%94%A8%E7%9A%84-cglib-%E4%BD%9C%E4%B8%BA%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E4%B8%AD%E7%9A%84%E4%B8%80%E4%B8%AA%E6%B3%A8%E6%84%8F%E7%82%B9/
- 这个话题是由一次组内同学分享引出来的,首先在 springboot 2.x 开始默认使用了 cglib 作为 aop 的实现,这里也稍微讲一下,在一个 1.x 的老项目里,可以看到AopAutoConfiguration 是这样的
-@Configuration
-@ConditionalOnClass ( { EnableAspectJAutoProxy . class , Aspect . class , Advice . class } )
-@ConditionalOnProperty ( prefix = "spring.aop" , name = "auto" , havingValue = "true" , matchIfMissing = true )
-public class AopAutoConfiguration {
-
- @Configuration
- @EnableAspectJAutoProxy ( proxyTargetClass = false )
- @ConditionalOnProperty ( prefix = "spring.aop" , name = "proxy-target-class" , havingValue = "false" , matchIfMissing = true )
- public static class JdkDynamicAutoProxyConfiguration {
- }
-
- @Configuration
- @EnableAspectJAutoProxy ( proxyTargetClass = true )
- @ConditionalOnProperty ( prefix = "spring.aop" , name = "proxy-target-class" , havingValue = "true" , matchIfMissing = false )
- public static class CglibAutoProxyConfiguration {
- }
-
-}
-
-而在 2.x 中变成了这样
-@Configuration ( proxyBeanMethods = false )
-@ConditionalOnProperty ( prefix = "spring.aop" , name = "auto" , havingValue = "true" , matchIfMissing = true )
-public class AopAutoConfiguration {
-
- @Configuration ( proxyBeanMethods = false )
- @ConditionalOnClass ( Advice . class )
- static class AspectJAutoProxyingConfiguration {
-
- @Configuration ( proxyBeanMethods = false )
- @EnableAspectJAutoProxy ( proxyTargetClass = false )
- @ConditionalOnProperty ( prefix = "spring.aop" , name = "proxy-target-class" , havingValue = "false" )
- static class JdkDynamicAutoProxyConfiguration {
-
+ 聊聊 Dubbo 的 SPI
+ /2020/05/31/%E8%81%8A%E8%81%8A-Dubbo-%E7%9A%84-SPI/
+ SPI全称是Service Provider Interface,咋眼看跟api是不是有点相似,api是application interface,这两个其实在某些方面有类似的地方,也有蛮大的区别,比如我们基于 dubbo 的微服务,一般我们可以提供服务,然后非泛化调用的话,我们可以把 api 包提供给应用调用方,他们根据接口签名传对应参数并配置好对应的服务发现如 zk 等就可以调用我们的服务了,然后 spi 会有点类似但是是反过来的关系,相当于是一种规范,比如我约定完成这个功能需要两个有两个接口,一个是连接的,一个是断开的,其实就可以用 jdbc 的驱动举例,比较老套了,然后各个厂家去做具体的实现吧,到时候根据我接口的全限定名的文件来加载实际的实现类,然后运行的时候调用对应实现类的方法就完了
+
+看上面的图,java.sql.Driver就是 spi,对应在classpath 的 META-INF/services 目录下的这个文件,里边的内容就是具体的实现类
+
+简单介绍了 Java的 SPI,再来说说 dubbo 的,dubbo 中为啥要用 SPI 呢,主要是为了框架的可扩展性和性能方面的考虑,比如协议层 dubbo 默认使用 dubbo 协议,同时也支持很多其他协议,也支持用户自己实现协议,那么跟 Java 的 SPI 会有什么区别呢,我们也来看个文件
+
+是不是看着很想,又有点不一样,在 Java 的 SPI 配置文件里每一行只有一个实现类的全限定名,在 Dubbo的 SPI配置文件中是 key=value 的形式,我们只需要对应的 key 就能加载对应的实现,
+
+ @SuppressWarnings ( "unchecked" )
+ public T getExtension ( String name) {
+ if ( name == null || name. length ( ) == 0 )
+ throw new IllegalArgumentException ( "Extension name == null" ) ;
+ if ( "true" . equals ( name) ) {
+ return getDefaultExtension ( ) ;
}
-
- @Configuration ( proxyBeanMethods = false )
- @EnableAspectJAutoProxy ( proxyTargetClass = true )
- @ConditionalOnProperty ( prefix = "spring.aop" , name = "proxy-target-class" , havingValue = "true" ,
- matchIfMissing = true )
- static class CglibAutoProxyConfiguration {
-
+ Holder < Object > holder = cachedInstances. get ( name) ;
+ if ( holder == null ) {
+ cachedInstances. putIfAbsent ( name, new Holder < Object > ( ) ) ;
+ holder = cachedInstances. get ( name) ;
+ }
+ Object instance = holder. get ( ) ;
+ if ( instance == null ) {
+ synchronized ( holder) {
+ instance = holder. get ( ) ;
+ if ( instance == null ) {
+ instance = createExtension ( name) ;
+ holder. set ( instance) ;
+ }
+ }
}
+ return ( T ) instance;
+ }
+这里其实就可以看出来第二个不同点了,就是这个cachedInstances,第一个是不用像 Java 原生的 SPI 那样去遍历加载对应的服务类,只需要通过 key 去寻找,并且寻找的时候会先从缓存的对象里去取,还有就是注意下这里的 DCL(double check lock)
+@SuppressWarnings ( "unchecked" )
+ private T createExtension ( String name) {
+ Class < ? > clazz = getExtensionClasses ( ) . get ( name) ;
+ if ( clazz == null ) {
+ throw findException ( name) ;
+ }
+ try {
+ T instance = ( T ) EXTENSION_INSTANCES. get ( clazz) ;
+ if ( instance == null ) {
+ EXTENSION_INSTANCES. putIfAbsent ( clazz, ( T ) clazz. newInstance ( ) ) ;
+ instance = ( T ) EXTENSION_INSTANCES. get ( clazz) ;
+ }
+ injectExtension ( instance) ;
+ Set < Class < ? > > wrapperClasses = cachedWrapperClasses;
+ if ( wrapperClasses != null && wrapperClasses. size ( ) > 0 ) {
+ for ( Class < ? > wrapperClass : wrapperClasses) {
+ instance = injectExtension ( ( T ) wrapperClass. getConstructor ( type) . newInstance ( instance) ) ;
+ }
+ }
+ return instance;
+ } catch ( Throwable t) {
+ throw new IllegalStateException ( "Extension instance(name: " + name + ", class: " +
+ type + ") could not be instantiated: " + t. getMessage ( ) , t) ;
+ }
+ }
+然后就是创建扩展了,这里如果 wrapperClasses 就会遍历生成wrapper实例,并做 setter 依赖注入,但是这里cachedWrapperClasses的来源还是有点搞不清楚,得再看下 com.alibaba.dubbo.common.extension.ExtensionLoader#loadFile的具体逻辑 又看了遍新的代码,这个函数被抽出来了
+
+ private boolean isWrapperClass ( Class < ? > clazz) {
+ try {
+ clazz. getConstructor ( type) ;
+ return true ;
+ } catch ( NoSuchMethodException e) {
+ return false ;
+ }
+ }
+是否是 wrapperClass 其实就看构造函数的。
+]]>
+
+ Java
+ Dubbo
+ RPC
+ SPI
+ Dubbo
+ SPI
+
+
+ Java
+ Dubbo
+ RPC
+ SPI
+
+
+
+ 聊聊 Dubbo 的容错机制
+ /2020/11/22/%E8%81%8A%E8%81%8A-Dubbo-%E7%9A%84%E5%AE%B9%E9%94%99%E6%9C%BA%E5%88%B6/
+ 之前看了 dubbo 的一些代码,在学习过程中,主要关注那些比较“高级”的内容,SPI,自适应扩展等,却忘了一些作为一个 rpc 框架最核心需要的部分,比如如何通信,序列化,网络,容错机制等等,因为其实这个最核心的就是远程调用,自适应扩展其实就是让代码可扩展性,可读性,更优雅等,写的搓一点其实也问题不大,但是一个合适的通信协议,序列化方法,如何容错等却是真正保证是一个 rpc 框架最重要的要素。 首先来看这张图 在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试。 各节点关系:
+
+这里的 Invoker 是 Provider 的一个可调用 Service 的抽象,Invoker 封装了 Provider 地址及 Service 接口信息
+Directory 代表多个 Invoker,可以把它看成 List<Invoker> ,但与 List 不同的是,它的值可能是动态变化的,比如注册中心推送变更
+Cluster 将 Directory 中的多个 Invoker 伪装成一个 Invoker,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个
+Router 负责从多个 Invoker 中按路由规则选出子集,比如读写分离,应用隔离等
+LoadBalance 负责从多个 Invoker 中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选
+
+集群容错模式 Failover Cluster 失败自动切换,当出现失败,重试其它服务器 1。通常用于读操作,但重试会带来更长延迟。可通过 retries=”2” 来设置重试次数(不含第一次)。
+重试次数配置如下:
+<dubbo:service retries=”2” /> 这里重点看下 Failover Cluster集群模式的实现
+public class FailoverCluster implements Cluster {
- }
+ public final static String NAME = "failover" ;
-为何会加载 AopAutoConfiguration 在前面的文章聊聊 SpringBoot 自动装配 里已经介绍过,有兴趣的可以看下,可以发现 springboot 在 2.x 版本开始使用 cglib 作为默认的动态代理实现。
-然后就是出现的问题了,代码是这样的,一个简单的基于 springboot 的带有数据库的插入,对插入代码加了事务注解,
-@Mapper
-public interface StudentMapper {
-
- @Insert ( "insert into student(name, age)" + "values ('nick', '18')" )
- public Long insert ( ) ;
-}
-
-@Component
-public class StudentManager {
-
- @Resource
- private StudentMapper studentMapper;
-
- public Long createStudent ( ) {
- return studentMapper. insert ( ) ;
- }
-}
-
-@Component
-public class StudentServiceImpl implements StudentService {
-
- @Resource
- private StudentManager studentManager;
-
-
- @Resource
- private StudentServiceImpl studentService;
-
- @Override
- @Transactional
- public Long createStudent ( ) {
- Long id = studentManager. createStudent ( ) ;
- Long id2 = studentService. createStudent2 ( ) ;
- return 1L ;
- }
-
- @Transactional
- private Long createStudent2 ( ) {
-
- return studentManager. createStudent ( ) ;
+ public < T > Invoker < T > join ( Directory < T > directory) throws RpcException {
+ return new FailoverClusterInvoker < T > ( directory) ;
}
-}
-
-第一个公有方法 createStudent 首先调用了 manager 层的创建方法,然后再通过引入的 studentService 调用了createStudent2,我们先跑一下看看会出现啥情况,果不其然报错了,正是这个报错让我纠结了很久
-
-报了个空指针,而且是在 createStudent2 已经被调用到了,在它的内部,报的 studentManager 是 null,首先 cglib 作为动态代理它是通过继承的方式来实现的,相当于是会在调用目标对象的代理方法时调用 cglib 生成的子类,具体的代理切面逻辑在子类实现,然后在调用目标对象的目标方法,但是继承的方式对于 final 和私有方法其实是没法进行代理的,因为没法继承,所以我最开始的想法是应该通过 studentService 调用 createStudent2 的时候就报错了,也就是不会进入这个方法内部,后面才发现犯了个特别二的错误,继承的方式去调用父类的私有方法,对于 Java 来说是可以调用到的,父类的私有方法并不由子类的InstanceKlass维护,只能通过子类的InstanceKlass找到Java类对应的_super,这样间接地访问。也就是说子类其实是可以访问的,那为啥访问了会报空指针呢,这里报的是studentManager 是空的,可以往依赖注入方面去想,如果忽略依赖注入,我这个studentManager 的确是 null,那是不是就没有被依赖注入呢,但是为啥前面那个可以呢
-这个问题着实查了很久,不废话来看代码
-@Override
- protected Object invokeJoinpoint ( ) throws Throwable {
- if ( this . methodProxy != null ) {
-
- return this . methodProxy. invoke ( this . target, this . arguments) ;
- }
- else {
- return super . invokeJoinpoint ( ) ;
- }
- }
-
-
-
-这个是org.springframework.aop.framework.CglibAopProxy.CglibMethodInvocation的代码,其实它在这里不是直接调用 super 也就是父类的方法,而是通过 methodProxy 调用 target 目标对象的方法,也就是原始的 studentService bean 的方法,这样子 spring 管理的已经做好依赖注入的 bean 就能正常起作用,否则就会出现上面的问题,因为 cglib 其实是通过继承来实现,通过将调用转移到子类上加入代理逻辑,我们在简单使用的时候会直接 invokeSuper() 调用父类的方法,但是在这里 spring 的场景里需要去支持 spring 的功能逻辑,所以上面的问题就可以开始来解释了,因为 createStudent 是公共方法,cglib 可以对其进行继承代理,但是在执行逻辑的时候其实是通过调用目标对象,也就是 spring 管理的被代理的目标对象的 bean 调用的 createStudent,而对于下面的 createStudent2 方法因为是私有方法,不会走代理逻辑,也就不会有调用回目标对象的逻辑,只是通过继承关系,在子类中没有这个方法,所以会通过子类的InstanceKlass找到这个类对应的_super,然后调用父类的这个私有方法,这里要搞清楚一个点,从这个代理类直接找到其父类然后调用这个私有方法,这个类是由 cglib 生成的,不是被 spring 管理起来经过依赖注入的 bean,所以是没有 studentManager 这个依赖的,也就出现了前面的问题
-而在前面提到的cglib通过methodProxy调用到目标对象,目标对象是在什么时候设置的呢,其实是在bean的生命周期中,org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization这个接口的在bean的初始化过程中,会调用实现了这个接口的方法,
-@Override
-public Object postProcessAfterInitialization ( @Nullable Object bean, String beanName) {
- if ( bean != null ) {
- Object cacheKey = getCacheKey ( bean. getClass ( ) , beanName) ;
- if ( this . earlyProxyReferences. remove ( cacheKey) != bean) {
- return wrapIfNecessary ( bean, beanName, cacheKey) ;
- }
- }
- return bean;
-}
-
-具体的逻辑在 org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary这个方法里
-protected Object getCacheKey ( Class < ? > beanClass, @Nullable String beanName) {
- if ( StringUtils . hasLength ( beanName) ) {
- return ( FactoryBean . class . isAssignableFrom ( beanClass) ?
- BeanFactory . FACTORY_BEAN_PREFIX + beanName : beanName) ;
- }
- else {
- return beanClass;
- }
- }
-
-
- protected Object wrapIfNecessary ( Object bean, String beanName, Object cacheKey) {
- if ( StringUtils . hasLength ( beanName) && this . targetSourcedBeans. contains ( beanName) ) {
- return bean;
- }
- if ( Boolean . FALSE. equals ( this . advisedBeans. get ( cacheKey) ) ) {
- return bean;
- }
- if ( isInfrastructureClass ( bean. getClass ( ) ) || shouldSkip ( bean. getClass ( ) , beanName) ) {
- this . advisedBeans. put ( cacheKey, Boolean . FALSE) ;
- return bean;
- }
-
- Object [ ] specificInterceptors = getAdvicesAndAdvisorsForBean ( bean. getClass ( ) , beanName, null ) ;
- if ( specificInterceptors != DO_NOT_PROXY) {
- this . advisedBeans. put ( cacheKey, Boolean . TRUE) ;
- Object proxy = createProxy (
- bean. getClass ( ) , beanName, specificInterceptors, new SingletonTargetSource ( bean) ) ;
- this . proxyTypes. put ( cacheKey, proxy. getClass ( ) ) ;
- return proxy;
- }
+}
+这个代码就非常简单,重点需要看FailoverClusterInvoker里的代码,FailoverClusterInvoker继承了AbstractClusterInvoker类,其中invoke 方法是在抽象类里实现的
+@Override
+public Result invoke(final Invocation invocation) throws RpcException {
+ checkWhetherDestroyed();
+ // binding attachments into invocation.
+ // 绑定 attachments 到 invocation 中.
+ Map<String, Object> contextAttachments = RpcContext.getContext().getObjectAttachments();
+ if (contextAttachments != null && contextAttachments.size() != 0) {
+ ((RpcInvocation) invocation).addObjectAttachments(contextAttachments);
+ }
+ // 列举 Invoker
+ List<Invoker<T>> invokers = list(invocation);
+ // 加载 LoadBalance 负载均衡器
+ LoadBalance loadbalance = initLoadBalance(invokers, invocation);
+ RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
+ // 调用 实际的 doInvoke 进行后续操作
+ return doInvoke(invocation, invokers, loadbalance);
+}
+// 这是个抽象方法,实际是由子类实现的
+ protected abstract Result doInvoke(Invocation invocation, List<Invoker<T>> invokers,
+ LoadBalance loadbalance) throws RpcException;
+然后重点就是FailoverClusterInvoker中的doInvoke方法了,其实它里面也就这么一个方法
+@Override
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
+ List<Invoker<T>> copyInvokers = invokers;
+ checkInvokers(copyInvokers, invocation);
+ String methodName = RpcUtils.getMethodName(invocation);
+ // 获取重试次数,这里默认是 2 次,还有可以注意下后面的+1
+ int len = getUrl().getMethodParameter(methodName, RETRIES_KEY, DEFAULT_RETRIES) + 1;
+ if (len <= 0) {
+ len = 1;
+ }
+ // retry loop.
+ RpcException le = null; // last exception.
+ List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyInvokers.size()); // invoked invokers.
+ Set<String> providers = new HashSet<String>(len);
+ // 循环调用,失败重试
+ for (int i = 0; i < len; i++) {
+ //Reselect before retry to avoid a change of candidate `invokers`.
+ //NOTE: if `invokers` changed, then `invoked` also lose accuracy.
+ if (i > 0) {
+ checkWhetherDestroyed();
+ // 在进行重试前重新列举 Invoker,这样做的好处是,如果某个服务挂了,
+ // 通过调用 list 可得到最新可用的 Invoker 列表
+ copyInvokers = list(invocation);
+ // check again
+ // 对 copyinvokers 进行判空检查
+ checkInvokers(copyInvokers, invocation);
+ }
+ // 通过负载均衡来选择 invoker
+ Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked);
+ // 将其添加到 invoker 到 invoked 列表中
+ invoked.add(invoker);
+ // 设置上下文
+ RpcContext.getContext().setInvokers((List) invoked);
+ try {
+ // 正式调用
+ Result result = invoker.invoke(invocation);
+ if (le != null && logger.isWarnEnabled()) {
+ logger.warn("Although retry the method " + methodName
+ + " in the service " + getInterface().getName()
+ + " was successful by the provider " + invoker.getUrl().getAddress()
+ + ", but there have been failed providers " + providers
+ + " (" + providers.size() + "/" + copyInvokers.size()
+ + ") from the registry " + directory.getUrl().getAddress()
+ + " on the consumer " + NetUtils.getLocalHost()
+ + " using the dubbo version " + Version.getVersion() + ". Last error is: "
+ + le.getMessage(), le);
+ }
+ return result;
+ } catch (RpcException e) {
+ if (e.isBiz()) { // biz exception.
+ throw e;
+ }
+ le = e;
+ } catch (Throwable e) {
+ le = new RpcException(e.getMessage(), e);
+ } finally {
+ providers.add(invoker.getUrl().getAddress());
+ }
+ }
+ throw new RpcException(le.getCode(), "Failed to invoke the method "
+ + methodName + " in the service " + getInterface().getName()
+ + ". Tried " + len + " times of the providers " + providers
+ + " (" + providers.size() + "/" + copyInvokers.size()
+ + ") from the registry " + directory.getUrl().getAddress()
+ + " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version "
+ + Version.getVersion() + ". Last error is: "
+ + le.getMessage(), le.getCause() != null ? le.getCause() : le);
+ }
- this . advisedBeans. put ( cacheKey, Boolean . FALSE) ;
- return bean;
- }
-然后在 org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy 中创建了代理类
+Failfast Cluster 快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
+Failsafe Cluster 失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
+Failback Cluster 失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
+Forking Cluster 并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks=”2” 来设置最大并行数。
+Broadcast Cluster 广播调用所有提供者,逐个调用,任意一台报错则报错 2。通常用于通知所有提供者更新缓存或日志等本地资源信息。
]]>
Java
- SpringBoot
+ Dubbo - RPC
+ Dubbo
+ 容错机制
Java
- Spring
- SpringBoot
- cglib
- 事务
-
-
-
- 聊聊 Dubbo 的 SPI
- /2020/05/31/%E8%81%8A%E8%81%8A-Dubbo-%E7%9A%84-SPI/
- SPI全称是Service Provider Interface,咋眼看跟api是不是有点相似,api是application interface,这两个其实在某些方面有类似的地方,也有蛮大的区别,比如我们基于 dubbo 的微服务,一般我们可以提供服务,然后非泛化调用的话,我们可以把 api 包提供给应用调用方,他们根据接口签名传对应参数并配置好对应的服务发现如 zk 等就可以调用我们的服务了,然后 spi 会有点类似但是是反过来的关系,相当于是一种规范,比如我约定完成这个功能需要两个有两个接口,一个是连接的,一个是断开的,其实就可以用 jdbc 的驱动举例,比较老套了,然后各个厂家去做具体的实现吧,到时候根据我接口的全限定名的文件来加载实际的实现类,然后运行的时候调用对应实现类的方法就完了
-
-看上面的图,java.sql.Driver就是 spi,对应在classpath 的 META-INF/services 目录下的这个文件,里边的内容就是具体的实现类
-
-简单介绍了 Java的 SPI,再来说说 dubbo 的,dubbo 中为啥要用 SPI 呢,主要是为了框架的可扩展性和性能方面的考虑,比如协议层 dubbo 默认使用 dubbo 协议,同时也支持很多其他协议,也支持用户自己实现协议,那么跟 Java 的 SPI 会有什么区别呢,我们也来看个文件
-
-是不是看着很想,又有点不一样,在 Java 的 SPI 配置文件里每一行只有一个实现类的全限定名,在 Dubbo的 SPI配置文件中是 key=value 的形式,我们只需要对应的 key 就能加载对应的实现,
-
- @SuppressWarnings ( "unchecked" )
- public T getExtension ( String name) {
- if ( name == null || name. length ( ) == 0 )
- throw new IllegalArgumentException ( "Extension name == null" ) ;
- if ( "true" . equals ( name) ) {
- return getDefaultExtension ( ) ;
- }
- Holder < Object > holder = cachedInstances. get ( name) ;
- if ( holder == null ) {
- cachedInstances. putIfAbsent ( name, new Holder < Object > ( ) ) ;
- holder = cachedInstances. get ( name) ;
- }
- Object instance = holder. get ( ) ;
- if ( instance == null ) {
- synchronized ( holder) {
- instance = holder. get ( ) ;
- if ( instance == null ) {
- instance = createExtension ( name) ;
- holder. set ( instance) ;
- }
- }
- }
- return ( T ) instance;
- }
-这里其实就可以看出来第二个不同点了,就是这个cachedInstances,第一个是不用像 Java 原生的 SPI 那样去遍历加载对应的服务类,只需要通过 key 去寻找,并且寻找的时候会先从缓存的对象里去取,还有就是注意下这里的 DCL(double check lock)
-@SuppressWarnings ( "unchecked" )
- private T createExtension ( String name) {
- Class < ? > clazz = getExtensionClasses ( ) . get ( name) ;
- if ( clazz == null ) {
- throw findException ( name) ;
- }
- try {
- T instance = ( T ) EXTENSION_INSTANCES. get ( clazz) ;
- if ( instance == null ) {
- EXTENSION_INSTANCES. putIfAbsent ( clazz, ( T ) clazz. newInstance ( ) ) ;
- instance = ( T ) EXTENSION_INSTANCES. get ( clazz) ;
- }
- injectExtension ( instance) ;
- Set < Class < ? > > wrapperClasses = cachedWrapperClasses;
- if ( wrapperClasses != null && wrapperClasses. size ( ) > 0 ) {
- for ( Class < ? > wrapperClass : wrapperClasses) {
- instance = injectExtension ( ( T ) wrapperClass. getConstructor ( type) . newInstance ( instance) ) ;
- }
- }
- return instance;
- } catch ( Throwable t) {
- throw new IllegalStateException ( "Extension instance(name: " + name + ", class: " +
- type + ") could not be instantiated: " + t. getMessage ( ) , t) ;
- }
- }
-然后就是创建扩展了,这里如果 wrapperClasses 就会遍历生成wrapper实例,并做 setter 依赖注入,但是这里cachedWrapperClasses的来源还是有点搞不清楚,得再看下 com.alibaba.dubbo.common.extension.ExtensionLoader#loadFile的具体逻辑 又看了遍新的代码,这个函数被抽出来了
-
- private boolean isWrapperClass ( Class < ? > clazz) {
- try {
- clazz. getConstructor ( type) ;
- return true ;
- } catch ( NoSuchMethodException e) {
- return false ;
- }
- }
-是否是 wrapperClass 其实就看构造函数的。
-]]>
-
- Java
- Dubbo
- RPC
- SPI
- Dubbo
- SPI
-
-
- Java
- Dubbo
- RPC
- SPI
+ Dubbo
+ RPC
+ 容错机制
@@ -11689,141 +11760,6 @@ typedef struct dict {
自旋
-
- 聊聊 Dubbo 的容错机制
- /2020/11/22/%E8%81%8A%E8%81%8A-Dubbo-%E7%9A%84%E5%AE%B9%E9%94%99%E6%9C%BA%E5%88%B6/
- 之前看了 dubbo 的一些代码,在学习过程中,主要关注那些比较“高级”的内容,SPI,自适应扩展等,却忘了一些作为一个 rpc 框架最核心需要的部分,比如如何通信,序列化,网络,容错机制等等,因为其实这个最核心的就是远程调用,自适应扩展其实就是让代码可扩展性,可读性,更优雅等,写的搓一点其实也问题不大,但是一个合适的通信协议,序列化方法,如何容错等却是真正保证是一个 rpc 框架最重要的要素。 首先来看这张图 在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试。 各节点关系:
-
-这里的 Invoker 是 Provider 的一个可调用 Service 的抽象,Invoker 封装了 Provider 地址及 Service 接口信息
-Directory 代表多个 Invoker,可以把它看成 List<Invoker> ,但与 List 不同的是,它的值可能是动态变化的,比如注册中心推送变更
-Cluster 将 Directory 中的多个 Invoker 伪装成一个 Invoker,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个
-Router 负责从多个 Invoker 中按路由规则选出子集,比如读写分离,应用隔离等
-LoadBalance 负责从多个 Invoker 中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选
-
-集群容错模式 Failover Cluster 失败自动切换,当出现失败,重试其它服务器 1。通常用于读操作,但重试会带来更长延迟。可通过 retries=”2” 来设置重试次数(不含第一次)。
-重试次数配置如下:
-<dubbo:service retries=”2” /> 这里重点看下 Failover Cluster集群模式的实现
-public class FailoverCluster implements Cluster {
-
- public final static String NAME = "failover" ;
-
- public < T > Invoker < T > join ( Directory < T > directory) throws RpcException {
- return new FailoverClusterInvoker < T > ( directory) ;
- }
-
-}
-这个代码就非常简单,重点需要看FailoverClusterInvoker里的代码,FailoverClusterInvoker继承了AbstractClusterInvoker类,其中invoke 方法是在抽象类里实现的
-@Override
-public Result invoke(final Invocation invocation) throws RpcException {
- checkWhetherDestroyed();
- // binding attachments into invocation.
- // 绑定 attachments 到 invocation 中.
- Map<String, Object> contextAttachments = RpcContext.getContext().getObjectAttachments();
- if (contextAttachments != null && contextAttachments.size() != 0) {
- ((RpcInvocation) invocation).addObjectAttachments(contextAttachments);
- }
- // 列举 Invoker
- List<Invoker<T>> invokers = list(invocation);
- // 加载 LoadBalance 负载均衡器
- LoadBalance loadbalance = initLoadBalance(invokers, invocation);
- RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
- // 调用 实际的 doInvoke 进行后续操作
- return doInvoke(invocation, invokers, loadbalance);
-}
-// 这是个抽象方法,实际是由子类实现的
- protected abstract Result doInvoke(Invocation invocation, List<Invoker<T>> invokers,
- LoadBalance loadbalance) throws RpcException;
-然后重点就是FailoverClusterInvoker中的doInvoke方法了,其实它里面也就这么一个方法
-@Override
- @SuppressWarnings({"unchecked", "rawtypes"})
- public Result doInvoke(Invocation invocation, final List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
- List<Invoker<T>> copyInvokers = invokers;
- checkInvokers(copyInvokers, invocation);
- String methodName = RpcUtils.getMethodName(invocation);
- // 获取重试次数,这里默认是 2 次,还有可以注意下后面的+1
- int len = getUrl().getMethodParameter(methodName, RETRIES_KEY, DEFAULT_RETRIES) + 1;
- if (len <= 0) {
- len = 1;
- }
- // retry loop.
- RpcException le = null; // last exception.
- List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyInvokers.size()); // invoked invokers.
- Set<String> providers = new HashSet<String>(len);
- // 循环调用,失败重试
- for (int i = 0; i < len; i++) {
- //Reselect before retry to avoid a change of candidate `invokers`.
- //NOTE: if `invokers` changed, then `invoked` also lose accuracy.
- if (i > 0) {
- checkWhetherDestroyed();
- // 在进行重试前重新列举 Invoker,这样做的好处是,如果某个服务挂了,
- // 通过调用 list 可得到最新可用的 Invoker 列表
- copyInvokers = list(invocation);
- // check again
- // 对 copyinvokers 进行判空检查
- checkInvokers(copyInvokers, invocation);
- }
- // 通过负载均衡来选择 invoker
- Invoker<T> invoker = select(loadbalance, invocation, copyInvokers, invoked);
- // 将其添加到 invoker 到 invoked 列表中
- invoked.add(invoker);
- // 设置上下文
- RpcContext.getContext().setInvokers((List) invoked);
- try {
- // 正式调用
- Result result = invoker.invoke(invocation);
- if (le != null && logger.isWarnEnabled()) {
- logger.warn("Although retry the method " + methodName
- + " in the service " + getInterface().getName()
- + " was successful by the provider " + invoker.getUrl().getAddress()
- + ", but there have been failed providers " + providers
- + " (" + providers.size() + "/" + copyInvokers.size()
- + ") from the registry " + directory.getUrl().getAddress()
- + " on the consumer " + NetUtils.getLocalHost()
- + " using the dubbo version " + Version.getVersion() + ". Last error is: "
- + le.getMessage(), le);
- }
- return result;
- } catch (RpcException e) {
- if (e.isBiz()) { // biz exception.
- throw e;
- }
- le = e;
- } catch (Throwable e) {
- le = new RpcException(e.getMessage(), e);
- } finally {
- providers.add(invoker.getUrl().getAddress());
- }
- }
- throw new RpcException(le.getCode(), "Failed to invoke the method "
- + methodName + " in the service " + getInterface().getName()
- + ". Tried " + len + " times of the providers " + providers
- + " (" + providers.size() + "/" + copyInvokers.size()
- + ") from the registry " + directory.getUrl().getAddress()
- + " on the consumer " + NetUtils.getLocalHost() + " using the dubbo version "
- + Version.getVersion() + ". Last error is: "
- + le.getMessage(), le.getCause() != null ? le.getCause() : le);
- }
-
-
-Failfast Cluster 快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
-Failsafe Cluster 失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
-Failback Cluster 失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
-Forking Cluster 并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks=”2” 来设置最大并行数。
-Broadcast Cluster 广播调用所有提供者,逐个调用,任意一台报错则报错 2。通常用于通知所有提供者更新缓存或日志等本地资源信息。
-]]>
-
- Java
- Dubbo - RPC
- Dubbo
- 容错机制
-
-
- Java
- Dubbo
- RPC
- 容错机制
-
-
聊聊 Java 中绕不开的 Synchronized 关键字
/2021/06/20/%E8%81%8A%E8%81%8A-Java-%E4%B8%AD%E7%BB%95%E4%B8%8D%E5%BC%80%E7%9A%84-Synchronized-%E5%85%B3%E9%94%AE%E5%AD%97/
@@ -12032,16 +11968,42 @@ public Result invoke(final Invocation invocation) throws RpcException {
- 是何原因竟让两人深夜奔袭十公里
- /2022/06/05/%E6%98%AF%E4%BD%95%E5%8E%9F%E5%9B%A0%E7%AB%9F%E8%AE%A9%E4%B8%A4%E4%BA%BA%E6%B7%B1%E5%A4%9C%E5%A5%94%E8%A2%AD%E5%8D%81%E5%85%AC%E9%87%8C/
- 偶尔来个标题党,不过也是一次比较神奇的经历 上周五下班后跟 LD 约好去吃牛蛙,某个朋友好像对这类都不太能接受,我以前小时候也不常吃,但是这类其实都是口味比较重,没有那种肉本身的腥味,而且肉质比较特殊,吃过几次以后就有点爱上了,这次刚好是 LD 买的新店开业券,比较优惠(我们俩都是有点勤俭持家的,想着小电驴还有三格电,这家店又有点远,骑车单趟大概要 10 公里左右,有点担心,LD 说应该可以的,就一起骑了过去(跟她轮换着骑电驴和共享单车),结果大概离吃牛蛙的店还有一辆公里的时候,电量就报警了,只有最后一个红色的了,一共是五格,最后一格是红色的,提示我们该充电了,这样子是真的有点慌了,之前开了几个月都是还有一两格电的时候就充电了,没有试验过究竟这最后一格电能开多远,总之先到了再说。 这家牛蛙没想到还挺热闹的,我们到那已经快八点了,还有十几个排队的,有个人还想插队(向来是不惯着这种,一边去),旁边刚好是有些商店就逛了下,就跟常规的商业中心差不多,开业的比较早也算是这一边比较核心的商业综合体了,各种品牌都有,而且还有彩票售卖点的,只是不太理解现在的彩票都是兑图案的,而且要 10 块钱一张,我的概念里还是以前 2 块钱一张的双色球,偶尔能中个五块十块的。排队还剩四五个的时候我们就去门口坐着等了,又等了大概二十分钟才排到我们,靠近我们等的里面的位置,好像好几个小女生在那还叫了外卖奶茶,然后各种拍照,小朋友的生活还是丰富多彩的,我们到了就点了蒜蓉的,没有点传说中紫苏的,菜单上画了 N 个🌶,LD 还是想体验下说下次人多点可以试试,我们俩吃怕太辣了吃不消,口味还是不错的,这家貌似是 LD 闺蜜推荐的,口碑有保证。两个人光吃一个蛙锅就差不多了,本来还想再点个其他的,后面实在吃不下了就没点,吃完还是惯例点了个奶茶,不过是真的不好找,太大了。 本来是就回个家的事了,结果就因为前面铺垫的小电驴已经只有一格电了,标题的深夜奔袭十公里就出现了,这个电驴估计续航也虚标挺严重的,电量也是这样,骑的时候显示只有一格电,关掉再开起来又有三格,然后我们回去骑了没一公里就没电了,这下是真的完球了,觉得车子也比较新,直接停外面也不放心,就开始了深夜的十公里推电驴奔袭,LD 看我太累还帮我中间推了一段,虽然是跑过十公里的,但是推着个没电的电驴,还是着实不容易的,LD 也是陪我推着车走,中间好几次说我们把电驴停着打车回去,把电池带回去充满了明天再过来骑车,可能是心态已经转变了,这应该算是一次很特殊的体验,从我们吃完出来大概十点,到最后我们推到小区,大概是过了两个小时的样子,说句深夜也不太过分,把这次这么推车看成了一种意志力的考验,很多事情也都是怕坚持,或者说怕不能坚持,想走得远,没有持续的努力坚持肯定是不行的,所以还是坚持着把车推回来(好吧,我其实主要是怕车被偷,毕竟刚来杭州上学没多久就被偷了自行车留下了阴影),中间感谢 LD,跟我轮着推了一段路,有些下坡的时候还在那坐着用脚蹬一下,离家里大概还有一公里的时候,有个骑电瓶车的大叔还停下来问我们是车破了还是没电了,应该是出于好意吧,最后快到的时候真的非常渴,买了2.5 升的水被我一口气喝了大半瓶,奶茶已经不能起到解渴的作用了,本来以为这样能消耗很多,结果第二天一称还重了,(我的称一定有问题 233
+ 聊聊 Java 的类加载机制一
+ /2020/11/08/%E8%81%8A%E8%81%8A-Java-%E7%9A%84%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6/
+ 一说到这个主题,想到的应该是双亲委派模型,不过讲的包括但不限于这个,主要内容是参考深入理解 Java 虚拟机书中的介绍, 一个类型的生命周期包含了七个阶段,加载,验证,准备,解析,初始化,使用,卸载。
+
+
+通过一个类的全限定名来获取定义此类的二进制字节流
+将这个字节流代表的静态存储结构转化为方法区的运行时数据结构
+在内存中生成了一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据的访问入口
+
+
+
+文件格式验证
+元数据验证
+字节码验证
+符号引用验证
+
+
+以上验证 、准备 、解析 三个阶段又合称为链接阶段,链接阶段要做的是将加载到JVM中的二进制字节流的类数据信息合并到JVM的运行时状态中。
+
+初始化 类的初始化阶段是类加载过程的最后一个步骤,也是除了自定义类加载器之外将主动权交给了应用程序,其实就是执行类构造器()方法的过程,()并不是我们在 Java 代码中直接编写的方法,它是 Javac编译器的自动生成物,()方法是由编译器自动收集类中的所有类变量的复制动作和静态句块(static{}块)中的语句合并产生的,编译器收集的顺序是由语句在原文件中出现的顺序决定的,静态语句块中只能访问定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块可以复制,但是不能访问,同时还要保证父类的执行先于子类,然后保证多线程下的并发问题
+
+最终,方法区会存储当前类类信息,包括类的静态变量、类初始化代码(定义静态变量时的赋值语句 和 静态初始化代码块)、实例变量定义、实例初始化代码(定义实例变量时的赋值语句实例代码块和构造方法)和实例方法,还有父类的类信息引用。
]]>
- 生活
+ Java
+ 类加载
-
- 生活
-
聊聊 Java 的类加载机制二
@@ -13501,86 +13463,101 @@ app. setWebAp
- 聊聊 Sharding-Jdbc 分库分表下的分页方案
- /2022/01/09/%E8%81%8A%E8%81%8A-Sharding-Jdbc-%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8%E4%B8%8B%E7%9A%84%E5%88%86%E9%A1%B5%E6%96%B9%E6%A1%88/
- 前面在聊 Sharding-Jdbc 的时候看到了一篇文章,关于一个分页的查询,一直比较直接的想法就是分库分表下的分页是非常不合理的,一般我们的实操方案都是分表加上 ES 搜索做分页,或者通过合表读写分离的方案,因为对于 sharding-jdbc 如果没有带分表键,查询基本都是只能在所有分表都执行一遍,然后再加上分页,基本上是分页越大后续的查询越耗资源,但是仔细的去想这个细节还是这次,就简单说说 首先就是我的分表结构
-CREATE TABLE ` student_time_0` (
- ` id` int ( 11 ) unsigned NOT NULL AUTO_INCREMENT ,
- ` user_id` int ( 11 ) NOT NULL ,
- ` name` varchar ( 200 ) COLLATE utf8_bin DEFAULT NULL ,
- ` age` tinyint ( 3 ) unsigned DEFAULT NULL ,
- ` create_time` bigint ( 20 ) DEFAULT NULL ,
- PRIMARY KEY ( ` id` )
-) ENGINE = InnoDB AUTO_INCREMENT = 674 DEFAULT CHARSET = utf8 COLLATE = utf8_bin;
-有这样的三个表,student_time_0, student_time_1, student_time_2, 以 user_id 作为分表键,根据表数量取模作为分表依据 这里先构造点数据,
-insert into student_time ( ` name` , ` user_id` , ` age` , ` create_time` ) values ( ?, ?, ?, ?)
-主要是为了保证 create_time 唯一比较好说明问题,
-int i = 0 ;
-try (
- Connection conn = dataSource. getConnection ( ) ;
- PreparedStatement ps = conn. prepareStatement ( insertSql) ) {
- do {
- ps. setString ( 1 , localName + new Random ( ) . nextInt ( 100 ) ) ;
- ps. setLong ( 2 , 10086L + ( new Random ( ) . nextInt ( 100 ) ) ) ;
- ps. setInt ( 3 , 18 ) ;
- ps. setLong ( 4 , new Date ( ) . getTime ( ) ) ;
+ 聊聊 Sharding-Jdbc 的简单使用
+ /2021/12/12/%E8%81%8A%E8%81%8A-Sharding-Jdbc-%E7%9A%84%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8/
+ 我们在日常工作中还是使用比较多的分库分表组件的,其中比较优秀的就有 Sharding-Jdbc,一开始由当当开源,后来捐献给了 Apache,说一下简单使用,因为原来经常的使用都是基于 xml 跟 properties 组合起来使用,这里主要试下用 Java Config 来配置 首先是通过 Spring Initializr 创建个带 jdbc 的 Spring Boot 项目,然后引入主要的依赖
+< dependency>
+ < groupId> org.apache.shardingsphere</ groupId>
+ < artifactId> shardingsphere-jdbc-core</ artifactId>
+ < version> 5.0.0-beta</ version>
+</ dependency>
+因为前面有聊过 Spring Boot 的自动加载,在这里 spring 就会自己去找 DataSource 的配置,所以要在入口把它干掉
+@SpringBootApplication ( exclude = { DataSourceAutoConfiguration . class } )
+public class ShardingJdbcDemoApplication implements CommandLineRunner {
+然后因为想在入口跑代码,就实现了下 org.springframework.boot.CommandLineRunner 主要是后面的 Java Config 代码
+
+
+@Configuration
+public class MysqlConfig {
+ @Bean
+ public DataSource dataSource ( ) throws SQLException {
+
+ Map < String , DataSource > dataSourceMap = new HashMap < > ( ) ;
- int result = ps. executeUpdate ( ) ;
- LOGGER. info ( "current execute result: {}" , result) ;
- Thread . sleep ( new Random ( ) . nextInt ( 100 ) ) ;
- i++ ;
- } while ( i <= 2000 ) ;
-三个表的数据分别是 673,678,650,说明符合预期了,各个表数据不一样,接下来比如我们想要做一个这样的分页查询
-select * from student_time ORDER BY create_time ASC limit 1000 , 5 ;
-student_time 对于我们使用的 sharding-jdbc 来说当然是逻辑表,首先从一无所知去想这个查询如果我们自己来处理应该是怎么做, 首先是不是可以每个表都从 333 开始取 5 条数据,类似于下面的查询,然后进行 15 条的合并重排序获取前面的 5 条
-select * from student_time_0 ORDER BY create_time ASC limit 333, 5;
-select * from student_time_1 ORDER BY create_time ASC limit 333, 5;
-select * from student_time_2 ORDER BY create_time ASC limit 333, 5;
-忽略前面 limit 差的 1,这个结果除非三个表的分布是绝对的均匀,否则结果肯定会出现一定的偏差,以为每个表的 333 这个位置对于其他表来说都不一定是一样的,这样对于最后整体的结果,就会出现偏差 因为一直在纠结怎么让这个更直观的表现出来,所以尝试画了个图 黑色的框代表我从每个表里按排序从 334 到 338 的 5 条数据, 他们在每个表里都是代表了各自正确的排序值,但是对于我们想要的其实是合表后的 1001,1005 这五条,然后我们假设总的排序值位于前 1000 的分布是第 0 个表是 320 条,第 1 个表是 340 条,第 2 个表是 340 条,那么可以明显地看出来我这么查的结果简单合并肯定是不对的。 那么 sharding-jdbc 是如何保证这个结果的呢,其实就是我在每个表里都查分页偏移量和分页大小那么多的数据,在我这个例子里就是对于 0,1,2 三个分表每个都查 1005 条数据,即使我的数据不平衡到最极端的情况,前 1005 条数据都出在某个分表中,也可以正确获得最后的结果,但是明显的问题就是大分页,数据较多,就会导致非常大的问题,即使如 sharding-jdbc 对于合并排序的优化做得比较好,也还是需要传输那么大量的数据,并且查询也耗时,那么有没有解决方案呢,应该说有两个,或者说主要是想讲后者 第一个办法是像这种查询,如果业务上不需要进行跳页,而是只给下一页,那么我们就能把前一次的最大偏移量的 create_time 记录下来,下一页就可以拿着这个偏移量进行查询,这个比较简单易懂,就不多说了 第二个办法是看的58 沈剑的一篇文章,尝试理解讲述一下, 这个办法的第一步跟前面那个错误的方法或者说不准确的方法一样,先是将分页偏移量平均后在三个表里进行查询
-t0
-334 10158 nick95 18 1641548941767
-335 10098 nick11 18 1641548941879
-336 10167 nick51 18 1641548942089
-337 10167 nick3 18 1641548942119
-338 10170 nick57 18 1641548942169
+
+
+
+ HikariDataSource dataSource1 = new HikariDataSource ( ) ;
+ dataSource1. setDriverClassName ( "com.mysql.jdbc.Driver" ) ;
+ dataSource1. setJdbcUrl ( "jdbc:mysql://localhost:3306/sharding" ) ;
+ dataSource1. setUsername ( "username" ) ;
+ dataSource1. setPassword ( "password" ) ;
+ dataSourceMap. put ( "ds0" , dataSource1) ;
+
+
+ ShardingTableRuleConfiguration studentTableRuleConfig = new ShardingTableRuleConfiguration ( "student" , "ds0.student_$->{0..1}" ) ;
-t1
-334 10105 nick98 18 1641548939071 最小
-335 10174 nick94 18 1641548939377
-336 10129 nick85 18 1641548939442
-337 10141 nick84 18 1641548939480
-338 10096 nick74 18 1641548939668
+
+ studentTableRuleConfig. setTableShardingStrategy ( new StandardShardingStrategyConfiguration ( "user_id" , "tableShardingAlgorithm" ) ) ;
-t2
-334 10184 nick11 18 1641548945075
-335 10109 nick93 18 1641548945382
-336 10181 nick41 18 1641548945583
-337 10130 nick80 18 1641548945993
-338 10184 nick19 18 1641548946294 最大
-然后要做什么呢,其实目标比较明白,因为前面那种方法其实就是我知道了前一页的偏移量,所以可以直接当做条件来进行查询,那这里我也想着拿到这个条件,所以我将第一遍查出来的最小的 create_time 和最大的 create_time 找出来,然后再去三个表里查询,其实主要是最小值,因为我拿着最小值去查以后我就能知道这个最小值在每个表里处在什么位置,
-t0
-322 10161 nick81 18 1641548939284
-323 10113 nick16 18 1641548939393
-324 10110 nick56 18 1641548939577
-325 10116 nick69 18 1641548939588
-326 10173 nick51 18 1641548939646
-t1
-334 10105 nick98 18 1641548939071
-335 10174 nick94 18 1641548939377
-336 10129 nick85 18 1641548939442
-337 10141 nick84 18 1641548939480
-338 10096 nick74 18 1641548939668
+
+
+ ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration ( ) ;
+ shardingRuleConfig. getTables ( ) . add ( studentTableRuleConfig) ;
-t2
-297 10136 nick28 18 1641548939161
-298 10142 nick68 18 1641548939177
-299 10124 nick41 18 1641548939237
-300 10148 nick87 18 1641548939510
-301 10169 nick23 18 1641548939715
-我只贴了前五条数据,为了方便知道偏移量,每个分表都使用了自增主键,我们可以看到前一次查询的最小值分别在其他两个表里的位置分别是 322-1 和 297-1,那么对于总体来说这个时间应该是在 322 - 1 + 333 + 297 - 1 = 951,那这样子我只要对后面的数据最多每个表查 1000 - 951 + 5 = 54 条数据再进行合并排序就可以获得最终正确的结果。 这个就是传说中的二次查询法。
+
+ Properties tableShardingAlgorithmrProps = new Properties ( ) ;
+
+ tableShardingAlgorithmrProps. setProperty ( "algorithm-expression" , "student_${user_id % 2}" ) ;
+ shardingRuleConfig. getShardingAlgorithms ( ) . put ( "tableShardingAlgorithm" , new ShardingSphereAlgorithmConfiguration ( "INLINE" , tableShardingAlgorithmrProps) ) ;
+
+
+
+ return ShardingSphereDataSourceFactory . createDataSource ( dataSourceMap, Collections . singleton ( shardingRuleConfig) , new Properties ( ) ) ;
+
+ }
+}
+然后我们就可以在使用这个 DataSource 了,先看下这两个表的数据
+@Override
+ public void run ( String . . . args) {
+ LOGGER. info ( "run here" ) ;
+ String sql = "SELECT * FROM student WHERE user_id=? " ;
+ try (
+ Connection conn = dataSource. getConnection ( ) ;
+ PreparedStatement ps = conn. prepareStatement ( sql) ) {
+
+ ps. setInt ( 1 , 1001 ) ;
+
+ ResultSet resultSet = ps. executeQuery ( ) ;
+ while ( resultSet. next ( ) ) {
+ final int id = resultSet. getInt ( "id" ) ;
+ final String name = resultSet. getString ( "name" ) ;
+ final int userId = resultSet. getInt ( "user_id" ) ;
+ final int age = resultSet. getInt ( "age" ) ;
+ System . out. println ( "奇数表 id:" + id + " 姓名:" + name
+ + " 用户 id:" + userId + " 年龄:" + age ) ;
+ System . out. println ( "=============================" ) ;
+ }
+
+ ps. setInt ( 1 , 1000 ) ;
+ resultSet = ps. executeQuery ( ) ;
+ while ( resultSet. next ( ) ) {
+ final int id = resultSet. getInt ( "id" ) ;
+ final String name = resultSet. getString ( "name" ) ;
+ final int userId = resultSet. getInt ( "user_id" ) ;
+ final int age = resultSet. getInt ( "age" ) ;
+ System . out. println ( "偶数表 id:" + id + " 姓名:" + name
+ + " 用户 id:" + userId + " 年龄:" + age ) ;
+ System . out. println ( "=============================" ) ;
+ }
+ } catch ( SQLException e) {
+ e. printStackTrace ( ) ;
+ }
+ }
+看下查询结果
]]>
Java
@@ -13590,6 +13567,25 @@ t2
Sharding-Jdbc
+
+ 周末我在老丈人家打了天小工
+ /2020/08/16/%E5%91%A8%E6%9C%AB%E6%88%91%E5%9C%A8%E8%80%81%E4%B8%88%E4%BA%BA%E5%AE%B6%E6%89%93%E4%BA%86%E5%A4%A9%E5%B0%8F%E5%B7%A5/
+ 这周回家提前约好了要去老丈人家帮下忙,因为在翻修下老房子,活不是特别整的那种,所以大部分都是自己干,或者找个大工临时干几天(我们这那种比较专业的泥工匠叫做大工),像我这样去帮忙的,就是干点小工(把给大工帮忙的,干些偏体力活的叫做小工)的活。从大学毕业以后真的蛮少帮家里干活了,以前上学的时候放假还是帮家里淘个米,简单的扫地拖地啥的,当然刚高考完的时候,还去我爸厂里帮忙干了几天的活,实在是比较累,不过现在想着是觉得自己那时候比较牛,而不是特别排斥这个活,相对于现在的工作来说,导致了一系列的职业病,颈椎腰背都很僵硬,眼镜也不好,还有反流,像我爸那种活反而是脑力加体力的比较好的结合。 这一天的活前半部分主要是在清理厨房,瓷砖上的油污和墙上天花板上即将脱落的石灰或者白色涂料层,这种活特别是瓷砖上的油污,之前在自己家里也干活,还是比较熟悉的,不过前面主要是LD 在干,我主要是先搞墙上和天花板上的,干活还是很需要技巧的,如果直接去铲,那基本我会变成一个灰人,而且吸一鼻子灰,老丈人比较专业,先接上软管用水冲,一冲效果特别好,有些石灰涂料层直接就冲掉了,冲完之后先用带加长杆的刀片铲铲了一圈墙面,说实话因为老房子之前租出去了,所以墙面什么的被糟蹋的比较难看,一层一层的,不过这还算还好,后面主要是天花板上的,这可难倒我了,从小我爸妈是比较把我当小孩管着,爬上爬下的基本都是我爸搞定,但是到了老丈人家也只得硬着头皮上了,爬到跳(一种建筑工地用的架子)上,还有点晃,小心脏扑通扑通跳,而且带加长杆的铲子还是比较重的,铲一会手也有点累,不过坚持着铲完了,上面还是比较平整的,不过下来的时候又把我难住了🤦♂️,往下爬的时候有根杆子要跨过去,由于裤子比较紧,强行一把跨过去怕抽筋,所以以一个非常尴尬的姿势停留休息了一会,再跨了过去,幸好事后问 LD,他们都没看到,哈哈哈,然后就是帮忙一起搞瓷砖上的油污,这个太有经验了,不过老丈人更有意思,一会试试啤酒,一会用用沙子,后面在午饭前基本就弄的比较干净了,就坐着等吃饭了,下午午休了会,就继续干活了。 下午是我这次体验的重点了,因为要清理以前贴的墙纸,真的是个很麻烦的活,只能说贴墙纸的师傅活干得太好了,基本不可能整个撕下来,想用铲子一点点铲下来也不行,太轻了就只铲掉表面一层,太重了就把墙纸跟墙面的石灰啥的整个铲下来了,而且手又累又酸,后来想着是不是继续用水冲一下,对着一小面墙试验了下,效果还不错,但是又发现了个问题,那一面墙又有一块是后面糊上去的,铲掉外层的石灰后不平,然后就是最最重头的,也是让我后遗症持续到第二天的,要把那一块糊上去的水泥敲下来,毛估下大概是敲了80%左右,剩下的我的手已经不会用力了,因为那一块应该是要糊上去的始作俑者,就一块里面凹进去的,我拿着榔头敲到我手已经没法使劲了,而且大下午,感觉没五分钟,我的汗已经糊满脸,眼睛也睁不开,不然就流到眼睛里了,此处获得成就一:用榔头敲墙壁,也是个技术加体力的活,而且需要非常好的技巧,否则手马上就废了,敲下去的反作用力,没一会就不行了,然后是看着老丈人兄弟帮忙拆一个柜子,在我看来是个几天都搞不定的活,他轻轻松松在我敲墙的那会就搞定了,以前总觉得我干的活非常有技术含量,可是这个事情真的也是很有技巧啊,它是个把一间房间分隔开的柜子,从底到顶上,还带着门,我还在旁边帮忙撬一下脚踢,一根木条撬半天,唉,成就二:专业的人就是不一样。 最后就是成就三了:我之前沾沾自喜的跑了多少步,做了什么锻炼,其实都是渣渣,像这样干一天活,没经历过的,基本大半天就废了,反过来说,如果能经常去这么干一天活,跑步啥的都是渣渣,消耗的能量远远超过跑个十公里啥的。
+]]>
+
+ 生活
+ 运动
+ 跑步
+ 干活
+
+
+ 生活
+ 运动
+ 减肥
+ 跑步
+ 干活
+
+
聊聊 dubbo 的线程池
/2021/04/04/%E8%81%8A%E8%81%8A-dubbo-%E7%9A%84%E7%BA%BF%E7%A8%8B%E6%B1%A0/
@@ -13727,32 +13723,173 @@ void ReadView::prepare(trx_id_t id) {
- 聊聊 Sharding-Jdbc 的简单原理初篇
- /2021/12/26/%E8%81%8A%E8%81%8A-Sharding-Jdbc-%E7%9A%84%E7%AE%80%E5%8D%95%E5%8E%9F%E7%90%86%E5%88%9D%E7%AF%87/
- 在上一篇 sharding-jdbc 的介绍中其实碰到过一个问题,这里也引出了一个比较有意思的话题 就是我在执行 query 的时候犯过一个比较难发现的错误,
-ResultSet resultSet = ps. executeQuery ( sql) ;
-实际上应该是
-ResultSet resultSet = ps. executeQuery ( ) ;
-而这里的差别就是,是否传 sql 这个参数,首先我们要知道这个 ps 是什么,它也是个接口java.sql.PreparedStatement,而真正的实现类是org.apache.shardingsphere.driver.jdbc.core.statement.ShardingSpherePreparedStatement,我们来看下继承关系 这里可以看到继承关系里有org.apache.shardingsphere.driver.jdbc.unsupported.AbstractUnsupportedOperationPreparedStatement 那么在我上面的写错的代码里
-@Override
-public final ResultSet executeQuery ( final String sql) throws SQLException {
- throw new SQLFeatureNotSupportedException ( "executeQuery with SQL for PreparedStatement" ) ;
-}
-这个报错一开始让我有点懵,后来点进去了发现是这么个异常,但是我其实一开始是用的更新语句,以为更新不支持,因为平时使用没有深究过,以为是不是需要使用 Mybatis 才可以执行更新,但是理论上也不应该,再往上看原来这些异常是由 sharding-jdbc 包装的,也就是在上面说的AbstractUnsupportedOperationPreparedStatement,这其实也是一种设计思想,本身 jdbc 提供了一系列接口,由各家去支持,包括 mysql,sql server,oracle 等,而正因为这个设计,所以 sharding-jdbc 也可以在此基础上进行设计,我们可以总体地看下 sharding-jdbc 的实现基础 看了前面ShardingSpherePreparedStatement的继承关系,应该也能猜到这里的几个类都是实现了 jdbc 的基础接口, 在前一篇的 demo 中的
-Connection conn = dataSource. getConnection ( ) ;
-其实就获得了org.apache.shardingsphere.driver.jdbc.core.connection.ShardingSphereConnection#ShardingSphereConnection 然后获得java.sql.PreparedStatement
-PreparedStatement ps = conn. prepareStatement ( sql)
-就是获取了org.apache.shardingsphere.driver.jdbc.core.statement.ShardingSpherePreparedStatement 然后就是执行
-ResultSet resultSet = ps. executeQuery ( ) ;
-然后获得结果org.apache.shardingsphere.driver.jdbc.core.resultset.ShardingSphereResultSet
-其实像 mybatis 也是基于这样去实现的
+ 聊聊 mysql 的 MVCC
+ /2020/04/26/%E8%81%8A%E8%81%8A-mysql-%E7%9A%84-MVCC/
+ 很久以前,有位面试官问到,你知道 mysql 的事务隔离级别吗,“额 O__O …,不太清楚”,完了之后我就去网上找相关的文章,找到了这篇MySQL 四种事务隔离级的说明 , 文章写得特别好,看了这个就懂了各个事务隔离级别都是啥,不过看了这个之后多思考一下的话还是会发现问题,这么神奇的事务隔离级别是怎么实现的呢
+其中 innodb 的事务隔离用到了标题里说到的 mvcc,Multiversion concurrency control , 直译过来就是多版本并发控制,先不讲这个究竟是个啥,考虑下如果纯猜测,这个事务隔离级别应该会是怎么样实现呢,愚钝的我想了下,可以在事务开始的时候拷贝一个表,这个可以支持 RR 级别,RC 级别就不支持了,而且要是个非常大的表,想想就不可行
+腆着脸说虽然这个不可行,但是思路是对的,具体实行起来需要做一系列(肥肠多)的改动,首先根据我的理解,其实这个拷贝一个表是变成拷贝一条记录,但是如果有多个事务,那就得拷贝多次,这个问题其实可以借助版本管理系统来解释,在用版本管理系统,git 之类的之前,很原始的可能是开发完一个功能后,就打个压缩包用时间等信息命名,然后如果后面要找回这个就直接用这个压缩包的就行了,后来有了 svn,git 中心式和分布式的版本管理系统,它的一个特点是粒度可以控制到文件和代码行级别,对应的我们的 mysql 事务是不是也可以从一开始预想的表级别细化到行的级别,可能之前很多人都了解过,数据库的一行记录除了我们用户自定义的字段,还有一些额外的字段,去源码data0type.h 里捞一下
+
+# define DATA_ROW_ID 0
+# define DATA_ROW_ID_LEN 6
+
+
+constexpr size_t DATA_TRX_ID = 1 ;
+
+
+constexpr size_t DATA_TRX_ID_LEN = 6 ;
+
+
+constexpr size_t DATA_ROLL_PTR = 2 ;
+
+
+constexpr size_t DATA_ROLL_PTR_LEN = 7 ;
+
+一个是 DATA_ROW_ID,这个是在数据没指定主键的时候会生成一个隐藏的,如果用户有指定主键就是主键了
+一个是 DATA_TRX_ID,这个表示这条记录的事务 ID
+还有一个是 DATA_ROLL_PTR 指向回滚段的指针
+指向的回滚段其实就是我们常说的 undo log,这里面的具体结构就是个链表,在 mvcc 里会使用到这个,还有就是这个 DATA_TRX_ID,每条记录都记录了这个事务 ID,表示的是这条记录的当前值是被哪个事务修改的,下面就扯回事务了,我们知道 Read Uncommitted, 其实用不到隔离,直接读取当前值就好了,到了 Read Committed 级别,我们要让事务读取到提交过的值,mysql 使用了一个叫 read view 的玩意,它里面有这些值是我们需要注意的,
+m_low_limit_id, 这个是 read view 创建时最大的活跃事务 id
+m_up_limit_id, 这个是 read view 创建时最小的活跃事务 id
+m_ids, 这个是 read view 创建时所有的活跃事务 id 数组
+m_creator_trx_id 这个是当前记录的创建事务 id
+判断事务的可见性主要的逻辑是这样,
+
+当记录的事务 id 小于最小活跃事务 id,说明是可见的,
+如果记录的事务 id 等于当前事务 id,说明是自己的更改,可见
+如果记录的事务 id 大于最大的活跃事务 id, 不可见
+如果记录的事务 id 介于 m_low_limit_id 和 m_up_limit_id 之间,则要判断它是否在 m_ids 中,如果在,不可见,如果不在,表示已提交,可见 具体的代码 捞一下看看/** Check whether the changes by id are visible.
+ @param[in] id transaction id to check against the view
+ @param[in] name table name
+ @return whether the view sees the modifications of id. */
+ bool changes_visible(trx_id_t id, const table_name_t &name) const
+ MY_ATTRIBUTE((warn_unused_result)) {
+ ut_ad(id > 0);
+
+ if (id < m_up_limit_id || id == m_creator_trx_id) {
+ return (true);
+ }
+
+ check_trx_id_sanity(id, name);
+
+ if (id >= m_low_limit_id) {
+ return (false);
+
+ } else if (m_ids.empty()) {
+ return (true);
+ }
+
+ const ids_t::value_type *p = m_ids.data();
+
+ return (!std::binary_search(p, p + m_ids.size(), id));
+ }
+剩下来一点是啥呢,就是 Read Committed 和 Repeated Read 也不一样,那前面说的 read view 都能支持吗,又是怎么支持呢,假如这个 read view 是在事务一开始就创建,那好像能支持的只是 RR 事务隔离级别,其实呢,这是通过创建 read view的时机,对于 RR 级别,就是在事务的第一个 select 语句是创建,对于 RC 级别,是在每个 select 语句执行前都是创建一次,那样就可以保证能读到所有已提交的数据
+
]]>
- Java
+ Mysql
+ C
+ 数据结构
+ 源码
+ Mysql
- Java
- Sharding-Jdbc
+ mysql
+ 数据结构
+ 源码
+ mvcc
+ read view
+
+
+
+ 聊聊 Sharding-Jdbc 分库分表下的分页方案
+ /2022/01/09/%E8%81%8A%E8%81%8A-Sharding-Jdbc-%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8%E4%B8%8B%E7%9A%84%E5%88%86%E9%A1%B5%E6%96%B9%E6%A1%88/
+ 前面在聊 Sharding-Jdbc 的时候看到了一篇文章,关于一个分页的查询,一直比较直接的想法就是分库分表下的分页是非常不合理的,一般我们的实操方案都是分表加上 ES 搜索做分页,或者通过合表读写分离的方案,因为对于 sharding-jdbc 如果没有带分表键,查询基本都是只能在所有分表都执行一遍,然后再加上分页,基本上是分页越大后续的查询越耗资源,但是仔细的去想这个细节还是这次,就简单说说 首先就是我的分表结构
+CREATE TABLE ` student_time_0` (
+ ` id` int ( 11 ) unsigned NOT NULL AUTO_INCREMENT ,
+ ` user_id` int ( 11 ) NOT NULL ,
+ ` name` varchar ( 200 ) COLLATE utf8_bin DEFAULT NULL ,
+ ` age` tinyint ( 3 ) unsigned DEFAULT NULL ,
+ ` create_time` bigint ( 20 ) DEFAULT NULL ,
+ PRIMARY KEY ( ` id` )
+) ENGINE = InnoDB AUTO_INCREMENT = 674 DEFAULT CHARSET = utf8 COLLATE = utf8_bin;
+有这样的三个表,student_time_0, student_time_1, student_time_2, 以 user_id 作为分表键,根据表数量取模作为分表依据 这里先构造点数据,
+insert into student_time ( ` name` , ` user_id` , ` age` , ` create_time` ) values ( ?, ?, ?, ?)
+主要是为了保证 create_time 唯一比较好说明问题,
+int i = 0 ;
+try (
+ Connection conn = dataSource. getConnection ( ) ;
+ PreparedStatement ps = conn. prepareStatement ( insertSql) ) {
+ do {
+ ps. setString ( 1 , localName + new Random ( ) . nextInt ( 100 ) ) ;
+ ps. setLong ( 2 , 10086L + ( new Random ( ) . nextInt ( 100 ) ) ) ;
+ ps. setInt ( 3 , 18 ) ;
+ ps. setLong ( 4 , new Date ( ) . getTime ( ) ) ;
+
+
+ int result = ps. executeUpdate ( ) ;
+ LOGGER. info ( "current execute result: {}" , result) ;
+ Thread . sleep ( new Random ( ) . nextInt ( 100 ) ) ;
+ i++ ;
+ } while ( i <= 2000 ) ;
+三个表的数据分别是 673,678,650,说明符合预期了,各个表数据不一样,接下来比如我们想要做一个这样的分页查询
+select * from student_time ORDER BY create_time ASC limit 1000 , 5 ;
+student_time 对于我们使用的 sharding-jdbc 来说当然是逻辑表,首先从一无所知去想这个查询如果我们自己来处理应该是怎么做, 首先是不是可以每个表都从 333 开始取 5 条数据,类似于下面的查询,然后进行 15 条的合并重排序获取前面的 5 条
+select * from student_time_0 ORDER BY create_time ASC limit 333, 5;
+select * from student_time_1 ORDER BY create_time ASC limit 333, 5;
+select * from student_time_2 ORDER BY create_time ASC limit 333, 5;
+忽略前面 limit 差的 1,这个结果除非三个表的分布是绝对的均匀,否则结果肯定会出现一定的偏差,以为每个表的 333 这个位置对于其他表来说都不一定是一样的,这样对于最后整体的结果,就会出现偏差 因为一直在纠结怎么让这个更直观的表现出来,所以尝试画了个图 黑色的框代表我从每个表里按排序从 334 到 338 的 5 条数据, 他们在每个表里都是代表了各自正确的排序值,但是对于我们想要的其实是合表后的 1001,1005 这五条,然后我们假设总的排序值位于前 1000 的分布是第 0 个表是 320 条,第 1 个表是 340 条,第 2 个表是 340 条,那么可以明显地看出来我这么查的结果简单合并肯定是不对的。 那么 sharding-jdbc 是如何保证这个结果的呢,其实就是我在每个表里都查分页偏移量和分页大小那么多的数据,在我这个例子里就是对于 0,1,2 三个分表每个都查 1005 条数据,即使我的数据不平衡到最极端的情况,前 1005 条数据都出在某个分表中,也可以正确获得最后的结果,但是明显的问题就是大分页,数据较多,就会导致非常大的问题,即使如 sharding-jdbc 对于合并排序的优化做得比较好,也还是需要传输那么大量的数据,并且查询也耗时,那么有没有解决方案呢,应该说有两个,或者说主要是想讲后者 第一个办法是像这种查询,如果业务上不需要进行跳页,而是只给下一页,那么我们就能把前一次的最大偏移量的 create_time 记录下来,下一页就可以拿着这个偏移量进行查询,这个比较简单易懂,就不多说了 第二个办法是看的58 沈剑的一篇文章,尝试理解讲述一下, 这个办法的第一步跟前面那个错误的方法或者说不准确的方法一样,先是将分页偏移量平均后在三个表里进行查询
+t0
+334 10158 nick95 18 1641548941767
+335 10098 nick11 18 1641548941879
+336 10167 nick51 18 1641548942089
+337 10167 nick3 18 1641548942119
+338 10170 nick57 18 1641548942169
+
+
+t1
+334 10105 nick98 18 1641548939071 最小
+335 10174 nick94 18 1641548939377
+336 10129 nick85 18 1641548939442
+337 10141 nick84 18 1641548939480
+338 10096 nick74 18 1641548939668
+
+t2
+334 10184 nick11 18 1641548945075
+335 10109 nick93 18 1641548945382
+336 10181 nick41 18 1641548945583
+337 10130 nick80 18 1641548945993
+338 10184 nick19 18 1641548946294 最大
+然后要做什么呢,其实目标比较明白,因为前面那种方法其实就是我知道了前一页的偏移量,所以可以直接当做条件来进行查询,那这里我也想着拿到这个条件,所以我将第一遍查出来的最小的 create_time 和最大的 create_time 找出来,然后再去三个表里查询,其实主要是最小值,因为我拿着最小值去查以后我就能知道这个最小值在每个表里处在什么位置,
+t0
+322 10161 nick81 18 1641548939284
+323 10113 nick16 18 1641548939393
+324 10110 nick56 18 1641548939577
+325 10116 nick69 18 1641548939588
+326 10173 nick51 18 1641548939646
+
+t1
+334 10105 nick98 18 1641548939071
+335 10174 nick94 18 1641548939377
+336 10129 nick85 18 1641548939442
+337 10141 nick84 18 1641548939480
+338 10096 nick74 18 1641548939668
+
+t2
+297 10136 nick28 18 1641548939161
+298 10142 nick68 18 1641548939177
+299 10124 nick41 18 1641548939237
+300 10148 nick87 18 1641548939510
+301 10169 nick23 18 1641548939715
+我只贴了前五条数据,为了方便知道偏移量,每个分表都使用了自增主键,我们可以看到前一次查询的最小值分别在其他两个表里的位置分别是 322-1 和 297-1,那么对于总体来说这个时间应该是在 322 - 1 + 333 + 297 - 1 = 951,那这样子我只要对后面的数据最多每个表查 1000 - 951 + 5 = 54 条数据再进行合并排序就可以获得最终正确的结果。 这个就是传说中的二次查询法。
+]]>
+
+ Java
+
+
+ Java
+ Sharding-Jdbc
@@ -13813,467 +13950,117 @@ $
Mysql
- mysql
- 索引
- is null
- is not null
- procedure
-
-
-
- 聊聊 redis 缓存的应用问题
- /2021/01/31/%E8%81%8A%E8%81%8A-redis-%E7%BC%93%E5%AD%98%E7%9A%84%E5%BA%94%E7%94%A8%E9%97%AE%E9%A2%98/
- 前面写过一系列的 redis 源码分析的,但是实际上很多的问题还是需要结合实际的使用,然后其实就避不开缓存使用的三个著名问题,穿透,击穿和雪崩,这三个概念也是有着千丝万缕的关系,
-缓存穿透 缓存穿透是指当数据库中本身就不存在这个数据的时候,使用一般的缓存策略时访问不到缓存后就访问数据库,但是因为数据库也没数据,所以如果不做任何策略优化的话,这类数据就每次都会访问一次数据库,对数据库压力也会比较大。
-缓存击穿 缓存击穿跟穿透比较类似的,都是访问缓存不在,然后去访问数据库,与穿透不一样的是击穿是在数据库中存在数据,但是可能由于第一次访问,或者缓存过期了,需要访问到数据库,这对于访问量小的情况其实算是个正常情况,但是随着请求量变高就会引发一些性能隐患。
-缓存雪崩 缓存雪崩就是击穿的大规模集群效应,当大量的缓存过期失效的时候,这些请求都是直接访问到数据库了,会对数据库造成很大的压力。
-对于以上三种场景也有一些比较常见的解决方案,但也不能说是万无一失的,需要随着业务去寻找合适的方案
-解决缓存穿透 对于数据库中就没这个数据的时候,一种是可以对这个 key 设置下空值,即以一个特定的表示是数据库不存在的,这种情况需要合理地调整过期时间,当这个 key 在数据库中有数据了的话,也需要有策略去更新这个值,并且如果这类 key 非常多,这个方法就会不太合适,就可以使用第二种方法,就是布隆过滤器,bloom filter,前置一个布隆过滤器,当这个 key 在数据库不存在的话,先用布隆过滤器挡一道,如果不在的话就直接返回了,当然布隆过滤器不是绝对的准确的
-解决缓存击穿 当一个 key 的缓存过期了,如果大量请求过来访问这个 key,请求都会落在数据库里,这个时候就可以使用一些类似于互斥锁的方式去让一个线程去访问数据库,更新缓存,但是这里其实也有个问题,就是如果是热点 key 其实这种方式也比较危险,万一更新失败,或者更新操作的时候耗时比较久,就会有一大堆请求卡在那,这种情况可能需要有一些异步提前刷新缓存,可以结合具体场景选择方式
-解决缓存雪崩 雪崩的情况是指大批量的 key 都一起过期了,击穿的放大版,大批量的请求都打到数据库上了,一方面有可能直接缓存不可用了,就需要用集群化高可用的缓存服务,然后对于实际使用中也可以使用本地缓存结合 redis 缓存,去提高可用性,再配合一些限流措施,然后就是缓存使用过程总的过期时间最好能加一些随机值,防止在同一时间过期而导致雪崩,结合互斥锁防止大量请求打到数据库。
-]]>
-
- Redis
- 应用
- 缓存
- 缓存
- 穿透
- 击穿
- 雪崩
-
-
- Redis
- 缓存穿透
- 缓存击穿
- 缓存雪崩
- 布隆过滤器
- bloom filter
- 互斥锁
-
-
-
- 聊聊Java中的单例模式
- /2019/12/21/%E8%81%8A%E8%81%8AJava%E4%B8%AD%E7%9A%84%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/
- 这是个 Java 面试的高频问题,我也遇到过,以往都是觉得这类题没意思,网上一搜一大堆,也不愿意记,其实说回来,主要还是没静下心来好好去理解,今天无意中看到一个课程,基本帮我把一些疑惑的点讲清楚了,首先单例是啥意思,这个其实是有范围一说,比如我起了个Spring Boot应用,在这个应用范围内,我的常规 bean 是单例的,意味着 getBean 的时候其实永远只会拿到那一个对象,那要怎么来写一个单例呢,首先就是传说中的饿汉模式,也是最简单的
-饿汉模式 public class Singleton1 {
- // 首先,将构造方法变成私有的
- private Singleton1() {};
- // 创建私有静态实例,这样第一次使用的时候就会进行创建
- private static Singleton instance = new Singleton1();
-
- // 使用这个对象都是通过这个 getInstance 来获取
- public static Singleton1 getInstance() {
- return instance;
- }
- // 瞎写一个静态方法。这里想说的是,如果我们只是要调用 Singleton.getDate(...),
- // 本来是不想要生成 Singleton 实例的,不过没办法,已经生成了
- public static Date getDate(String mode) {return new Date();}
-}
-上面借鉴了一些代码,其实这是最基本,也不会错的方法,但是正如其中getDate方法里说的问题,有时候并没有想那这个对象,但是因为我调用了这个类的静态方法,导致对象已经生成了,可能这也是饿汉模式名字的来由,不管三七二十一给你生成个单例就完事了,不管有没有用,但是这种个人觉得也没啥大问题,如果是面试的话最好说出来它的缺点
-饱汉模式 public class Singleton2 {
- // 首先,也是先堵死 new Singleton() 这条路,将构造方法变成私有
- private Singleton2() {}
- // 和饿汉模式相比,这边不需要先实例化出来,注意这里的 volatile,它是必须的
- private static volatile Singleton2 instance = null;
-
- private int m = 9;
-
- public static Singleton getInstance() {
- if (instance == null) {
- // 加锁
- synchronized (Singleton2.class) {
- // 这一次判断也是必须的,不然会有并发问题
- if (instance == null) {
- instance = new Singleton2();
- }
- }
- }
- return instance;
- }
-}
-这里容易错的有三点,理解了其实就比较好记了
-第一点,为啥不在 getInstance 上整个代码块加 synchronized,这个其实比较容易理解,就是锁的力度太大,性能太差了,这点其实也要去理解,可以举个夸张的例子,比如我一个电商的服务,如果为了避免一个人的订单出现问题,是不是可以从请求入口就把他锁住,到请求结束释放,那么里面做的事情都有保障,然而这显然不可能,因为我们想要这种竞态条件抢占资源的时间尽量减少,防止其他线程等待。 第二点,为啥synchronized之已经检查了 instance == null,还要在里面再检查一次,这个有个术语,叫 double check lock,但是为啥要这么做呢,其实很简单,想象当有两个线程,都过了第一步为空判断,这个时候只有一个线程能拿到这个锁,另一个线程就等待了,如果不再判断一次,那么第一个线程新建完对象释放锁之后,第二个线程又能拿到锁,再去创建一个对象。 第三点,为啥要volatile关键字,原先对它的理解是它修饰的变量在 JMM 中能及时将变量值写到主存中,但是它还有个很重要的作用,就是防止指令重排序,instance = new Singleton();这行代码其实在底层是分成三条指令执行的,第一条是在堆上申请了一块内存放这个对象,但是对象的字段啥的都还是默认值,第二条是设置对象的值,比如上面的 m 是 9,然后第三条是将这个对象和虚拟机栈上的指针建立引用关联,那么如果我不用volatile关键字,这三条指令就有可能出现重排,比如变成了 1-3-2 这种顺序,当执行完第二步时,有个线程来访问这个对象了,先判断是不是空,发现不是空的,就拿去直接用了,是不是就出现问题了,所以这个volatile也是不可缺少的
-嵌套类 public class Singleton3 {
-
- private Singleton3() {}
- // 主要是使用了 嵌套类可以访问外部类的静态属性和静态方法 的特性
- private static class Holder {
- private static Singleton3 instance = new Singleton3();
- }
- public static Singleton3 getInstance() {
- return Holder.instance;
- }
-}
-这个我个人感觉是饿汉模式的升级版,可以在调用getInstance的时候去实例化对象,也是比较推荐的
-枚举单例 public enum Singleton {
- INSTANCE;
-
- public void doSomething(){
- //todo doSomething
- }
-}
-枚举很特殊,它在类加载的时候会初始化里面的所有的实例,而且 JVM 保证了它们不会再被实例化,所以它天生就是单例的。
-]]>
-
- Java
- Design Patterns
- Singleton
-
-
- 设计模式
- Design Patterns
- 单例
- Singleton
-
-
-
- 聊聊 SpringBoot 自动装配
- /2021/07/11/%E8%81%8A%E8%81%8ASpringBoot-%E8%87%AA%E5%8A%A8%E8%A3%85%E9%85%8D/
- springboot 自动装配调用链
-springboot 相比 spring能更方便开发人员上手,比较重要的一点就是自动装配,大致来看下这个逻辑
-public static void main ( String [ ] args) {
- SpringApplication . run ( SpbDemoApplication . class , args) ;
- }
-
-
-然后就是上面调用的 run 方法
-public static ConfigurableApplicationContext run ( Class < ? > primarySource, String . . . args) {
- return run ( new Class < ? > [ ] { primarySource } , args) ;
-}
-
-
-继续往下看
-public static ConfigurableApplicationContext run ( Class < ? > [ ] primarySources, String [ ] args) {
- return new SpringApplication ( primarySources) . run ( args) ;
-}
-
-调用SpringApplication的构造方法
-
-
-public SpringApplication ( Class < ? > . . . primarySources) {
- this ( null , primarySources) ;
-}
-
-
-@SuppressWarnings ( { "unchecked" , "rawtypes" } )
-public SpringApplication ( ResourceLoader resourceLoader, Class < ? > . . . primarySources) {
- this . resourceLoader = resourceLoader;
- Assert . notNull ( primarySources, "PrimarySources must not be null" ) ;
- this . primarySources = new LinkedHashSet < > ( Arrays . asList ( primarySources) ) ;
- this . webApplicationType = WebApplicationType . deduceFromClasspath ( ) ;
-
- this . bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories ( ) ;
- setInitializers ( ( Collection ) getSpringFactoriesInstances ( ApplicationContextInitializer . class ) ) ;
- setListeners ( ( Collection ) getSpringFactoriesInstances ( ApplicationListener . class ) ) ;
- this . mainApplicationClass = deduceMainApplicationClass ( ) ;
-}
-这里就是重点了
-private List < BootstrapRegistryInitializer > getBootstrapRegistryInitializersFromSpringFactories ( ) {
- ArrayList < BootstrapRegistryInitializer > initializers = new ArrayList < > ( ) ;
- getSpringFactoriesInstances ( Bootstrapper . class ) . stream ( )
- . map ( ( bootstrapper) -> ( ( BootstrapRegistryInitializer ) bootstrapper:: initialize ) )
- . forEach ( initializers:: add ) ;
- initializers. addAll ( getSpringFactoriesInstances ( BootstrapRegistryInitializer . class ) ) ;
- return initializers;
-}
-private < T > Collection < T > getSpringFactoriesInstances ( Class < T > type) {
- return getSpringFactoriesInstances ( type, new Class < ? > [ ] { } ) ;
-}
-
-private < T > Collection < T > getSpringFactoriesInstances ( Class < T > type, Class < ? > [ ] parameterTypes, Object . . . args) {
- ClassLoader classLoader = getClassLoader ( ) ;
-
-
- Set < String > names = new LinkedHashSet < > ( SpringFactoriesLoader . loadFactoryNames ( type, classLoader) ) ;
- List < T > instances = createSpringFactoriesInstances ( type, parameterTypes, classLoader, args, names) ;
- AnnotationAwareOrderComparator . sort ( instances) ;
- return instances;
-}
-
-
-
-public static List < String > loadFactoryNames ( Class < ? > factoryType, @Nullable ClassLoader classLoader) {
- ClassLoader classLoaderToUse = classLoader;
- if ( classLoaderToUse == null ) {
- classLoaderToUse = SpringFactoriesLoader . class . getClassLoader ( ) ;
- }
- String factoryTypeName = factoryType. getName ( ) ;
- return loadSpringFactories ( classLoaderToUse) . getOrDefault ( factoryTypeName, Collections . emptyList ( ) ) ;
-}
- private static Map < String , List < String > > loadSpringFactories ( ClassLoader classLoader) {
- Map < String , List < String > > result = cache. get ( classLoader) ;
- if ( result != null ) {
- return result;
- }
-
- result = new HashMap < > ( ) ;
- try {
-
- Enumeration < URL> urls = classLoader. getResources ( FACTORIES_RESOURCE_LOCATION) ;
- while ( urls. hasMoreElements ( ) ) {
- URL url = urls. nextElement ( ) ;
- UrlResource resource = new UrlResource ( url) ;
- Properties properties = PropertiesLoaderUtils . loadProperties ( resource) ;
- for ( Map. Entry < ? , ? > entry : properties. entrySet ( ) ) {
- String factoryTypeName = ( ( String ) entry. getKey ( ) ) . trim ( ) ;
- String [ ] factoryImplementationNames =
- StringUtils . commaDelimitedListToStringArray ( ( String ) entry. getValue ( ) ) ;
- for ( String factoryImplementationName : factoryImplementationNames) {
- result. computeIfAbsent ( factoryTypeName, key -> new ArrayList < > ( ) )
- . add ( factoryImplementationName. trim ( ) ) ;
- }
- }
- }
-
-
- result. replaceAll ( ( factoryType, implementations) -> implementations. stream ( ) . distinct ( )
- . collect ( Collectors . collectingAndThen ( Collectors . toList ( ) , Collections :: unmodifiableList ) ) ) ;
- cache. put ( classLoader, result) ;
- }
- catch ( IOException ex) {
- throw new IllegalArgumentException ( "Unable to load factories from location [" +
- FACTORIES_RESOURCE_LOCATION + "]" , ex) ;
- }
- return result;
-}
-
-我们可以看下 spring-boot-autoconfigure 的 META-INF/spring.factories
-# Initializers
-org. springframework. context. ApplicationContextInitializer= \
-org. springframework. boot. autoconfigure. SharedMetadataReaderFactoryContextInitializer, \
-org. springframework. boot. autoconfigure. logging. ConditionEvaluationReportLoggingListener
-
-# Application Listeners
-org. springframework. context. ApplicationListener= \
-org. springframework. boot. autoconfigure. BackgroundPreinitializer
-
-# Environment Post Processors
-org. springframework. boot. env. EnvironmentPostProcessor= \
-org. springframework. boot. autoconfigure. integration. IntegrationPropertiesEnvironmentPostProcessor
-
-# Auto Configuration Import Listeners
-org. springframework. boot. autoconfigure. AutoConfigurationImportListener= \
-org. springframework. boot. autoconfigure. condition. ConditionEvaluationReportAutoConfigurationImportListener
-
-# Auto Configuration Import Filters
-org. springframework. boot. autoconfigure. AutoConfigurationImportFilter= \
-org. springframework. boot. autoconfigure. condition. OnBeanCondition, \
-org. springframework. boot. autoconfigure. condition. OnClassCondition, \
-org. springframework. boot. autoconfigure. condition. OnWebApplicationCondition
-
-# 注意这里,其实就是类似于 dubbo spi 的通过 org. springframework. boot. autoconfigure. EnableAutoConfiguration 作为 key
-# 获取下面所有的 AutoConfiguration 配置类
-# Auto Configure
-org. springframework. boot. autoconfigure. EnableAutoConfiguration= \
-org. springframework. boot. autoconfigure. admin. SpringApplicationAdminJmxAutoConfiguration, \
-org. springframework. boot. autoconfigure. aop. AopAutoConfiguration, \
-org. springframework. boot. autoconfigure. amqp. RabbitAutoConfiguration, \
-org. springframework. boot. autoconfigure. batch. BatchAutoConfiguration, \
-org. springframework. boot. autoconfigure. cache. CacheAutoConfiguration, \
-org. springframework. boot. autoconfigure. cassandra. CassandraAutoConfiguration, \
-org. springframework. boot. autoconfigure. context. ConfigurationPropertiesAutoConfiguration, \
-org. springframework. boot. autoconfigure. context. LifecycleAutoConfiguration, \
-org. springframework. boot. autoconfigure. context. MessageSourceAutoConfiguration, \
-org. springframework. boot. autoconfigure. context. PropertyPlaceholderAutoConfiguration, \
-org. springframework. boot. autoconfigure. couchbase. CouchbaseAutoConfiguration, \
-org. springframework. boot. autoconfigure. dao. PersistenceExceptionTranslationAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. cassandra. CassandraDataAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. cassandra. CassandraReactiveDataAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. cassandra. CassandraReactiveRepositoriesAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. cassandra. CassandraRepositoriesAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. couchbase. CouchbaseDataAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. couchbase. CouchbaseReactiveDataAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. couchbase. CouchbaseReactiveRepositoriesAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. couchbase. CouchbaseRepositoriesAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. elasticsearch. ElasticsearchDataAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. elasticsearch. ElasticsearchRepositoriesAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. elasticsearch. ReactiveElasticsearchRepositoriesAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. elasticsearch. ReactiveElasticsearchRestClientAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. jdbc. JdbcRepositoriesAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. jpa. JpaRepositoriesAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. ldap. LdapRepositoriesAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. mongo. MongoDataAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. mongo. MongoReactiveDataAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. mongo. MongoReactiveRepositoriesAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. mongo. MongoRepositoriesAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. neo4j. Neo4jDataAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. neo4j. Neo4jReactiveDataAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. neo4j. Neo4jReactiveRepositoriesAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. neo4j. Neo4jRepositoriesAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. r2dbc. R2dbcDataAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. r2dbc. R2dbcRepositoriesAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. redis. RedisAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. redis. RedisReactiveAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. redis. RedisRepositoriesAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. rest. RepositoryRestMvcAutoConfiguration, \
-org. springframework. boot. autoconfigure. data. web. SpringDataWebAutoConfiguration, \
-org. springframework. boot. autoconfigure. elasticsearch. ElasticsearchRestClientAutoConfiguration, \
-org. springframework. boot. autoconfigure. flyway. FlywayAutoConfiguration, \
-org. springframework. boot. autoconfigure. freemarker. FreeMarkerAutoConfiguration, \
-org. springframework. boot. autoconfigure. groovy. template. GroovyTemplateAutoConfiguration, \
-org. springframework. boot. autoconfigure. gson. GsonAutoConfiguration, \
-org. springframework. boot. autoconfigure. h2. H2ConsoleAutoConfiguration, \
-org. springframework. boot. autoconfigure. hateoas. HypermediaAutoConfiguration, \
-org. springframework. boot. autoconfigure. hazelcast. HazelcastAutoConfiguration, \
-org. springframework. boot. autoconfigure. hazelcast. HazelcastJpaDependencyAutoConfiguration, \
-org. springframework. boot. autoconfigure. http. HttpMessageConvertersAutoConfiguration, \
-org. springframework. boot. autoconfigure. http. codec. CodecsAutoConfiguration, \
-org. springframework. boot. autoconfigure. influx. InfluxDbAutoConfiguration, \
-org. springframework. boot. autoconfigure. info. ProjectInfoAutoConfiguration, \
-org. springframework. boot. autoconfigure. integration. IntegrationAutoConfiguration, \
-org. springframework. boot. autoconfigure. jackson. JacksonAutoConfiguration, \
-org. springframework. boot. autoconfigure. jdbc. DataSourceAutoConfiguration, \
-org. springframework. boot. autoconfigure. jdbc. JdbcTemplateAutoConfiguration, \
-org. springframework. boot. autoconfigure. jdbc. JndiDataSourceAutoConfiguration, \
-org. springframework. boot. autoconfigure. jdbc. XADataSourceAutoConfiguration, \
-org. springframework. boot. autoconfigure. jdbc. DataSourceTransactionManagerAutoConfiguration, \
-org. springframework. boot. autoconfigure. jms. JmsAutoConfiguration, \
-org. springframework. boot. autoconfigure. jmx. JmxAutoConfiguration, \
-org. springframework. boot. autoconfigure. jms. JndiConnectionFactoryAutoConfiguration, \
-org. springframework. boot. autoconfigure. jms. activemq. ActiveMQAutoConfiguration, \
-org. springframework. boot. autoconfigure. jms. artemis. ArtemisAutoConfiguration, \
-org. springframework. boot. autoconfigure. jersey. JerseyAutoConfiguration, \
-org. springframework. boot. autoconfigure. jooq. JooqAutoConfiguration, \
-org. springframework. boot. autoconfigure. jsonb. JsonbAutoConfiguration, \
-org. springframework. boot. autoconfigure. kafka. KafkaAutoConfiguration, \
-org. springframework. boot. autoconfigure. availability. ApplicationAvailabilityAutoConfiguration, \
-org. springframework. boot. autoconfigure. ldap. embedded. EmbeddedLdapAutoConfiguration, \
-org. springframework. boot. autoconfigure. ldap. LdapAutoConfiguration, \
-org. springframework. boot. autoconfigure. liquibase. LiquibaseAutoConfiguration, \
-org. springframework. boot. autoconfigure. mail. MailSenderAutoConfiguration, \
-org. springframework. boot. autoconfigure. mail. MailSenderValidatorAutoConfiguration, \
-org. springframework. boot. autoconfigure. mongo. embedded. EmbeddedMongoAutoConfiguration, \
-org. springframework. boot. autoconfigure. mongo. MongoAutoConfiguration, \
-org. springframework. boot. autoconfigure. mongo. MongoReactiveAutoConfiguration, \
-org. springframework. boot. autoconfigure. mustache. MustacheAutoConfiguration, \
-org. springframework. boot. autoconfigure. neo4j. Neo4jAutoConfiguration, \
-org. springframework. boot. autoconfigure. netty. NettyAutoConfiguration, \
-org. springframework. boot. autoconfigure. orm. jpa. HibernateJpaAutoConfiguration, \
-org. springframework. boot. autoconfigure. quartz. QuartzAutoConfiguration, \
-org. springframework. boot. autoconfigure. r2dbc. R2dbcAutoConfiguration, \
-org. springframework. boot. autoconfigure. r2dbc. R2dbcTransactionManagerAutoConfiguration, \
-org. springframework. boot. autoconfigure. rsocket. RSocketMessagingAutoConfiguration, \
-org. springframework. boot. autoconfigure. rsocket. RSocketRequesterAutoConfiguration, \
-org. springframework. boot. autoconfigure. rsocket. RSocketServerAutoConfiguration, \
-org. springframework. boot. autoconfigure. rsocket. RSocketStrategiesAutoConfiguration, \
-org. springframework. boot. autoconfigure. security. servlet. SecurityAutoConfiguration, \
-org. springframework. boot. autoconfigure. security. servlet. UserDetailsServiceAutoConfiguration, \
-org. springframework. boot. autoconfigure. security. servlet. SecurityFilterAutoConfiguration, \
-org. springframework. boot. autoconfigure. security. reactive. ReactiveSecurityAutoConfiguration, \
-org. springframework. boot. autoconfigure. security. reactive. ReactiveUserDetailsServiceAutoConfiguration, \
-org. springframework. boot. autoconfigure. security. rsocket. RSocketSecurityAutoConfiguration, \
-org. springframework. boot. autoconfigure. security. saml2. Saml2RelyingPartyAutoConfiguration, \
-org. springframework. boot. autoconfigure. sendgrid. SendGridAutoConfiguration, \
-org. springframework. boot. autoconfigure. session. SessionAutoConfiguration, \
-org. springframework. boot. autoconfigure. security. oauth2. client. servlet. OAuth2ClientAutoConfiguration, \
-org. springframework. boot. autoconfigure. security. oauth2. client. reactive. ReactiveOAuth2ClientAutoConfiguration, \
-org. springframework. boot. autoconfigure. security. oauth2. resource. servlet. OAuth2ResourceServerAutoConfiguration, \
-org. springframework. boot. autoconfigure. security. oauth2. resource. reactive. ReactiveOAuth2ResourceServerAutoConfiguration, \
-org. springframework. boot. autoconfigure. solr. SolrAutoConfiguration, \
-org. springframework. boot. autoconfigure. sql. init. SqlInitializationAutoConfiguration, \
-org. springframework. boot. autoconfigure. task. TaskExecutionAutoConfiguration, \
-org. springframework. boot. autoconfigure. task. TaskSchedulingAutoConfiguration, \
-org. springframework. boot. autoconfigure. thymeleaf. ThymeleafAutoConfiguration, \
-org. springframework. boot. autoconfigure. transaction. TransactionAutoConfiguration, \
-org. springframework. boot. autoconfigure. transaction. jta. JtaAutoConfiguration, \
-org. springframework. boot. autoconfigure. validation. ValidationAutoConfiguration, \
-org. springframework. boot. autoconfigure. web. client. RestTemplateAutoConfiguration, \
-org. springframework. boot. autoconfigure. web. embedded. EmbeddedWebServerFactoryCustomizerAutoConfiguration, \
-org. springframework. boot. autoconfigure. web. reactive. HttpHandlerAutoConfiguration, \
-org. springframework. boot. autoconfigure. web. reactive. ReactiveWebServerFactoryAutoConfiguration, \
-org. springframework. boot. autoconfigure. web. reactive. WebFluxAutoConfiguration, \
-org. springframework. boot. autoconfigure. web. reactive. error. ErrorWebFluxAutoConfiguration, \
-org. springframework. boot. autoconfigure. web. reactive. function. client. ClientHttpConnectorAutoConfiguration, \
-org. springframework. boot. autoconfigure. web. reactive. function. client. WebClientAutoConfiguration, \
-org. springframework. boot. autoconfigure. web. servlet. DispatcherServletAutoConfiguration, \
-org. springframework. boot. autoconfigure. web. servlet. ServletWebServerFactoryAutoConfiguration, \
-org. springframework. boot. autoconfigure. web. servlet. error. ErrorMvcAutoConfiguration, \
-org. springframework. boot. autoconfigure. web. servlet. HttpEncodingAutoConfiguration, \
-org. springframework. boot. autoconfigure. web. servlet. MultipartAutoConfiguration, \
-org. springframework. boot. autoconfigure. web. servlet. WebMvcAutoConfiguration, \
-org. springframework. boot. autoconfigure. websocket. reactive. WebSocketReactiveAutoConfiguration, \
-org. springframework. boot. autoconfigure. websocket. servlet. WebSocketServletAutoConfiguration, \
-org. springframework. boot. autoconfigure. websocket. servlet. WebSocketMessagingAutoConfiguration, \
-org. springframework. boot. autoconfigure. webservices. WebServicesAutoConfiguration, \
-org. springframework. boot. autoconfigure. webservices. client. WebServiceTemplateAutoConfiguration
+ mysql
+ 索引
+ is null
+ is not null
+ procedure
+
+
+
+ 聊聊 redis 缓存的应用问题
+ /2021/01/31/%E8%81%8A%E8%81%8A-redis-%E7%BC%93%E5%AD%98%E7%9A%84%E5%BA%94%E7%94%A8%E9%97%AE%E9%A2%98/
+ 前面写过一系列的 redis 源码分析的,但是实际上很多的问题还是需要结合实际的使用,然后其实就避不开缓存使用的三个著名问题,穿透,击穿和雪崩,这三个概念也是有着千丝万缕的关系,
+缓存穿透 缓存穿透是指当数据库中本身就不存在这个数据的时候,使用一般的缓存策略时访问不到缓存后就访问数据库,但是因为数据库也没数据,所以如果不做任何策略优化的话,这类数据就每次都会访问一次数据库,对数据库压力也会比较大。
+缓存击穿 缓存击穿跟穿透比较类似的,都是访问缓存不在,然后去访问数据库,与穿透不一样的是击穿是在数据库中存在数据,但是可能由于第一次访问,或者缓存过期了,需要访问到数据库,这对于访问量小的情况其实算是个正常情况,但是随着请求量变高就会引发一些性能隐患。
+缓存雪崩 缓存雪崩就是击穿的大规模集群效应,当大量的缓存过期失效的时候,这些请求都是直接访问到数据库了,会对数据库造成很大的压力。
+对于以上三种场景也有一些比较常见的解决方案,但也不能说是万无一失的,需要随着业务去寻找合适的方案
+解决缓存穿透 对于数据库中就没这个数据的时候,一种是可以对这个 key 设置下空值,即以一个特定的表示是数据库不存在的,这种情况需要合理地调整过期时间,当这个 key 在数据库中有数据了的话,也需要有策略去更新这个值,并且如果这类 key 非常多,这个方法就会不太合适,就可以使用第二种方法,就是布隆过滤器,bloom filter,前置一个布隆过滤器,当这个 key 在数据库不存在的话,先用布隆过滤器挡一道,如果不在的话就直接返回了,当然布隆过滤器不是绝对的准确的
+解决缓存击穿 当一个 key 的缓存过期了,如果大量请求过来访问这个 key,请求都会落在数据库里,这个时候就可以使用一些类似于互斥锁的方式去让一个线程去访问数据库,更新缓存,但是这里其实也有个问题,就是如果是热点 key 其实这种方式也比较危险,万一更新失败,或者更新操作的时候耗时比较久,就会有一大堆请求卡在那,这种情况可能需要有一些异步提前刷新缓存,可以结合具体场景选择方式
+解决缓存雪崩 雪崩的情况是指大批量的 key 都一起过期了,击穿的放大版,大批量的请求都打到数据库上了,一方面有可能直接缓存不可用了,就需要用集群化高可用的缓存服务,然后对于实际使用中也可以使用本地缓存结合 redis 缓存,去提高可用性,再配合一些限流措施,然后就是缓存使用过程总的过期时间最好能加一些随机值,防止在同一时间过期而导致雪崩,结合互斥锁防止大量请求打到数据库。
+]]>
+
+ Redis
+ 应用
+ 缓存
+ 缓存
+ 穿透
+ 击穿
+ 雪崩
+
+
+ Redis
+ 缓存穿透
+ 缓存击穿
+ 缓存雪崩
+ 布隆过滤器
+ bloom filter
+ 互斥锁
+
+
+
+ 聊聊Java中的单例模式
+ /2019/12/21/%E8%81%8A%E8%81%8AJava%E4%B8%AD%E7%9A%84%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/
+ 这是个 Java 面试的高频问题,我也遇到过,以往都是觉得这类题没意思,网上一搜一大堆,也不愿意记,其实说回来,主要还是没静下心来好好去理解,今天无意中看到一个课程,基本帮我把一些疑惑的点讲清楚了,首先单例是啥意思,这个其实是有范围一说,比如我起了个Spring Boot应用,在这个应用范围内,我的常规 bean 是单例的,意味着 getBean 的时候其实永远只会拿到那一个对象,那要怎么来写一个单例呢,首先就是传说中的饿汉模式,也是最简单的
+饿汉模式 public class Singleton1 {
+ // 首先,将构造方法变成私有的
+ private Singleton1() {};
+ // 创建私有静态实例,这样第一次使用的时候就会进行创建
+ private static Singleton instance = new Singleton1();
-# Failure analyzers
-org. springframework. boot. diagnostics. FailureAnalyzer= \
-org. springframework. boot. autoconfigure. data. redis. RedisUrlSyntaxFailureAnalyzer, \
-org. springframework. boot. autoconfigure. diagnostics. analyzer. NoSuchBeanDefinitionFailureAnalyzer, \
-org. springframework. boot. autoconfigure. flyway. FlywayMigrationScriptMissingFailureAnalyzer, \
-org. springframework. boot. autoconfigure. jdbc. DataSourceBeanCreationFailureAnalyzer, \
-org. springframework. boot. autoconfigure. jdbc. HikariDriverConfigurationFailureAnalyzer, \
-org. springframework. boot. autoconfigure. r2dbc. ConnectionFactoryBeanCreationFailureAnalyzer, \
-org. springframework. boot. autoconfigure. session. NonUniqueSessionRepositoryFailureAnalyzer
+ // 使用这个对象都是通过这个 getInstance 来获取
+ public static Singleton1 getInstance() {
+ return instance;
+ }
+ // 瞎写一个静态方法。这里想说的是,如果我们只是要调用 Singleton.getDate(...),
+ // 本来是不想要生成 Singleton 实例的,不过没办法,已经生成了
+ public static Date getDate(String mode) {return new Date();}
+}
+上面借鉴了一些代码,其实这是最基本,也不会错的方法,但是正如其中getDate方法里说的问题,有时候并没有想那这个对象,但是因为我调用了这个类的静态方法,导致对象已经生成了,可能这也是饿汉模式名字的来由,不管三七二十一给你生成个单例就完事了,不管有没有用,但是这种个人觉得也没啥大问题,如果是面试的话最好说出来它的缺点
+饱汉模式 public class Singleton2 {
+ // 首先,也是先堵死 new Singleton() 这条路,将构造方法变成私有
+ private Singleton2() {}
+ // 和饿汉模式相比,这边不需要先实例化出来,注意这里的 volatile,它是必须的
+ private static volatile Singleton2 instance = null;
-# Template availability providers
-org. springframework. boot. autoconfigure. template. TemplateAvailabilityProvider= \
-org. springframework. boot. autoconfigure. freemarker. FreeMarkerTemplateAvailabilityProvider, \
-org. springframework. boot. autoconfigure. mustache. MustacheTemplateAvailabilityProvider, \
-org. springframework. boot. autoconfigure. groovy. template. GroovyTemplateAvailabilityProvider, \
-org. springframework. boot. autoconfigure. thymeleaf. ThymeleafTemplateAvailabilityProvider, \
-org. springframework. boot. autoconfigure. web. servlet. JspTemplateAvailabilityProvider
+ private int m = 9;
-# DataSource initializer detectors
-org. springframework. boot. sql. init. dependency. DatabaseInitializerDetector= \
-org. springframework. boot. autoconfigure. flyway. FlywayMigrationInitializerDatabaseInitializerDetector
-
+ public static Singleton getInstance() {
+ if (instance == null) {
+ // 加锁
+ synchronized (Singleton2.class) {
+ // 这一次判断也是必须的,不然会有并发问题
+ if (instance == null) {
+ instance = new Singleton2();
+ }
+ }
+ }
+ return instance;
+ }
+}
+这里容易错的有三点,理解了其实就比较好记了
+第一点,为啥不在 getInstance 上整个代码块加 synchronized,这个其实比较容易理解,就是锁的力度太大,性能太差了,这点其实也要去理解,可以举个夸张的例子,比如我一个电商的服务,如果为了避免一个人的订单出现问题,是不是可以从请求入口就把他锁住,到请求结束释放,那么里面做的事情都有保障,然而这显然不可能,因为我们想要这种竞态条件抢占资源的时间尽量减少,防止其他线程等待。 第二点,为啥synchronized之已经检查了 instance == null,还要在里面再检查一次,这个有个术语,叫 double check lock,但是为啥要这么做呢,其实很简单,想象当有两个线程,都过了第一步为空判断,这个时候只有一个线程能拿到这个锁,另一个线程就等待了,如果不再判断一次,那么第一个线程新建完对象释放锁之后,第二个线程又能拿到锁,再去创建一个对象。 第三点,为啥要volatile关键字,原先对它的理解是它修饰的变量在 JMM 中能及时将变量值写到主存中,但是它还有个很重要的作用,就是防止指令重排序,instance = new Singleton();这行代码其实在底层是分成三条指令执行的,第一条是在堆上申请了一块内存放这个对象,但是对象的字段啥的都还是默认值,第二条是设置对象的值,比如上面的 m 是 9,然后第三条是将这个对象和虚拟机栈上的指针建立引用关联,那么如果我不用volatile关键字,这三条指令就有可能出现重排,比如变成了 1-3-2 这种顺序,当执行完第二步时,有个线程来访问这个对象了,先判断是不是空,发现不是空的,就拿去直接用了,是不是就出现问题了,所以这个volatile也是不可缺少的
+嵌套类 public class Singleton3 {
-上面根据 org.springframework.boot.autoconfigure.EnableAutoConfiguration 获取的各个配置类,在通过反射加载就能得到一堆 JavaConfig配置类,然后再根据 ConditionalOnProperty等条件配置加载具体的 bean,大致就是这么个逻辑
+ private Singleton3() {}
+ // 主要是使用了 嵌套类可以访问外部类的静态属性和静态方法 的特性
+ private static class Holder {
+ private static Singleton3 instance = new Singleton3();
+ }
+ public static Singleton3 getInstance() {
+ return Holder.instance;
+ }
+}
+这个我个人感觉是饿汉模式的升级版,可以在调用getInstance的时候去实例化对象,也是比较推荐的
+枚举单例 public enum Singleton {
+ INSTANCE;
+
+ public void doSomething(){
+ //todo doSomething
+ }
+}
+枚举很特殊,它在类加载的时候会初始化里面的所有的实例,而且 JVM 保证了它们不会再被实例化,所以它天生就是单例的。
]]>
Java
- SpringBoot
+ Design Patterns
+ Singleton
- Java
- Spring
- SpringBoot
- 自动装配
- AutoConfiguration
+ 设计模式
+ Design Patterns
+ 单例
+ Singleton
@@ -14596,17 +14383,32 @@ $
- 聊聊最近平淡的生活之《花束般的恋爱》观后感
- /2021/12/31/%E8%81%8A%E8%81%8A%E6%9C%80%E8%BF%91%E5%B9%B3%E6%B7%A1%E7%9A%84%E7%94%9F%E6%B4%BB%E4%B9%8B%E3%80%8A%E8%8A%B1%E6%9D%9F%E8%88%AC%E7%9A%84%E6%81%8B%E7%88%B1%E3%80%8B%E8%A7%82%E5%90%8E%E6%84%9F/
- 周末在领导的提议下看了豆瓣的年度榜单,本来感觉没啥心情看的,看到主演有有村架纯就觉得可以看一下,颜值即正义嘛,男主小麦跟女主小娟(后面简称小麦跟小娟)是两个在一次非常偶然的没赶上地铁末班车事件中相识,这里得说下日本这种通宵营业的店好像挺不错的,看着也挺正常,国内估计只有酒吧之类的可以。晚上去的地方是有点暗暗的,好像也有点类似酒吧,旁边有类似于 dj 那种,然后同桌的还有除了男女主的另外一对男女,也是因为没赶上地铁末班车的,但也是陌生人,然后小麦突然看到了有个非常有名的电影人,小娟竟然也认识,然后旁边那对完全不认识,还在那吹自己看过很多电影,比如《肖申克的救赎》,于是男女主都特别鄙夷地看着他们,然后他们又去了另一个有点像泡澡的地方席地而坐,他们发现了自己的鞋子都是一样的,然后在女的去上厕所的时候,小麦暗恋的学姐也来了,然后小麦就去跟学姐他们一起坐了,小娟回来后有点不开心就说去朋友家睡,幸好小麦看出来了(他竟然看出来了,本来以为应该是没填过恋爱很木讷的),就追出去,然后就去了小麦家,到了家小娟发现小麦家的书柜上的书简直就跟她自己家的一模一样,小麦还给小娟吹了头发,一起吃烤饭团,看电影,第二天送小娟上了公交,还约好了一起看木乃伊展,然而并没有交换联系方式,但是他们还是约上了一起看了木乃伊展,在餐馆就出现了片头那一幕的来源,因为餐馆他们想一起听歌,就用有线耳机一人一个耳朵听,但是旁边就有个大叔说“你们是不是不爱音乐,左右耳朵是不一样的,只有一起听才是真正的音乐”这样的话,然后的剧情有点跳,因为是指他们一直在这家餐馆吃饭,中间有他们一起出去玩情节穿插着,也是在这他们确立了关系,可以说主体就是体现了他们非常的合拍和默契,就像一些影评说的,这部电影是说如何跟百分百合拍的人分手,然后就是正常的恋爱开始啪啪啪,一直腻在床上,也没去就业说明会,后面也有讲了一点小麦带着小娟去认识他的朋友,也把小娟介绍给了他们认识,这里算是个小伏笔,后面他们分手也有这里的人的一些关系,接下去的剧情说实话我是不太喜欢的,如果一部八分的电影只是说恋爱被现实打败的话,我觉得在我这是不合格的,但是事实也是这样,小麦其实是有家里的资助,所以后面还是按自己的喜好给一些机构画点插画,小娟则要出去工作,因为小娟家庭观念也是要让她出去有正经工作,用脚指头想也能知道肯定不顺利,然后就是暂时在一家蛋糕店工作,小麦就每天去接小娟,日子过得甜甜蜜蜜,后面小娟在自己的努力下考了个什么资格证,去了一家医院还是什么做前台行政,这中间当然就有父母来见面吃饭了,他们在开始恋爱不久就同居合租了,然后小娟父母就是来说要让她有个正经工作,对男的说的话就是人生就是责任这类的话,而小麦爸爸算是个导火索,因为小麦家里是做烟花生意的,他爸让他就做烟花生意,因为要回老家,并且小麦也不想做,所以就拒绝了,然后他爸就说不给他每个月五万的资助,这也导致了小麦需要去找工作,这个过程也是很辛苦,本来想要年前找好工作,然后事与愿违,后面有一次小娟被同事吐槽怎么从来不去团建,于是她就去了(我以为会拒绝),正在团建的时候小麦给她电话,说找到工作了,是一个创业物流公司这种,这里剧情就是我觉得比较俗套的,小麦各种被虐,累成狗,但是就像小娟爸爸说的话,人生就是责任,所以一直在坚持,但是这样也导致了跟小娟的交流也越来越少,他们原来最爱的漫画,爱玩的游戏,也只剩小娟一个人看,一个人玩,而正是这个时候,小娟说她辞掉了工作,去做一个不是太靠谱的漫画改造的密室逃脱,然后这里其实有一点后面争议很大的,就是这个工作其实是前面小麦介绍给小娟的那些朋友中一个的女朋友介绍的,而在有个剧情就是小娟有一次在这个密室逃脱的老板怀里醒过来,是在 KTV 那样的场景里,这就有很多人觉得小娟是不是出轨了,我觉得其实不那么重要,因为这个离职的事情已经让一切矛盾都摆在眼前,小麦其实是接受这种需要承担责任的生活,也想着要跟小娟结婚,但是小娟似乎还是想要过着那样理想的生活,做自己想做的事情,看自己爱看的漫画,也要小麦能像以前那样一直那么默契的有着相同的爱好,这里的触发点其实还有个是那个小麦的朋友(也就是他女朋友介绍小娟那个不靠谱工作的)的葬礼上,小麦在参加完葬礼后有挺多想倾诉的,而小娟只是想睡了,这个让小麦第二天起来都不想理小娟,只是这里我不太理解,难道这点闹情绪都不能接受吗,所谓的合拍也只是毫无限制的情况下的合拍吧,真正的生活怎么可能如此理想呢,即使没有物质生活的压力,也会有其他的各种压力和限制,在这之后其实小麦想说的是小娟是不是没有想跟自己继续在一起的想法了,而小娟觉得都不说话了,还怎么结婚呢,后面其实导演搞了个小 trick,突然放了异常婚礼,但是不是男女主的,我并不觉得这个桥段很好,在婚礼里男女主都觉得自己想要跟对方说分手了,但是当他们去了最开始一直去的餐馆的时候,一个算是一个现实映照的就是他们一直坐的位子被占了,可能也是导演想通过这个来说明他们已经回不去了,在餐馆交谈的时候,小麦其实是说他们结婚吧,并没有想前面婚礼上预设地要分手,但是小娟放弃了,不想结婚,因为不想过那样的生活了,而小麦觉得可能生活就是那样,不可能一直保持刚恋爱时候的那种感觉,生活就是责任,人生就意味着责任。
-我的一些观点也在前面说了,恋爱到婚姻,即使物质没问题,经济没问题,也会有各种各样的问题,需要一起去解决,因为结婚就意味着需要相互扶持,而不是各取所需,可能我的要求比较高,后面男女主在分手后还一起住了一段时间,我原来还在想会不会通过这个方式让他们继续去磨合同步,只是我失望了,最后给个打分可能是 5 到 6 分吧,勉强及格,好的影视剧应该源于生活高于生活,这一部可能还比不上生活。
+ 聊聊 Sharding-Jdbc 的简单原理初篇
+ /2021/12/26/%E8%81%8A%E8%81%8A-Sharding-Jdbc-%E7%9A%84%E7%AE%80%E5%8D%95%E5%8E%9F%E7%90%86%E5%88%9D%E7%AF%87/
+ 在上一篇 sharding-jdbc 的介绍中其实碰到过一个问题,这里也引出了一个比较有意思的话题 就是我在执行 query 的时候犯过一个比较难发现的错误,
+ResultSet resultSet = ps. executeQuery ( sql) ;
+实际上应该是
+ResultSet resultSet = ps. executeQuery ( ) ;
+而这里的差别就是,是否传 sql 这个参数,首先我们要知道这个 ps 是什么,它也是个接口java.sql.PreparedStatement,而真正的实现类是org.apache.shardingsphere.driver.jdbc.core.statement.ShardingSpherePreparedStatement,我们来看下继承关系 这里可以看到继承关系里有org.apache.shardingsphere.driver.jdbc.unsupported.AbstractUnsupportedOperationPreparedStatement 那么在我上面的写错的代码里
+@Override
+public final ResultSet executeQuery ( final String sql) throws SQLException {
+ throw new SQLFeatureNotSupportedException ( "executeQuery with SQL for PreparedStatement" ) ;
+}
+这个报错一开始让我有点懵,后来点进去了发现是这么个异常,但是我其实一开始是用的更新语句,以为更新不支持,因为平时使用没有深究过,以为是不是需要使用 Mybatis 才可以执行更新,但是理论上也不应该,再往上看原来这些异常是由 sharding-jdbc 包装的,也就是在上面说的AbstractUnsupportedOperationPreparedStatement,这其实也是一种设计思想,本身 jdbc 提供了一系列接口,由各家去支持,包括 mysql,sql server,oracle 等,而正因为这个设计,所以 sharding-jdbc 也可以在此基础上进行设计,我们可以总体地看下 sharding-jdbc 的实现基础 看了前面ShardingSpherePreparedStatement的继承关系,应该也能猜到这里的几个类都是实现了 jdbc 的基础接口, 在前一篇的 demo 中的
+Connection conn = dataSource. getConnection ( ) ;
+其实就获得了org.apache.shardingsphere.driver.jdbc.core.connection.ShardingSphereConnection#ShardingSphereConnection 然后获得java.sql.PreparedStatement
+PreparedStatement ps = conn. prepareStatement ( sql)
+就是获取了org.apache.shardingsphere.driver.jdbc.core.statement.ShardingSpherePreparedStatement 然后就是执行
+ResultSet resultSet = ps. executeQuery ( ) ;
+然后获得结果org.apache.shardingsphere.driver.jdbc.core.resultset.ShardingSphereResultSet
+其实像 mybatis 也是基于这样去实现的
]]>
- 生活
+ Java
- 生活
- 看剧
+ Java
+ Sharding-Jdbc
@@ -14640,22 +14442,17 @@ $
- 聊聊给亲戚朋友的老电脑重装系统那些事儿
- /2021/05/09/%E8%81%8A%E8%81%8A%E7%BB%99%E4%BA%B2%E6%88%9A%E6%9C%8B%E5%8F%8B%E7%9A%84%E8%80%81%E7%94%B5%E8%84%91%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E9%82%A3%E4%BA%9B%E4%BA%8B%E5%84%BF/
- 前面这个五一回去之前,LD 姐姐跟我说电脑很卡了,想让我重装系统,问了下 LD 可能是那个 09 年买的笔记本,想想有点害怕[捂脸],前年有一次好像让我帮忙装了她同事的一个三星的笔记本,本着一些系统洁癖,所以就从开始找纯净版的 win7 家庭版,因为之前那些本基本都自带 win7 的家庭版,而且把激活码就贴在机器下面,然后从三星官网去找官方驱动,还好这个机型的驱动还在,先做了系统镜像,其实感觉这种情况需要两个 U 盘,一个 U 盘装系统作为安装启动盘,一个放驱动,毕竟不是专业装系统的,然后因为官方驱动需要一个个下载一个个安装,然后驱动文件下载的地方还没标明是 32 位还是 64 位的,结果还被 LD 姐姐催着,一直问好没好,略尴尬,索性还是找个一键安装的
-这次甚至更夸张,上次还让带回去,我准备好了系统镜像啥的,第二天装,这次直接带了两个老旧笔记本过来说让当天就装好,感觉有点像被当修电脑的使,又说这些电脑其实都不用了的,都是为了她们当医生的要每年看会课,然后只能用电脑浏览器看,结果都在用 360 浏览器,真的是万恶的 360,其实以前对 360 没啥坏印象,毕竟以前也经常用,只是对于这些老电脑,360 全家桶真的就是装了就废了,2G 的内存,开机就开着 360 安全卫士,360 杀毒,有一个还装了腾讯电脑管家,然后腾讯视频跟爱奇艺也开机启动了,然后还打开 360 浏览器看课,就算再好的系统也吃不消这么用,重装了系统,还是这么装这些东西,也是分分钟变卡,可惜他们都没啥这类概念。
-对于他们要看的课,更搞笑的是,明明在页面上注明了说要使用 IE 浏览器,结果他们都在用 360 浏览器看,但是这个也不能完全怪他们,因为实在是现在的 IE 啥的也有开始不兼容 flash 的配置,需要开启兼容配置,但是只要开启了之后就可以直接用 IE 看,比 360 靠谱很多, 资源占用也比较少,360 估计是基于 chromium 加了很多内置的插件,本身 chromium 也是内存大户,但是说这些其实他们也不懂,总觉得找我免费装下系统能撑一段时间,反正对我来说也应该很简单(他们觉得),实际上开始工作以后,我自己想装个双系统都是上淘宝买别人的服务装的,台式机更是几年没动过系统了,因为要重装一大堆软件,数据备份啥的,还有驱动什么的,分区格式,那些驱动精灵啥的也都是越来越坑,一装就给你带一堆垃圾软件。
-感悟是,总觉得学计算机的就应该会装系统,会修电脑,之前亲戚还拿着一个完全开不起来的笔记本让我来修,这真的是,我说可以找官方维修的,结果我说我搞不定,她直接觉得是修不好了,直接电脑都懒得拿回去了,后面又一次反复解释了才明白,另外就是 360 全家桶,别说老电脑了,新机器都不太吃得消。
+ 聊聊最近平淡的生活之《花束般的恋爱》观后感
+ /2021/12/31/%E8%81%8A%E8%81%8A%E6%9C%80%E8%BF%91%E5%B9%B3%E6%B7%A1%E7%9A%84%E7%94%9F%E6%B4%BB%E4%B9%8B%E3%80%8A%E8%8A%B1%E6%9D%9F%E8%88%AC%E7%9A%84%E6%81%8B%E7%88%B1%E3%80%8B%E8%A7%82%E5%90%8E%E6%84%9F/
+ 周末在领导的提议下看了豆瓣的年度榜单,本来感觉没啥心情看的,看到主演有有村架纯就觉得可以看一下,颜值即正义嘛,男主小麦跟女主小娟(后面简称小麦跟小娟)是两个在一次非常偶然的没赶上地铁末班车事件中相识,这里得说下日本这种通宵营业的店好像挺不错的,看着也挺正常,国内估计只有酒吧之类的可以。晚上去的地方是有点暗暗的,好像也有点类似酒吧,旁边有类似于 dj 那种,然后同桌的还有除了男女主的另外一对男女,也是因为没赶上地铁末班车的,但也是陌生人,然后小麦突然看到了有个非常有名的电影人,小娟竟然也认识,然后旁边那对完全不认识,还在那吹自己看过很多电影,比如《肖申克的救赎》,于是男女主都特别鄙夷地看着他们,然后他们又去了另一个有点像泡澡的地方席地而坐,他们发现了自己的鞋子都是一样的,然后在女的去上厕所的时候,小麦暗恋的学姐也来了,然后小麦就去跟学姐他们一起坐了,小娟回来后有点不开心就说去朋友家睡,幸好小麦看出来了(他竟然看出来了,本来以为应该是没填过恋爱很木讷的),就追出去,然后就去了小麦家,到了家小娟发现小麦家的书柜上的书简直就跟她自己家的一模一样,小麦还给小娟吹了头发,一起吃烤饭团,看电影,第二天送小娟上了公交,还约好了一起看木乃伊展,然而并没有交换联系方式,但是他们还是约上了一起看了木乃伊展,在餐馆就出现了片头那一幕的来源,因为餐馆他们想一起听歌,就用有线耳机一人一个耳朵听,但是旁边就有个大叔说“你们是不是不爱音乐,左右耳朵是不一样的,只有一起听才是真正的音乐”这样的话,然后的剧情有点跳,因为是指他们一直在这家餐馆吃饭,中间有他们一起出去玩情节穿插着,也是在这他们确立了关系,可以说主体就是体现了他们非常的合拍和默契,就像一些影评说的,这部电影是说如何跟百分百合拍的人分手,然后就是正常的恋爱开始啪啪啪,一直腻在床上,也没去就业说明会,后面也有讲了一点小麦带着小娟去认识他的朋友,也把小娟介绍给了他们认识,这里算是个小伏笔,后面他们分手也有这里的人的一些关系,接下去的剧情说实话我是不太喜欢的,如果一部八分的电影只是说恋爱被现实打败的话,我觉得在我这是不合格的,但是事实也是这样,小麦其实是有家里的资助,所以后面还是按自己的喜好给一些机构画点插画,小娟则要出去工作,因为小娟家庭观念也是要让她出去有正经工作,用脚指头想也能知道肯定不顺利,然后就是暂时在一家蛋糕店工作,小麦就每天去接小娟,日子过得甜甜蜜蜜,后面小娟在自己的努力下考了个什么资格证,去了一家医院还是什么做前台行政,这中间当然就有父母来见面吃饭了,他们在开始恋爱不久就同居合租了,然后小娟父母就是来说要让她有个正经工作,对男的说的话就是人生就是责任这类的话,而小麦爸爸算是个导火索,因为小麦家里是做烟花生意的,他爸让他就做烟花生意,因为要回老家,并且小麦也不想做,所以就拒绝了,然后他爸就说不给他每个月五万的资助,这也导致了小麦需要去找工作,这个过程也是很辛苦,本来想要年前找好工作,然后事与愿违,后面有一次小娟被同事吐槽怎么从来不去团建,于是她就去了(我以为会拒绝),正在团建的时候小麦给她电话,说找到工作了,是一个创业物流公司这种,这里剧情就是我觉得比较俗套的,小麦各种被虐,累成狗,但是就像小娟爸爸说的话,人生就是责任,所以一直在坚持,但是这样也导致了跟小娟的交流也越来越少,他们原来最爱的漫画,爱玩的游戏,也只剩小娟一个人看,一个人玩,而正是这个时候,小娟说她辞掉了工作,去做一个不是太靠谱的漫画改造的密室逃脱,然后这里其实有一点后面争议很大的,就是这个工作其实是前面小麦介绍给小娟的那些朋友中一个的女朋友介绍的,而在有个剧情就是小娟有一次在这个密室逃脱的老板怀里醒过来,是在 KTV 那样的场景里,这就有很多人觉得小娟是不是出轨了,我觉得其实不那么重要,因为这个离职的事情已经让一切矛盾都摆在眼前,小麦其实是接受这种需要承担责任的生活,也想着要跟小娟结婚,但是小娟似乎还是想要过着那样理想的生活,做自己想做的事情,看自己爱看的漫画,也要小麦能像以前那样一直那么默契的有着相同的爱好,这里的触发点其实还有个是那个小麦的朋友(也就是他女朋友介绍小娟那个不靠谱工作的)的葬礼上,小麦在参加完葬礼后有挺多想倾诉的,而小娟只是想睡了,这个让小麦第二天起来都不想理小娟,只是这里我不太理解,难道这点闹情绪都不能接受吗,所谓的合拍也只是毫无限制的情况下的合拍吧,真正的生活怎么可能如此理想呢,即使没有物质生活的压力,也会有其他的各种压力和限制,在这之后其实小麦想说的是小娟是不是没有想跟自己继续在一起的想法了,而小娟觉得都不说话了,还怎么结婚呢,后面其实导演搞了个小 trick,突然放了异常婚礼,但是不是男女主的,我并不觉得这个桥段很好,在婚礼里男女主都觉得自己想要跟对方说分手了,但是当他们去了最开始一直去的餐馆的时候,一个算是一个现实映照的就是他们一直坐的位子被占了,可能也是导演想通过这个来说明他们已经回不去了,在餐馆交谈的时候,小麦其实是说他们结婚吧,并没有想前面婚礼上预设地要分手,但是小娟放弃了,不想结婚,因为不想过那样的生活了,而小麦觉得可能生活就是那样,不可能一直保持刚恋爱时候的那种感觉,生活就是责任,人生就意味着责任。
+我的一些观点也在前面说了,恋爱到婚姻,即使物质没问题,经济没问题,也会有各种各样的问题,需要一起去解决,因为结婚就意味着需要相互扶持,而不是各取所需,可能我的要求比较高,后面男女主在分手后还一起住了一段时间,我原来还在想会不会通过这个方式让他们继续去磨合同步,只是我失望了,最后给个打分可能是 5 到 6 分吧,勉强及格,好的影视剧应该源于生活高于生活,这一部可能还比不上生活。
]]>
生活
生活
- 装电脑
- 老电脑
- 360 全家桶
- 修电脑的
+ 看剧
@@ -14909,239 +14706,442 @@ server.6= 192.168<
- 聊聊 Java 的类加载机制一
- /2020/11/08/%E8%81%8A%E8%81%8A-Java-%E7%9A%84%E7%B1%BB%E5%8A%A0%E8%BD%BD%E6%9C%BA%E5%88%B6/
- 一说到这个主题,想到的应该是双亲委派模型,不过讲的包括但不限于这个,主要内容是参考深入理解 Java 虚拟机书中的介绍, 一个类型的生命周期包含了七个阶段,加载,验证,准备,解析,初始化,使用,卸载。
-
-
-通过一个类的全限定名来获取定义此类的二进制字节流
-将这个字节流代表的静态存储结构转化为方法区的运行时数据结构
-在内存中生成了一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据的访问入口
-
-
-
-文件格式验证
-元数据验证
-字节码验证
-符号引用验证
-
-
-以上验证 、准备 、解析 三个阶段又合称为链接阶段,链接阶段要做的是将加载到JVM中的二进制字节流的类数据信息合并到JVM的运行时状态中。
-
-初始化 类的初始化阶段是类加载过程的最后一个步骤,也是除了自定义类加载器之外将主动权交给了应用程序,其实就是执行类构造器()方法的过程,()并不是我们在 Java 代码中直接编写的方法,它是 Javac编译器的自动生成物,()方法是由编译器自动收集类中的所有类变量的复制动作和静态句块(static{}块)中的语句合并产生的,编译器收集的顺序是由语句在原文件中出现的顺序决定的,静态语句块中只能访问定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块可以复制,但是不能访问,同时还要保证父类的执行先于子类,然后保证多线程下的并发问题
-
-最终,方法区会存储当前类类信息,包括类的静态变量、类初始化代码(定义静态变量时的赋值语句 和 静态初始化代码块)、实例变量定义、实例初始化代码(定义实例变量时的赋值语句实例代码块和构造方法)和实例方法,还有父类的类信息引用。
+ 难得的大扫除
+ /2022/04/10/%E9%9A%BE%E5%BE%97%E7%9A%84%E5%A4%A7%E6%89%AB%E9%99%A4/
+ 因为房东要来续签合同,记得之前她说要来看看,后来一直都没来成,一方面我们没打扫过也不想被看到,小房子东西从搬进来以后越来越多,虽然不是脏乱差,但也觉得有点不满意干净状态,这里不得不感叹房东家的有钱程度,买了房子自己都没进房子看过,买来只是为了个学籍,去年前房东把房子卖给新房东后,我们还是比较担心会要换房子了,这里其实是个我们在乎的优点略大于缺点的小房子,面积比较小,但是交通便利以及上下班通勤,周边配套也还不错,有个比较大的菜市场,虽然不常去,因为不太会挑不会还价,还是主要去附近一公里左右的超市,可以安静地挑菜,但是说实在的菜场的菜还是比超市新鲜一些。 大扫除说实在的住在这边以后就没有一次真正意义上的大扫除,因为平时也有在正常打扫,只有偶尔的厨房煤气灶和厕所专门清理下,平时扫地拖地都有做,但是因为说实在的这房子也比较老了,地板什么的都有明显的老化,表面上的油漆都已经被磨损掉了,一些污渍很难拖干净,而且包括厨房和厕所的瓷砖都是纹路特别多,加上磨损,基本是污渍很多,特别是厨房的,又有油渍,我们搬进来的时候厨房的地就已经不太干净了,还有一点就是虽然不是在乡下的房子,但是旁边有两条主干道,一般只要开着窗没几天就灰尘积起来了,公司的电脑在家两天不到就一层灰,而且有些灰在地上时间久一点就会变成那种棉絮状的,看起来就会觉得更脏,并且地板我们平时就是扫一下,然后拖一下没明显的脏东西跟大灰尘就好了,有一些脏的就很难拖干净。 这次的算是整体的大扫除,把柜子,桌子,茶几台,窗边的灰尘都要擦掉,有一些角落还是有蛮多灰尘,当然特别难受的就是电脑那些接口,线缆上的,都杂糅在一块,如果要全都解开了理顺了还是比较麻烦,并且得断电,所以还是尽力清理,但没有全弄开了(我承认我是在偷懒,这里得说下清理了键盘,键盘之前都是放着用,也没盖住,按键缝里就很容易积灰也很难清理,这次索性直接把键全拔了,但是里面的清理也还是挺麻烦,因为不是平板一块,而且还有小孔,有些缝隙也比较难擦进去,只能慢慢地用牙线棒裹着抹布还有棉签擦一下,然后把键帽用洗手液什么的都擦一下洗洗干净,最后晾干了装好感觉就是一把新键盘了,后面主要是拖地了,这次最神奇的就是这个拖地,本来我就跟 LD 吹牛说拖地我是专业的,从小拖到大,有些地板缝边上的污渍,我又是用力来回拖,再用脚踩着拖,还是能把一些原来以为拖不掉的污渍给拖干净了,但是后来的厨房就比较难,用洗洁精来回拖感觉一点都起不来,可能是污渍积了太久了,一开始都想要放弃了,就打算拖干就好了,后来突然看到旁边有个洗衣服的板刷,结果竟然能刷起来,这样就停不下来了,说累是真的非常累,感觉刷一块瓷砖就要休息一会,但是整体刷完之后就是焕然一新的赶脚,简直太有成就感了。
]]>
- Java
- 类加载
+ 生活
+
+ 生活
+ 大扫除
+
- 难得的大扫除
- /2022/04/10/%E9%9A%BE%E5%BE%97%E7%9A%84%E5%A4%A7%E6%89%AB%E9%99%A4/
- 因为房东要来续签合同,记得之前她说要来看看,后来一直都没来成,一方面我们没打扫过也不想被看到,小房子东西从搬进来以后越来越多,虽然不是脏乱差,但也觉得有点不满意干净状态,这里不得不感叹房东家的有钱程度,买了房子自己都没进房子看过,买来只是为了个学籍,去年前房东把房子卖给新房东后,我们还是比较担心会要换房子了,这里其实是个我们在乎的优点略大于缺点的小房子,面积比较小,但是交通便利以及上下班通勤,周边配套也还不错,有个比较大的菜市场,虽然不常去,因为不太会挑不会还价,还是主要去附近一公里左右的超市,可以安静地挑菜,但是说实在的菜场的菜还是比超市新鲜一些。 大扫除说实在的住在这边以后就没有一次真正意义上的大扫除,因为平时也有在正常打扫,只有偶尔的厨房煤气灶和厕所专门清理下,平时扫地拖地都有做,但是因为说实在的这房子也比较老了,地板什么的都有明显的老化,表面上的油漆都已经被磨损掉了,一些污渍很难拖干净,而且包括厨房和厕所的瓷砖都是纹路特别多,加上磨损,基本是污渍很多,特别是厨房的,又有油渍,我们搬进来的时候厨房的地就已经不太干净了,还有一点就是虽然不是在乡下的房子,但是旁边有两条主干道,一般只要开着窗没几天就灰尘积起来了,公司的电脑在家两天不到就一层灰,而且有些灰在地上时间久一点就会变成那种棉絮状的,看起来就会觉得更脏,并且地板我们平时就是扫一下,然后拖一下没明显的脏东西跟大灰尘就好了,有一些脏的就很难拖干净。 这次的算是整体的大扫除,把柜子,桌子,茶几台,窗边的灰尘都要擦掉,有一些角落还是有蛮多灰尘,当然特别难受的就是电脑那些接口,线缆上的,都杂糅在一块,如果要全都解开了理顺了还是比较麻烦,并且得断电,所以还是尽力清理,但没有全弄开了(我承认我是在偷懒,这里得说下清理了键盘,键盘之前都是放着用,也没盖住,按键缝里就很容易积灰也很难清理,这次索性直接把键全拔了,但是里面的清理也还是挺麻烦,因为不是平板一块,而且还有小孔,有些缝隙也比较难擦进去,只能慢慢地用牙线棒裹着抹布还有棉签擦一下,然后把键帽用洗手液什么的都擦一下洗洗干净,最后晾干了装好感觉就是一把新键盘了,后面主要是拖地了,这次最神奇的就是这个拖地,本来我就跟 LD 吹牛说拖地我是专业的,从小拖到大,有些地板缝边上的污渍,我又是用力来回拖,再用脚踩着拖,还是能把一些原来以为拖不掉的污渍给拖干净了,但是后来的厨房就比较难,用洗洁精来回拖感觉一点都起不来,可能是污渍积了太久了,一开始都想要放弃了,就打算拖干就好了,后来突然看到旁边有个洗衣服的板刷,结果竟然能刷起来,这样就停不下来了,说累是真的非常累,感觉刷一块瓷砖就要休息一会,但是整体刷完之后就是焕然一新的赶脚,简直太有成就感了。
+ 聊一下 RocketMQ 的消息存储之 MMAP
+ /2021/09/04/%E8%81%8A%E4%B8%80%E4%B8%8B-RocketMQ-%E7%9A%84%E6%B6%88%E6%81%AF%E5%AD%98%E5%82%A8/
+ 这是个很大的话题了,可能会分成两部分说,第一部分就是所谓的零拷贝 ( zero-copy ),这一块其实也不新鲜,我对零拷贝的概念主要来自这篇文章 ,个人感觉写得非常好,在 rocketmq 中,最大的一块存储就是消息存储,也就是 CommitLog ,当然还有 ConsumeQueue 和 IndexFile,以及其他一些文件,CommitLog 的存储是以一个 1G 大小的文件作为存储单位,写完了就再建一个,那么如何提高这 1G 文件的读写效率呢,就是 mmap,传统意义的读写文件,read,write 都需要由系统调用,来回地在用户态跟内核态进行拷贝切换,
+read ( file, tmp_buf, len) ;
+write ( socket, tmp_buf, len) ;
+
+
+
+
+如上面的图显示的,要在用户态跟内核态进行切换,数据还需要在内核缓冲跟用户缓冲之间拷贝多次,
+
+
+第一步是调用 read,需要在用户态切换成内核态,DMA 模块从磁盘中读取文件,并存储在内核缓冲区,相当于是第一次复制
+数据从内核缓冲区被拷贝到用户缓冲区,read 调用返回,伴随着内核态又切换成用户态,完成了第二次复制
+然后是write 写入,这里也会伴随着用户态跟内核态的切换,数据从用户缓冲区被复制到内核空间缓冲区,完成了第三次复制,这次有点不一样的是数据不是在内核缓冲区了,会复制到 socket buffer 中。
+write 系统调用返回,又切换回了用户态,然后数据由 DMA 拷贝到协议引擎。
+
+
+如此就能看出其实默认的读写操作代价是非常大的,而在 rocketmq 等高性能中间件中都有使用的零拷贝技术,其中 rocketmq 使用的是 mmap
+mmap mmap基于 OS 的 mmap 的内存映射技术,通过MMU 映射文件,将文件直接映射到用户态的内存地址,使得对文件的操作不再是 write/read,而转化为直接对内存地址的操作,使随机读写文件和读写内存相似的速度。
+
+mmap 把文件映射到用户空间里的虚拟内存,省去了从内核缓冲区复制到用户空间的过程,文件中的位置在虚拟内存中有了对应的地址,可以像操作内存一样操作这个文件,这样的文件读写文件方式少了数据从内核缓存到用户空间的拷贝,效率很高。
+
+tmp_buf = mmap ( file, len) ;
+write ( socket, tmp_buf, len) ;
+
+
+
+第一步:mmap系统调用使得文件内容被DMA引擎复制到内核缓冲区。然后该缓冲区与用户进程共享,在内核和用户内存空间之间不进行任何拷贝。
+第二步:写系统调用使得内核将数据从原来的内核缓冲区复制到与套接字相关的内核缓冲区。
+第三步:第三次拷贝发生在DMA引擎将数据从内核套接字缓冲区传递给协议引擎时。
+通过使用mmap而不是read,我们将内核需要拷贝的数据量减少了一半。当大量的数据被传输时,这将有很好的效果。然而,这种改进并不是没有代价的;在使用mmap+write方法时,有一些隐藏的陷阱。例如当你对一个文件进行内存映射,然后在另一个进程截断同一文件时调用写。你的写系统调用将被总线错误信号SIGBUS打断,因为你执行了一个错误的内存访问。该信号的默认行为是杀死进程并dumpcore–这对网络服务器来说不是最理想的操作。
+有两种方法可以解决这个问题。
+第一种方法是为SIGBUS信号安装一个信号处理程序,然后在处理程序中简单地调用返回。通过这样做,写系统调用会返回它在被打断之前所写的字节数,并将errno设置为成功。让我指出,这将是一个糟糕的解决方案,一个治标不治本的解决方案。因为SIGBUS预示着进程出了严重的问题,所以不鼓励使用这种解决方案。
+第二个解决方案涉及内核的文件租赁(在Windows中称为 “机会锁”)。这是解决这个问题的正确方法。通过在文件描述符上使用租赁,你与内核在一个特定的文件上达成租约。然后你可以向内核请求一个读/写租约。当另一个进程试图截断你正在传输的文件时,内核会向你发送一个实时信号,即RT_SIGNAL_LEASE信号。它告诉你内核即将终止你对该文件的写或读租约。在你的程序访问一个无效的地址和被SIGBUS信号杀死之前,你的写调用会被打断了。写入调用的返回值是中断前写入的字节数,errno将被设置为成功。下面是一些示例代码,显示了如何从内核中获得租约。
+if ( fcntl ( fd, F_SETSIG, RT_SIGNAL_LEASE) == - 1 ) {
+ perror ( "kernel lease set signal" ) ;
+ return - 1 ;
+}
+
+if ( fcntl ( fd, F_SETLEASE, l_type) ) {
+ perror ( "kernel lease set type" ) ;
+ return - 1 ;
+}
]]>
- 生活
+ MQ
+ RocketMQ
+ 消息队列
- 生活
- 大扫除
+ MQ
+ 消息队列
+ RocketMQ
- 聊聊 Sharding-Jdbc 的简单使用
- /2021/12/12/%E8%81%8A%E8%81%8A-Sharding-Jdbc-%E7%9A%84%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8/
- 我们在日常工作中还是使用比较多的分库分表组件的,其中比较优秀的就有 Sharding-Jdbc,一开始由当当开源,后来捐献给了 Apache,说一下简单使用,因为原来经常的使用都是基于 xml 跟 properties 组合起来使用,这里主要试下用 Java Config 来配置 首先是通过 Spring Initializr 创建个带 jdbc 的 Spring Boot 项目,然后引入主要的依赖
-< dependency>
- < groupId> org.apache.shardingsphere</ groupId>
- < artifactId> shardingsphere-jdbc-core</ artifactId>
- < version> 5.0.0-beta</ version>
-</ dependency>
-因为前面有聊过 Spring Boot 的自动加载,在这里 spring 就会自己去找 DataSource 的配置,所以要在入口把它干掉
-@SpringBootApplication ( exclude = { DataSourceAutoConfiguration . class } )
-public class ShardingJdbcDemoApplication implements CommandLineRunner {
-然后因为想在入口跑代码,就实现了下 org.springframework.boot.CommandLineRunner 主要是后面的 Java Config 代码
+ 聊聊 SpringBoot 自动装配
+ /2021/07/11/%E8%81%8A%E8%81%8ASpringBoot-%E8%87%AA%E5%8A%A8%E8%A3%85%E9%85%8D/
+ springboot 自动装配调用链
+springboot 相比 spring能更方便开发人员上手,比较重要的一点就是自动装配,大致来看下这个逻辑
+public static void main ( String [ ] args) {
+ SpringApplication . run ( SpbDemoApplication . class , args) ;
+ }
+
+
+然后就是上面调用的 run 方法
+public static ConfigurableApplicationContext run ( Class < ? > primarySource, String . . . args) {
+ return run ( new Class < ? > [ ] { primarySource } , args) ;
+}
+
+
+继续往下看
+public static ConfigurableApplicationContext run ( Class < ? > [ ] primarySources, String [ ] args) {
+ return new SpringApplication ( primarySources) . run ( args) ;
+}
+
+调用SpringApplication的构造方法
+
+
+public SpringApplication ( Class < ? > . . . primarySources) {
+ this ( null , primarySources) ;
+}
+
+
+@SuppressWarnings ( { "unchecked" , "rawtypes" } )
+public SpringApplication ( ResourceLoader resourceLoader, Class < ? > . . . primarySources) {
+ this . resourceLoader = resourceLoader;
+ Assert . notNull ( primarySources, "PrimarySources must not be null" ) ;
+ this . primarySources = new LinkedHashSet < > ( Arrays . asList ( primarySources) ) ;
+ this . webApplicationType = WebApplicationType . deduceFromClasspath ( ) ;
+
+ this . bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories ( ) ;
+ setInitializers ( ( Collection ) getSpringFactoriesInstances ( ApplicationContextInitializer . class ) ) ;
+ setListeners ( ( Collection ) getSpringFactoriesInstances ( ApplicationListener . class ) ) ;
+ this . mainApplicationClass = deduceMainApplicationClass ( ) ;
+}
+这里就是重点了
+private List < BootstrapRegistryInitializer > getBootstrapRegistryInitializersFromSpringFactories ( ) {
+ ArrayList < BootstrapRegistryInitializer > initializers = new ArrayList < > ( ) ;
+ getSpringFactoriesInstances ( Bootstrapper . class ) . stream ( )
+ . map ( ( bootstrapper) -> ( ( BootstrapRegistryInitializer ) bootstrapper:: initialize ) )
+ . forEach ( initializers:: add ) ;
+ initializers. addAll ( getSpringFactoriesInstances ( BootstrapRegistryInitializer . class ) ) ;
+ return initializers;
+}
+private < T > Collection < T > getSpringFactoriesInstances ( Class < T > type) {
+ return getSpringFactoriesInstances ( type, new Class < ? > [ ] { } ) ;
+}
+
+private < T > Collection < T > getSpringFactoriesInstances ( Class < T > type, Class < ? > [ ] parameterTypes, Object . . . args) {
+ ClassLoader classLoader = getClassLoader ( ) ;
+
+
+ Set < String > names = new LinkedHashSet < > ( SpringFactoriesLoader . loadFactoryNames ( type, classLoader) ) ;
+ List < T > instances = createSpringFactoriesInstances ( type, parameterTypes, classLoader, args, names) ;
+ AnnotationAwareOrderComparator . sort ( instances) ;
+ return instances;
+}
+
-
-@Configuration
-public class MysqlConfig {
+
+public static List < String > loadFactoryNames ( Class < ? > factoryType, @Nullable ClassLoader classLoader) {
+ ClassLoader classLoaderToUse = classLoader;
+ if ( classLoaderToUse == null ) {
+ classLoaderToUse = SpringFactoriesLoader . class . getClassLoader ( ) ;
+ }
+ String factoryTypeName = factoryType. getName ( ) ;
+ return loadSpringFactories ( classLoaderToUse) . getOrDefault ( factoryTypeName, Collections . emptyList ( ) ) ;
+}
+ private static Map < String , List < String > > loadSpringFactories ( ClassLoader classLoader) {
+ Map < String , List < String > > result = cache. get ( classLoader) ;
+ if ( result != null ) {
+ return result;
+ }
- @Bean
- public DataSource dataSource ( ) throws SQLException {
-
- Map < String , DataSource > dataSourceMap = new HashMap < > ( ) ;
+ result = new HashMap < > ( ) ;
+ try {
+
+ Enumeration < URL> urls = classLoader. getResources ( FACTORIES_RESOURCE_LOCATION) ;
+ while ( urls. hasMoreElements ( ) ) {
+ URL url = urls. nextElement ( ) ;
+ UrlResource resource = new UrlResource ( url) ;
+ Properties properties = PropertiesLoaderUtils . loadProperties ( resource) ;
+ for ( Map. Entry < ? , ? > entry : properties. entrySet ( ) ) {
+ String factoryTypeName = ( ( String ) entry. getKey ( ) ) . trim ( ) ;
+ String [ ] factoryImplementationNames =
+ StringUtils . commaDelimitedListToStringArray ( ( String ) entry. getValue ( ) ) ;
+ for ( String factoryImplementationName : factoryImplementationNames) {
+ result. computeIfAbsent ( factoryTypeName, key -> new ArrayList < > ( ) )
+ . add ( factoryImplementationName. trim ( ) ) ;
+ }
+ }
+ }
-
-
-
- HikariDataSource dataSource1 = new HikariDataSource ( ) ;
- dataSource1. setDriverClassName ( "com.mysql.jdbc.Driver" ) ;
- dataSource1. setJdbcUrl ( "jdbc:mysql://localhost:3306/sharding" ) ;
- dataSource1. setUsername ( "username" ) ;
- dataSource1. setPassword ( "password" ) ;
- dataSourceMap. put ( "ds0" , dataSource1) ;
+
+ result. replaceAll ( ( factoryType, implementations) -> implementations. stream ( ) . distinct ( )
+ . collect ( Collectors . collectingAndThen ( Collectors . toList ( ) , Collections :: unmodifiableList ) ) ) ;
+ cache. put ( classLoader, result) ;
+ }
+ catch ( IOException ex) {
+ throw new IllegalArgumentException ( "Unable to load factories from location [" +
+ FACTORIES_RESOURCE_LOCATION + "]" , ex) ;
+ }
+ return result;
+}
-
-
- ShardingTableRuleConfiguration studentTableRuleConfig = new ShardingTableRuleConfiguration ( "student" , "ds0.student_$->{0..1}" ) ;
+我们可以看下 spring-boot-autoconfigure 的 META-INF/spring.factories
+# Initializers
+org. springframework. context. ApplicationContextInitializer= \
+org. springframework. boot. autoconfigure. SharedMetadataReaderFactoryContextInitializer, \
+org. springframework. boot. autoconfigure. logging. ConditionEvaluationReportLoggingListener
-
- studentTableRuleConfig. setTableShardingStrategy ( new StandardShardingStrategyConfiguration ( "user_id" , "tableShardingAlgorithm" ) ) ;
+# Application Listeners
+org. springframework. context. ApplicationListener= \
+org. springframework. boot. autoconfigure. BackgroundPreinitializer
+# Environment Post Processors
+org. springframework. boot. env. EnvironmentPostProcessor= \
+org. springframework. boot. autoconfigure. integration. IntegrationPropertiesEnvironmentPostProcessor
-
-
- ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration ( ) ;
- shardingRuleConfig. getTables ( ) . add ( studentTableRuleConfig) ;
+# Auto Configuration Import Listeners
+org. springframework. boot. autoconfigure. AutoConfigurationImportListener= \
+org. springframework. boot. autoconfigure. condition. ConditionEvaluationReportAutoConfigurationImportListener
-
- Properties tableShardingAlgorithmrProps = new Properties ( ) ;
-
- tableShardingAlgorithmrProps. setProperty ( "algorithm-expression" , "student_${user_id % 2}" ) ;
- shardingRuleConfig. getShardingAlgorithms ( ) . put ( "tableShardingAlgorithm" , new ShardingSphereAlgorithmConfiguration ( "INLINE" , tableShardingAlgorithmrProps) ) ;
+# Auto Configuration Import Filters
+org. springframework. boot. autoconfigure. AutoConfigurationImportFilter= \
+org. springframework. boot. autoconfigure. condition. OnBeanCondition, \
+org. springframework. boot. autoconfigure. condition. OnClassCondition, \
+org. springframework. boot. autoconfigure. condition. OnWebApplicationCondition
+
+# 注意这里,其实就是类似于 dubbo spi 的通过 org. springframework. boot. autoconfigure. EnableAutoConfiguration 作为 key
+# 获取下面所有的 AutoConfiguration 配置类
+# Auto Configure
+org. springframework. boot. autoconfigure. EnableAutoConfiguration= \
+org. springframework. boot. autoconfigure. admin. SpringApplicationAdminJmxAutoConfiguration, \
+org. springframework. boot. autoconfigure. aop. AopAutoConfiguration, \
+org. springframework. boot. autoconfigure. amqp. RabbitAutoConfiguration, \
+org. springframework. boot. autoconfigure. batch. BatchAutoConfiguration, \
+org. springframework. boot. autoconfigure. cache. CacheAutoConfiguration, \
+org. springframework. boot. autoconfigure. cassandra. CassandraAutoConfiguration, \
+org. springframework. boot. autoconfigure. context. ConfigurationPropertiesAutoConfiguration, \
+org. springframework. boot. autoconfigure. context. LifecycleAutoConfiguration, \
+org. springframework. boot. autoconfigure. context. MessageSourceAutoConfiguration, \
+org. springframework. boot. autoconfigure. context. PropertyPlaceholderAutoConfiguration, \
+org. springframework. boot. autoconfigure. couchbase. CouchbaseAutoConfiguration, \
+org. springframework. boot. autoconfigure. dao. PersistenceExceptionTranslationAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. cassandra. CassandraDataAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. cassandra. CassandraReactiveDataAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. cassandra. CassandraReactiveRepositoriesAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. cassandra. CassandraRepositoriesAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. couchbase. CouchbaseDataAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. couchbase. CouchbaseReactiveDataAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. couchbase. CouchbaseReactiveRepositoriesAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. couchbase. CouchbaseRepositoriesAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. elasticsearch. ElasticsearchDataAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. elasticsearch. ElasticsearchRepositoriesAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. elasticsearch. ReactiveElasticsearchRepositoriesAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. elasticsearch. ReactiveElasticsearchRestClientAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. jdbc. JdbcRepositoriesAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. jpa. JpaRepositoriesAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. ldap. LdapRepositoriesAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. mongo. MongoDataAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. mongo. MongoReactiveDataAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. mongo. MongoReactiveRepositoriesAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. mongo. MongoRepositoriesAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. neo4j. Neo4jDataAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. neo4j. Neo4jReactiveDataAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. neo4j. Neo4jReactiveRepositoriesAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. neo4j. Neo4jRepositoriesAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. r2dbc. R2dbcDataAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. r2dbc. R2dbcRepositoriesAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. redis. RedisAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. redis. RedisReactiveAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. redis. RedisRepositoriesAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. rest. RepositoryRestMvcAutoConfiguration, \
+org. springframework. boot. autoconfigure. data. web. SpringDataWebAutoConfiguration, \
+org. springframework. boot. autoconfigure. elasticsearch. ElasticsearchRestClientAutoConfiguration, \
+org. springframework. boot. autoconfigure. flyway. FlywayAutoConfiguration, \
+org. springframework. boot. autoconfigure. freemarker. FreeMarkerAutoConfiguration, \
+org. springframework. boot. autoconfigure. groovy. template. GroovyTemplateAutoConfiguration, \
+org. springframework. boot. autoconfigure. gson. GsonAutoConfiguration, \
+org. springframework. boot. autoconfigure. h2. H2ConsoleAutoConfiguration, \
+org. springframework. boot. autoconfigure. hateoas. HypermediaAutoConfiguration, \
+org. springframework. boot. autoconfigure. hazelcast. HazelcastAutoConfiguration, \
+org. springframework. boot. autoconfigure. hazelcast. HazelcastJpaDependencyAutoConfiguration, \
+org. springframework. boot. autoconfigure. http. HttpMessageConvertersAutoConfiguration, \
+org. springframework. boot. autoconfigure. http. codec. CodecsAutoConfiguration, \
+org. springframework. boot. autoconfigure. influx. InfluxDbAutoConfiguration, \
+org. springframework. boot. autoconfigure. info. ProjectInfoAutoConfiguration, \
+org. springframework. boot. autoconfigure. integration. IntegrationAutoConfiguration, \
+org. springframework. boot. autoconfigure. jackson. JacksonAutoConfiguration, \
+org. springframework. boot. autoconfigure. jdbc. DataSourceAutoConfiguration, \
+org. springframework. boot. autoconfigure. jdbc. JdbcTemplateAutoConfiguration, \
+org. springframework. boot. autoconfigure. jdbc. JndiDataSourceAutoConfiguration, \
+org. springframework. boot. autoconfigure. jdbc. XADataSourceAutoConfiguration, \
+org. springframework. boot. autoconfigure. jdbc. DataSourceTransactionManagerAutoConfiguration, \
+org. springframework. boot. autoconfigure. jms. JmsAutoConfiguration, \
+org. springframework. boot. autoconfigure. jmx. JmxAutoConfiguration, \
+org. springframework. boot. autoconfigure. jms. JndiConnectionFactoryAutoConfiguration, \
+org. springframework. boot. autoconfigure. jms. activemq. ActiveMQAutoConfiguration, \
+org. springframework. boot. autoconfigure. jms. artemis. ArtemisAutoConfiguration, \
+org. springframework. boot. autoconfigure. jersey. JerseyAutoConfiguration, \
+org. springframework. boot. autoconfigure. jooq. JooqAutoConfiguration, \
+org. springframework. boot. autoconfigure. jsonb. JsonbAutoConfiguration, \
+org. springframework. boot. autoconfigure. kafka. KafkaAutoConfiguration, \
+org. springframework. boot. autoconfigure. availability. ApplicationAvailabilityAutoConfiguration, \
+org. springframework. boot. autoconfigure. ldap. embedded. EmbeddedLdapAutoConfiguration, \
+org. springframework. boot. autoconfigure. ldap. LdapAutoConfiguration, \
+org. springframework. boot. autoconfigure. liquibase. LiquibaseAutoConfiguration, \
+org. springframework. boot. autoconfigure. mail. MailSenderAutoConfiguration, \
+org. springframework. boot. autoconfigure. mail. MailSenderValidatorAutoConfiguration, \
+org. springframework. boot. autoconfigure. mongo. embedded. EmbeddedMongoAutoConfiguration, \
+org. springframework. boot. autoconfigure. mongo. MongoAutoConfiguration, \
+org. springframework. boot. autoconfigure. mongo. MongoReactiveAutoConfiguration, \
+org. springframework. boot. autoconfigure. mustache. MustacheAutoConfiguration, \
+org. springframework. boot. autoconfigure. neo4j. Neo4jAutoConfiguration, \
+org. springframework. boot. autoconfigure. netty. NettyAutoConfiguration, \
+org. springframework. boot. autoconfigure. orm. jpa. HibernateJpaAutoConfiguration, \
+org. springframework. boot. autoconfigure. quartz. QuartzAutoConfiguration, \
+org. springframework. boot. autoconfigure. r2dbc. R2dbcAutoConfiguration, \
+org. springframework. boot. autoconfigure. r2dbc. R2dbcTransactionManagerAutoConfiguration, \
+org. springframework. boot. autoconfigure. rsocket. RSocketMessagingAutoConfiguration, \
+org. springframework. boot. autoconfigure. rsocket. RSocketRequesterAutoConfiguration, \
+org. springframework. boot. autoconfigure. rsocket. RSocketServerAutoConfiguration, \
+org. springframework. boot. autoconfigure. rsocket. RSocketStrategiesAutoConfiguration, \
+org. springframework. boot. autoconfigure. security. servlet. SecurityAutoConfiguration, \
+org. springframework. boot. autoconfigure. security. servlet. UserDetailsServiceAutoConfiguration, \
+org. springframework. boot. autoconfigure. security. servlet. SecurityFilterAutoConfiguration, \
+org. springframework. boot. autoconfigure. security. reactive. ReactiveSecurityAutoConfiguration, \
+org. springframework. boot. autoconfigure. security. reactive. ReactiveUserDetailsServiceAutoConfiguration, \
+org. springframework. boot. autoconfigure. security. rsocket. RSocketSecurityAutoConfiguration, \
+org. springframework. boot. autoconfigure. security. saml2. Saml2RelyingPartyAutoConfiguration, \
+org. springframework. boot. autoconfigure. sendgrid. SendGridAutoConfiguration, \
+org. springframework. boot. autoconfigure. session. SessionAutoConfiguration, \
+org. springframework. boot. autoconfigure. security. oauth2. client. servlet. OAuth2ClientAutoConfiguration, \
+org. springframework. boot. autoconfigure. security. oauth2. client. reactive. ReactiveOAuth2ClientAutoConfiguration, \
+org. springframework. boot. autoconfigure. security. oauth2. resource. servlet. OAuth2ResourceServerAutoConfiguration, \
+org. springframework. boot. autoconfigure. security. oauth2. resource. reactive. ReactiveOAuth2ResourceServerAutoConfiguration, \
+org. springframework. boot. autoconfigure. solr. SolrAutoConfiguration, \
+org. springframework. boot. autoconfigure. sql. init. SqlInitializationAutoConfiguration, \
+org. springframework. boot. autoconfigure. task. TaskExecutionAutoConfiguration, \
+org. springframework. boot. autoconfigure. task. TaskSchedulingAutoConfiguration, \
+org. springframework. boot. autoconfigure. thymeleaf. ThymeleafAutoConfiguration, \
+org. springframework. boot. autoconfigure. transaction. TransactionAutoConfiguration, \
+org. springframework. boot. autoconfigure. transaction. jta. JtaAutoConfiguration, \
+org. springframework. boot. autoconfigure. validation. ValidationAutoConfiguration, \
+org. springframework. boot. autoconfigure. web. client. RestTemplateAutoConfiguration, \
+org. springframework. boot. autoconfigure. web. embedded. EmbeddedWebServerFactoryCustomizerAutoConfiguration, \
+org. springframework. boot. autoconfigure. web. reactive. HttpHandlerAutoConfiguration, \
+org. springframework. boot. autoconfigure. web. reactive. ReactiveWebServerFactoryAutoConfiguration, \
+org. springframework. boot. autoconfigure. web. reactive. WebFluxAutoConfiguration, \
+org. springframework. boot. autoconfigure. web. reactive. error. ErrorWebFluxAutoConfiguration, \
+org. springframework. boot. autoconfigure. web. reactive. function. client. ClientHttpConnectorAutoConfiguration, \
+org. springframework. boot. autoconfigure. web. reactive. function. client. WebClientAutoConfiguration, \
+org. springframework. boot. autoconfigure. web. servlet. DispatcherServletAutoConfiguration, \
+org. springframework. boot. autoconfigure. web. servlet. ServletWebServerFactoryAutoConfiguration, \
+org. springframework. boot. autoconfigure. web. servlet. error. ErrorMvcAutoConfiguration, \
+org. springframework. boot. autoconfigure. web. servlet. HttpEncodingAutoConfiguration, \
+org. springframework. boot. autoconfigure. web. servlet. MultipartAutoConfiguration, \
+org. springframework. boot. autoconfigure. web. servlet. WebMvcAutoConfiguration, \
+org. springframework. boot. autoconfigure. websocket. reactive. WebSocketReactiveAutoConfiguration, \
+org. springframework. boot. autoconfigure. websocket. servlet. WebSocketServletAutoConfiguration, \
+org. springframework. boot. autoconfigure. websocket. servlet. WebSocketMessagingAutoConfiguration, \
+org. springframework. boot. autoconfigure. webservices. WebServicesAutoConfiguration, \
+org. springframework. boot. autoconfigure. webservices. client. WebServiceTemplateAutoConfiguration
+# Failure analyzers
+org. springframework. boot. diagnostics. FailureAnalyzer= \
+org. springframework. boot. autoconfigure. data. redis. RedisUrlSyntaxFailureAnalyzer, \
+org. springframework. boot. autoconfigure. diagnostics. analyzer. NoSuchBeanDefinitionFailureAnalyzer, \
+org. springframework. boot. autoconfigure. flyway. FlywayMigrationScriptMissingFailureAnalyzer, \
+org. springframework. boot. autoconfigure. jdbc. DataSourceBeanCreationFailureAnalyzer, \
+org. springframework. boot. autoconfigure. jdbc. HikariDriverConfigurationFailureAnalyzer, \
+org. springframework. boot. autoconfigure. r2dbc. ConnectionFactoryBeanCreationFailureAnalyzer, \
+org. springframework. boot. autoconfigure. session. NonUniqueSessionRepositoryFailureAnalyzer
-
- return ShardingSphereDataSourceFactory . createDataSource ( dataSourceMap, Collections . singleton ( shardingRuleConfig) , new Properties ( ) ) ;
+# Template availability providers
+org. springframework. boot. autoconfigure. template. TemplateAvailabilityProvider= \
+org. springframework. boot. autoconfigure. freemarker. FreeMarkerTemplateAvailabilityProvider, \
+org. springframework. boot. autoconfigure. mustache. MustacheTemplateAvailabilityProvider, \
+org. springframework. boot. autoconfigure. groovy. template. GroovyTemplateAvailabilityProvider, \
+org. springframework. boot. autoconfigure. thymeleaf. ThymeleafTemplateAvailabilityProvider, \
+org. springframework. boot. autoconfigure. web. servlet. JspTemplateAvailabilityProvider
- }
-}
-然后我们就可以在使用这个 DataSource 了,先看下这两个表的数据
-@Override
- public void run ( String . . . args) {
- LOGGER. info ( "run here" ) ;
- String sql = "SELECT * FROM student WHERE user_id=? " ;
- try (
- Connection conn = dataSource. getConnection ( ) ;
- PreparedStatement ps = conn. prepareStatement ( sql) ) {
-
- ps. setInt ( 1 , 1001 ) ;
+# DataSource initializer detectors
+org. springframework. boot. sql. init. dependency. DatabaseInitializerDetector= \
+org. springframework. boot. autoconfigure. flyway. FlywayMigrationInitializerDatabaseInitializerDetector
+
- ResultSet resultSet = ps. executeQuery ( ) ;
- while ( resultSet. next ( ) ) {
- final int id = resultSet. getInt ( "id" ) ;
- final String name = resultSet. getString ( "name" ) ;
- final int userId = resultSet. getInt ( "user_id" ) ;
- final int age = resultSet. getInt ( "age" ) ;
- System . out. println ( "奇数表 id:" + id + " 姓名:" + name
- + " 用户 id:" + userId + " 年龄:" + age ) ;
- System . out. println ( "=============================" ) ;
- }
-
- ps. setInt ( 1 , 1000 ) ;
- resultSet = ps. executeQuery ( ) ;
- while ( resultSet. next ( ) ) {
- final int id = resultSet. getInt ( "id" ) ;
- final String name = resultSet. getString ( "name" ) ;
- final int userId = resultSet. getInt ( "user_id" ) ;
- final int age = resultSet. getInt ( "age" ) ;
- System . out. println ( "偶数表 id:" + id + " 姓名:" + name
- + " 用户 id:" + userId + " 年龄:" + age ) ;
- System . out. println ( "=============================" ) ;
- }
- } catch ( SQLException e) {
- e. printStackTrace ( ) ;
- }
- }
-看下查询结果
+上面根据 org.springframework.boot.autoconfigure.EnableAutoConfiguration 获取的各个配置类,在通过反射加载就能得到一堆 JavaConfig配置类,然后再根据 ConditionalOnProperty等条件配置加载具体的 bean,大致就是这么个逻辑
]]>
Java
+ SpringBoot
Java
- Sharding-Jdbc
+ Spring
+ SpringBoot
+ 自动装配
+ AutoConfiguration
- 聊聊 mysql 的 MVCC
- /2020/04/26/%E8%81%8A%E8%81%8A-mysql-%E7%9A%84-MVCC/
- 很久以前,有位面试官问到,你知道 mysql 的事务隔离级别吗,“额 O__O …,不太清楚”,完了之后我就去网上找相关的文章,找到了这篇MySQL 四种事务隔离级的说明 , 文章写得特别好,看了这个就懂了各个事务隔离级别都是啥,不过看了这个之后多思考一下的话还是会发现问题,这么神奇的事务隔离级别是怎么实现的呢
-其中 innodb 的事务隔离用到了标题里说到的 mvcc,Multiversion concurrency control , 直译过来就是多版本并发控制,先不讲这个究竟是个啥,考虑下如果纯猜测,这个事务隔离级别应该会是怎么样实现呢,愚钝的我想了下,可以在事务开始的时候拷贝一个表,这个可以支持 RR 级别,RC 级别就不支持了,而且要是个非常大的表,想想就不可行
-腆着脸说虽然这个不可行,但是思路是对的,具体实行起来需要做一系列(肥肠多)的改动,首先根据我的理解,其实这个拷贝一个表是变成拷贝一条记录,但是如果有多个事务,那就得拷贝多次,这个问题其实可以借助版本管理系统来解释,在用版本管理系统,git 之类的之前,很原始的可能是开发完一个功能后,就打个压缩包用时间等信息命名,然后如果后面要找回这个就直接用这个压缩包的就行了,后来有了 svn,git 中心式和分布式的版本管理系统,它的一个特点是粒度可以控制到文件和代码行级别,对应的我们的 mysql 事务是不是也可以从一开始预想的表级别细化到行的级别,可能之前很多人都了解过,数据库的一行记录除了我们用户自定义的字段,还有一些额外的字段,去源码data0type.h 里捞一下
-
-# define DATA_ROW_ID 0
-# define DATA_ROW_ID_LEN 6
-
-
-constexpr size_t DATA_TRX_ID = 1 ;
-
-
-constexpr size_t DATA_TRX_ID_LEN = 6 ;
-
-
-constexpr size_t DATA_ROLL_PTR = 2 ;
-
-
-constexpr size_t DATA_ROLL_PTR_LEN = 7 ;
-
-一个是 DATA_ROW_ID,这个是在数据没指定主键的时候会生成一个隐藏的,如果用户有指定主键就是主键了
-一个是 DATA_TRX_ID,这个表示这条记录的事务 ID
-还有一个是 DATA_ROLL_PTR 指向回滚段的指针
-指向的回滚段其实就是我们常说的 undo log,这里面的具体结构就是个链表,在 mvcc 里会使用到这个,还有就是这个 DATA_TRX_ID,每条记录都记录了这个事务 ID,表示的是这条记录的当前值是被哪个事务修改的,下面就扯回事务了,我们知道 Read Uncommitted, 其实用不到隔离,直接读取当前值就好了,到了 Read Committed 级别,我们要让事务读取到提交过的值,mysql 使用了一个叫 read view 的玩意,它里面有这些值是我们需要注意的,
-m_low_limit_id, 这个是 read view 创建时最大的活跃事务 id
-m_up_limit_id, 这个是 read view 创建时最小的活跃事务 id
-m_ids, 这个是 read view 创建时所有的活跃事务 id 数组
-m_creator_trx_id 这个是当前记录的创建事务 id
-判断事务的可见性主要的逻辑是这样,
-
-当记录的事务 id 小于最小活跃事务 id,说明是可见的,
-如果记录的事务 id 等于当前事务 id,说明是自己的更改,可见
-如果记录的事务 id 大于最大的活跃事务 id, 不可见
-如果记录的事务 id 介于 m_low_limit_id 和 m_up_limit_id 之间,则要判断它是否在 m_ids 中,如果在,不可见,如果不在,表示已提交,可见 具体的代码 捞一下看看/** Check whether the changes by id are visible.
- @param[in] id transaction id to check against the view
- @param[in] name table name
- @return whether the view sees the modifications of id. */
- bool changes_visible(trx_id_t id, const table_name_t &name) const
- MY_ATTRIBUTE((warn_unused_result)) {
- ut_ad(id > 0);
-
- if (id < m_up_limit_id || id == m_creator_trx_id) {
- return (true);
- }
-
- check_trx_id_sanity(id, name);
-
- if (id >= m_low_limit_id) {
- return (false);
-
- } else if (m_ids.empty()) {
- return (true);
- }
-
- const ids_t::value_type *p = m_ids.data();
-
- return (!std::binary_search(p, p + m_ids.size(), id));
- }
-剩下来一点是啥呢,就是 Read Committed 和 Repeated Read 也不一样,那前面说的 read view 都能支持吗,又是怎么支持呢,假如这个 read view 是在事务一开始就创建,那好像能支持的只是 RR 事务隔离级别,其实呢,这是通过创建 read view的时机,对于 RR 级别,就是在事务的第一个 select 语句是创建,对于 RC 级别,是在每个 select 语句执行前都是创建一次,那样就可以保证能读到所有已提交的数据
-
+ 聊聊给亲戚朋友的老电脑重装系统那些事儿
+ /2021/05/09/%E8%81%8A%E8%81%8A%E7%BB%99%E4%BA%B2%E6%88%9A%E6%9C%8B%E5%8F%8B%E7%9A%84%E8%80%81%E7%94%B5%E8%84%91%E9%87%8D%E8%A3%85%E7%B3%BB%E7%BB%9F%E9%82%A3%E4%BA%9B%E4%BA%8B%E5%84%BF/
+ 前面这个五一回去之前,LD 姐姐跟我说电脑很卡了,想让我重装系统,问了下 LD 可能是那个 09 年买的笔记本,想想有点害怕[捂脸],前年有一次好像让我帮忙装了她同事的一个三星的笔记本,本着一些系统洁癖,所以就从开始找纯净版的 win7 家庭版,因为之前那些本基本都自带 win7 的家庭版,而且把激活码就贴在机器下面,然后从三星官网去找官方驱动,还好这个机型的驱动还在,先做了系统镜像,其实感觉这种情况需要两个 U 盘,一个 U 盘装系统作为安装启动盘,一个放驱动,毕竟不是专业装系统的,然后因为官方驱动需要一个个下载一个个安装,然后驱动文件下载的地方还没标明是 32 位还是 64 位的,结果还被 LD 姐姐催着,一直问好没好,略尴尬,索性还是找个一键安装的
+这次甚至更夸张,上次还让带回去,我准备好了系统镜像啥的,第二天装,这次直接带了两个老旧笔记本过来说让当天就装好,感觉有点像被当修电脑的使,又说这些电脑其实都不用了的,都是为了她们当医生的要每年看会课,然后只能用电脑浏览器看,结果都在用 360 浏览器,真的是万恶的 360,其实以前对 360 没啥坏印象,毕竟以前也经常用,只是对于这些老电脑,360 全家桶真的就是装了就废了,2G 的内存,开机就开着 360 安全卫士,360 杀毒,有一个还装了腾讯电脑管家,然后腾讯视频跟爱奇艺也开机启动了,然后还打开 360 浏览器看课,就算再好的系统也吃不消这么用,重装了系统,还是这么装这些东西,也是分分钟变卡,可惜他们都没啥这类概念。
+对于他们要看的课,更搞笑的是,明明在页面上注明了说要使用 IE 浏览器,结果他们都在用 360 浏览器看,但是这个也不能完全怪他们,因为实在是现在的 IE 啥的也有开始不兼容 flash 的配置,需要开启兼容配置,但是只要开启了之后就可以直接用 IE 看,比 360 靠谱很多, 资源占用也比较少,360 估计是基于 chromium 加了很多内置的插件,本身 chromium 也是内存大户,但是说这些其实他们也不懂,总觉得找我免费装下系统能撑一段时间,反正对我来说也应该很简单(他们觉得),实际上开始工作以后,我自己想装个双系统都是上淘宝买别人的服务装的,台式机更是几年没动过系统了,因为要重装一大堆软件,数据备份啥的,还有驱动什么的,分区格式,那些驱动精灵啥的也都是越来越坑,一装就给你带一堆垃圾软件。
+感悟是,总觉得学计算机的就应该会装系统,会修电脑,之前亲戚还拿着一个完全开不起来的笔记本让我来修,这真的是,我说可以找官方维修的,结果我说我搞不定,她直接觉得是修不好了,直接电脑都懒得拿回去了,后面又一次反复解释了才明白,另外就是 360 全家桶,别说老电脑了,新机器都不太吃得消。
]]>
- Mysql
- C
- 数据结构
- 源码
- Mysql
+ 生活
- mysql
- 数据结构
- 源码
- mvcc
- read view
+ 生活
+ 装电脑
+ 老电脑
+ 360 全家桶
+ 修电脑的
diff --git a/sitemap.xml b/sitemap.xml
index 6b4380f981..ea07fefcd8 100644
--- a/sitemap.xml
+++ b/sitemap.xml
@@ -200,7 +200,7 @@
- https://nicksxs.me/2021/09/04/%E8%81%8A%E4%B8%80%E4%B8%8B-RocketMQ-%E7%9A%84%E6%B6%88%E6%81%AF%E5%AD%98%E5%82%A8/
+ https://nicksxs.me/2021/10/03/%E8%81%8A%E4%B8%80%E4%B8%8B-RocketMQ-%E7%9A%84%E6%B6%88%E6%81%AF%E5%AD%98%E5%82%A8%E4%B8%89/
2022-06-11
@@ -209,7 +209,7 @@
- https://nicksxs.me/2021/10/03/%E8%81%8A%E4%B8%80%E4%B8%8B-RocketMQ-%E7%9A%84%E6%B6%88%E6%81%AF%E5%AD%98%E5%82%A8%E4%B8%89/
+ https://nicksxs.me/2021/12/12/%E8%81%8A%E8%81%8A-Sharding-Jdbc-%E7%9A%84%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8/
2022-06-11
@@ -227,7 +227,7 @@
- https://nicksxs.me/2021/12/12/%E8%81%8A%E8%81%8A-Sharding-Jdbc-%E7%9A%84%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8/
+ https://nicksxs.me/2021/09/04/%E8%81%8A%E4%B8%80%E4%B8%8B-RocketMQ-%E7%9A%84%E6%B6%88%E6%81%AF%E5%AD%98%E5%82%A8/
2022-06-11
@@ -425,7 +425,7 @@
- https://nicksxs.me/2021/09/26/%E8%81%8A%E4%B8%80%E4%B8%8B-SpringBoot-%E4%B8%AD%E5%8A%A8%E6%80%81%E5%88%87%E6%8D%A2%E6%95%B0%E6%8D%AE%E6%BA%90%E7%9A%84%E6%96%B9%E6%B3%95/
+ https://nicksxs.me/2021/09/19/%E8%81%8A%E4%B8%80%E4%B8%8B-SpringBoot-%E4%B8%AD%E4%BD%BF%E7%94%A8%E7%9A%84-cglib-%E4%BD%9C%E4%B8%BA%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E4%B8%AD%E7%9A%84%E4%B8%80%E4%B8%AA%E6%B3%A8%E6%84%8F%E7%82%B9/
2022-06-11
@@ -434,7 +434,7 @@
- https://nicksxs.me/2021/09/19/%E8%81%8A%E4%B8%80%E4%B8%8B-SpringBoot-%E4%B8%AD%E4%BD%BF%E7%94%A8%E7%9A%84-cglib-%E4%BD%9C%E4%B8%BA%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E4%B8%AD%E7%9A%84%E4%B8%80%E4%B8%AA%E6%B3%A8%E6%84%8F%E7%82%B9/
+ https://nicksxs.me/2021/09/26/%E8%81%8A%E4%B8%80%E4%B8%8B-SpringBoot-%E4%B8%AD%E5%8A%A8%E6%80%81%E5%88%87%E6%8D%A2%E6%95%B0%E6%8D%AE%E6%BA%90%E7%9A%84%E6%96%B9%E6%B3%95/
2022-06-11
@@ -1343,7 +1343,7 @@
- https://nicksxs.me/2015/03/11/Reverse-Bits/
+ https://nicksxs.me/2019/12/10/Redis-Part-1/
2020-01-12
@@ -1352,7 +1352,7 @@
- https://nicksxs.me/2015/03/13/Reverse-Integer/
+ https://nicksxs.me/2015/03/11/Reverse-Bits/
2020-01-12
@@ -1361,7 +1361,7 @@
- https://nicksxs.me/2015/01/14/Two-Sum/
+ https://nicksxs.me/2015/03/13/Reverse-Integer/
2020-01-12
@@ -1370,7 +1370,7 @@
- https://nicksxs.me/2017/05/09/ambari-summary/
+ https://nicksxs.me/2015/01/14/Two-Sum/
2020-01-12
@@ -1379,7 +1379,7 @@
- https://nicksxs.me/2016/08/14/docker-mysql-cluster/
+ https://nicksxs.me/2017/05/09/ambari-summary/
2020-01-12
@@ -1388,7 +1388,7 @@
- https://nicksxs.me/2016/10/11/minimum-size-subarray-sum-209/
+ https://nicksxs.me/2016/09/29/binary-watch/
2020-01-12
@@ -1397,7 +1397,7 @@
- https://nicksxs.me/2019/12/10/Redis-Part-1/
+ https://nicksxs.me/2016/08/14/docker-mysql-cluster/
2020-01-12
@@ -1406,7 +1406,7 @@
- https://nicksxs.me/2016/09/29/binary-watch/
+ https://nicksxs.me/2016/10/11/minimum-size-subarray-sum-209/
2020-01-12
@@ -1433,7 +1433,7 @@
- https://nicksxs.me/2020/01/10/redis%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%BB%8B%E7%BB%8D%E4%B8%89/
+ https://nicksxs.me/2019/12/26/redis%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%BB%8B%E7%BB%8D/
2020-01-12
@@ -1442,7 +1442,7 @@
- https://nicksxs.me/2020/01/04/redis%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%BB%8B%E7%BB%8D%E4%BA%8C/
+ https://nicksxs.me/2020/01/10/redis%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%BB%8B%E7%BB%8D%E4%B8%89/
2020-01-12
@@ -1451,7 +1451,7 @@
- https://nicksxs.me/2019/12/26/redis%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%BB%8B%E7%BB%8D/
+ https://nicksxs.me/2020/01/04/redis%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%BB%8B%E7%BB%8D%E4%BA%8C/
2020-01-12
@@ -1532,7 +1532,7 @@
- https://nicksxs.me/2014/12/23/my-new-post/
+ https://nicksxs.me/2015/06/22/invert-binary-tree/
2020-01-12
@@ -1541,7 +1541,7 @@
- https://nicksxs.me/2015/01/16/pcre-intro-and-a-simple-package/
+ https://nicksxs.me/2014/12/23/my-new-post/
2020-01-12
@@ -1550,7 +1550,7 @@
- https://nicksxs.me/2017/04/25/rabbitmq-tips/
+ https://nicksxs.me/2015/01/16/pcre-intro-and-a-simple-package/
2020-01-12
@@ -1559,7 +1559,7 @@
- https://nicksxs.me/2017/03/28/spark-little-tips/
+ https://nicksxs.me/2017/04/25/rabbitmq-tips/
2020-01-12
@@ -1568,7 +1568,7 @@
- https://nicksxs.me/2016/10/12/summary-ranges-228/
+ https://nicksxs.me/2017/03/28/spark-little-tips/
2020-01-12
@@ -1577,7 +1577,7 @@
- https://nicksxs.me/2016/07/13/swoole-websocket-test/
+ https://nicksxs.me/2016/10/12/summary-ranges-228/
2020-01-12
@@ -1586,7 +1586,7 @@
- https://nicksxs.me/2015/06/22/invert-binary-tree/
+ https://nicksxs.me/2016/07/13/swoole-websocket-test/
2020-01-12
@@ -1633,7 +1633,7 @@
https://nicksxs.me/
- 2022-08-26
+ 2022-08-28
daily
1.0
@@ -1641,1981 +1641,1981 @@
https://nicksxs.me/tags/%E7%94%9F%E6%B4%BB/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/2020/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/2021/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E6%8B%96%E6%9B%B4/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Java/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/JVM/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/C/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/leetcode/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/java/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Binary-Tree/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/DFS/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E4%BA%8C%E5%8F%89%E6%A0%91/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E9%A2%98%E8%A7%A3/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Linked-List/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/%E8%AF%BB%E5%90%8E%E6%84%9F/
- 2022-08-26
+ https://nicksxs.me/tags/2019/
+ 2022-08-28
+ weekly
+ 0.2
+
+
+
+ https://nicksxs.me/tags/%E6%8A%80%E6%9C%AF/
+ 2022-08-28
+ weekly
+ 0.2
+
+
+
+ https://nicksxs.me/tags/%E8%AF%BB%E4%B9%A6/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%B9%B4%E4%B8%AD%E6%80%BB%E7%BB%93/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/c/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%B9%B6%E5%8F%91/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/j-u-c/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/aqs/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/condition/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/await/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/signal/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/lock/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/unlock/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Apollo/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/value/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E6%B3%A8%E8%A7%A3/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/environment/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Stream/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Comparator/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E6%8E%92%E5%BA%8F/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/sort/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/nullsfirst/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Disruptor/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Dubbo/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/RPC/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Filter/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Interceptor/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/AOP/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Spring/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Tomcat/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Servlet/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Web/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/G1/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/GC/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Garbage-First-Collector/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E9%80%92%E5%BD%92/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Preorder-Traversal/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Inorder-Traversal/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%89%8D%E5%BA%8F/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E4%B8%AD%E5%BA%8F/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Print-FooBar-Alternately/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/DP/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Shift-2D-Grid/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/stack/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/min-stack/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E6%9C%80%E5%B0%8F%E6%A0%88/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/leetcode-155/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/3Sum-Closest/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/linked-list/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Lowest-Common-Ancestor-of-a-Binary-Tree/
- 2022-08-26
+ 2022-08-28
+ weekly
+ 0.2
+
+
+
+ https://nicksxs.me/tags/First-Bad-Version/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/string/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/First-Bad-Version/
- 2022-08-26
+ https://nicksxs.me/tags/Intersection-of-Two-Arrays/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/2019/
- 2022-08-26
+ https://nicksxs.me/tags/Median-of-Two-Sorted-Arrays/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/dp/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E4%BB%A3%E7%A0%81%E9%A2%98%E8%A7%A3/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Trapping-Rain-Water/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E6%8E%A5%E9%9B%A8%E6%B0%B4/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Leetcode-42/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/Intersection-of-Two-Arrays/
- 2022-08-26
+ https://nicksxs.me/tags/Rotate-Image/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/Rotate-Image/
- 2022-08-26
+ https://nicksxs.me/tags/%E7%9F%A9%E9%98%B5/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/%E7%9F%A9%E9%98%B5/
- 2022-08-26
+ https://nicksxs.me/tags/Remove-Duplicates-from-Sorted-List/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/%E6%8A%80%E6%9C%AF/
- 2022-08-26
+ https://nicksxs.me/tags/linux/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/%E8%AF%BB%E4%B9%A6/
- 2022-08-26
+ https://nicksxs.me/tags/grep/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/Median-of-Two-Sorted-Arrays/
- 2022-08-26
+ https://nicksxs.me/tags/%E8%BD%AC%E4%B9%89/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/linux/
- 2022-08-26
+ https://nicksxs.me/tags/mfc/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/grep/
- 2022-08-26
+ https://nicksxs.me/tags/Maven/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/%E8%BD%AC%E4%B9%89/
- 2022-08-26
+ https://nicksxs.me/tags/C/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/mfc/
- 2022-08-26
+ https://nicksxs.me/tags/Redis/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/Maven/
- 2022-08-26
+ https://nicksxs.me/tags/Distributed-Lock/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/Remove-Duplicates-from-Sorted-List/
- 2022-08-26
+ https://nicksxs.me/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/hadoop/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/cluster/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/docker/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/mysql/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Docker/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/namespace/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/cgroup/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Dockerfile/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/echo/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/uname/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%8F%91%E8%A1%8C%E7%89%88/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Gogs/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Webhook/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%8D%9A%E5%AE%A2%EF%BC%8C%E6%96%87%E7%AB%A0/
- 2022-08-26
- weekly
- 0.2
-
-
-
- https://nicksxs.me/tags/C/
- 2022-08-26
- weekly
- 0.2
-
-
-
- https://nicksxs.me/tags/Redis/
- 2022-08-26
- weekly
- 0.2
-
-
-
- https://nicksxs.me/tags/Distributed-Lock/
- 2022-08-26
- weekly
- 0.2
-
-
-
- https://nicksxs.me/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Mysql/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Mybatis/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Sql%E6%B3%A8%E5%85%A5/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E7%BC%93%E5%AD%98/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/openresty/
- 2022-08-26
+ https://nicksxs.me/tags/nginx/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/nginx/
- 2022-08-26
+ https://nicksxs.me/tags/%E6%97%A5%E5%BF%97/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/%E6%97%A5%E5%BF%97/
- 2022-08-26
+ https://nicksxs.me/tags/openresty/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/mq/
- 2022-08-26
+ https://nicksxs.me/tags/php/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/php/
- 2022-08-26
+ https://nicksxs.me/tags/mq/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/im/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/redis/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E6%BA%90%E7%A0%81/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E6%B7%98%E6%B1%B0%E7%AD%96%E7%95%A5/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%BA%94%E7%94%A8/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Evict/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E8%BF%87%E6%9C%9F%E7%AD%96%E7%95%A5/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Rust/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E6%89%80%E6%9C%89%E6%9D%83/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%86%85%E5%AD%98%E5%88%86%E5%B8%83/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E6%96%B0%E8%AF%AD%E8%A8%80/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%8F%AF%E5%8F%98%E5%BC%95%E7%94%A8/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E4%B8%8D%E5%8F%AF%E5%8F%98%E5%BC%95%E7%94%A8/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%88%87%E7%89%87/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/spark/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/python/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Spring-Event/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/websocket/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/swoole/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/WordPress/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%B0%8F%E6%8A%80%E5%B7%A7/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/gc/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E6%A0%87%E8%AE%B0%E6%95%B4%E7%90%86/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/jvm/
- 2022-08-26
- weekly
- 0.2
-
-
-
- https://nicksxs.me/tags/MQ/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/
- 2022-08-26
+ https://nicksxs.me/tags/%E8%AF%BB%E5%90%8E%E6%84%9F/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/RocketMQ/
- 2022-08-26
+ https://nicksxs.me/tags/MQ/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/%E5%89%8A%E5%B3%B0%E5%A1%AB%E8%B0%B7/
- 2022-08-26
+ https://nicksxs.me/tags/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/%E4%B8%AD%E9%97%B4%E4%BB%B6/
- 2022-08-26
+ https://nicksxs.me/tags/RocketMQ/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/ssh/
- 2022-08-26
+ https://nicksxs.me/tags/%E5%89%8A%E5%B3%B0%E5%A1%AB%E8%B0%B7/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/%E7%AB%AF%E5%8F%A3%E8%BD%AC%E5%8F%91/
- 2022-08-26
+ https://nicksxs.me/tags/%E4%B8%AD%E9%97%B4%E4%BB%B6/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%90%90%E6%A7%BD/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E7%96%AB%E6%83%85/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E7%BE%8E%E5%9B%BD/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%85%AC%E4%BA%A4%E8%BD%A6/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%8F%A3%E7%BD%A9/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E6%9D%80%E4%BA%BA%E8%AF%9B%E5%BF%83/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%BC%80%E8%BD%A6/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%8A%A0%E5%A1%9E/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E7%B3%9F%E5%BF%83%E4%BA%8B/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E8%A7%84%E5%88%99/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%85%AC%E4%BA%A4/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E8%B7%AF%E6%94%BF%E8%A7%84%E5%88%92/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%9F%BA%E7%A1%80%E8%AE%BE%E6%96%BD/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E6%9D%AD%E5%B7%9E/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%81%A5%E5%BA%B7%E7%A0%81/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E6%89%93%E5%8D%A1/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%B9%B8%E7%A6%8F%E4%BA%86%E5%90%97/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E8%B6%B3%E7%90%83/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/git/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/scp/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E8%BF%90%E5%8A%A8/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%87%8F%E8%82%A5/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E8%B7%91%E6%AD%A5/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%B9%B2%E6%B4%BB/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%BD%B1%E8%AF%84/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%AF%84%E7%94%9F%E8%99%AB/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/%E5%9B%A4%E7%89%A9%E8%B5%84/
- 2022-08-26
+ https://nicksxs.me/tags/%E5%AD%97%E7%AC%A6%E9%9B%86/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/DefaultMQPushConsumer/
- 2022-08-26
+ https://nicksxs.me/tags/%E7%BC%96%E7%A0%81/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/
- 2022-08-26
+ https://nicksxs.me/tags/utf8/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/NameServer/
- 2022-08-26
+ https://nicksxs.me/tags/utf8mb4/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/%E5%AD%97%E7%AC%A6%E9%9B%86/
- 2022-08-26
+ https://nicksxs.me/tags/utf8mb4-0900-ai-ci/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/%E7%BC%96%E7%A0%81/
- 2022-08-26
+ https://nicksxs.me/tags/utf8mb4-unicode-ci/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/utf8/
- 2022-08-26
+ https://nicksxs.me/tags/utf8mb4-general-ci/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/utf8mb4/
- 2022-08-26
+ https://nicksxs.me/tags/%E5%9B%A4%E7%89%A9%E8%B5%84/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/utf8mb4-0900-ai-ci/
- 2022-08-26
+ https://nicksxs.me/tags/DefaultMQPushConsumer/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/utf8mb4-unicode-ci/
- 2022-08-26
+ https://nicksxs.me/tags/%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/utf8mb4-general-ci/
- 2022-08-26
+ https://nicksxs.me/tags/NameServer/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/SpringBoot/
- 2022-08-26
+ 2022-08-28
+ weekly
+ 0.2
+
+
+
+ https://nicksxs.me/tags/cglib/
+ 2022-08-28
+ weekly
+ 0.2
+
+
+
+ https://nicksxs.me/tags/%E4%BA%8B%E5%8A%A1/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Druid/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E6%95%B0%E6%8D%AE%E6%BA%90%E5%8A%A8%E6%80%81%E5%88%87%E6%8D%A2/
- 2022-08-26
+ 2022-08-28
+ weekly
+ 0.2
+
+
+
+ https://nicksxs.me/tags/ssh/
+ 2022-08-28
+ weekly
+ 0.2
+
+
+
+ https://nicksxs.me/tags/%E7%AB%AF%E5%8F%A3%E8%BD%AC%E5%8F%91/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E4%B8%9C%E4%BA%AC%E5%A5%A5%E8%BF%90%E4%BC%9A/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E4%B8%BE%E9%87%8D/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%B0%84%E5%87%BB/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E4%B9%92%E4%B9%93%E7%90%83/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E8%B7%B3%E6%B0%B4/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/SPI/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Adaptive/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E8%87%AA%E9%80%82%E5%BA%94%E6%8B%93%E5%B1%95/
- 2022-08-26
- weekly
- 0.2
-
-
-
- https://nicksxs.me/tags/cglib/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/%E4%BA%8B%E5%8A%A1/
- 2022-08-26
+ https://nicksxs.me/tags/%E5%AE%B9%E9%94%99%E6%9C%BA%E5%88%B6/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Synchronized/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%81%8F%E5%90%91%E9%94%81/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E8%BD%BB%E9%87%8F%E7%BA%A7%E9%94%81/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E9%87%8D%E9%87%8F%E7%BA%A7%E9%94%81/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E8%87%AA%E6%97%8B/
- 2022-08-26
- weekly
- 0.2
-
-
-
- https://nicksxs.me/tags/%E5%AE%B9%E9%94%99%E6%9C%BA%E5%88%B6/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E7%B1%BB%E5%8A%A0%E8%BD%BD/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%8A%A0%E8%BD%BD/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E9%AA%8C%E8%AF%81/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%87%86%E5%A4%87/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E8%A7%A3%E6%9E%90/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%88%9D%E5%A7%8B%E5%8C%96/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E9%93%BE%E6%8E%A5/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/JPS/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/JStack/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/JMap/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/top/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Broker/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E8%87%AA%E5%8A%A8%E8%A3%85%E9%85%8D/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/AutoConfiguration/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Sharding-Jdbc/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/ThreadPool/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E7%BA%BF%E7%A8%8B%E6%B1%A0/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/FixedThreadPool/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/LimitedThreadPool/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/EagerThreadPool/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/CachedThreadPool/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/mvcc/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/read-view/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/gap-lock/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/next-key-lock/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%B9%BB%E8%AF%BB/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E7%B4%A2%E5%BC%95/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/is-null/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/is-not-null/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/procedure/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E7%BC%93%E5%AD%98%E7%A9%BF%E9%80%8F/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E7%BC%93%E5%AD%98%E5%87%BB%E7%A9%BF/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E7%BC%93%E5%AD%98%E9%9B%AA%E5%B4%A9/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/bloom-filter/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E4%BA%92%E6%96%A5%E9%94%81/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Design-Patterns/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%8D%95%E4%BE%8B/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Singleton/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Mac/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/PHP/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Homebrew/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/icu4c/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/zsh/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/ThreadLocal/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%BC%B1%E5%BC%95%E7%94%A8/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/WeakReference/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E6%97%85%E6%B8%B8/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%8E%A6%E9%97%A8/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E4%B8%AD%E5%B1%B1%E8%B7%AF/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%B1%80%E5%8F%A3%E8%A1%97/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E9%BC%93%E6%B5%AA%E5%B1%BF/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E6%9B%BE%E5%8E%9D%E5%9E%B5/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E6%A4%8D%E7%89%A9%E5%9B%AD/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E9%A9%AC%E6%88%8F%E5%9B%A2/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E6%B2%99%E8%8C%B6%E9%9D%A2/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E6%B5%B7%E8%9B%8E%E7%85%8E/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E6%89%B6%E6%A2%AF/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E8%B8%A9%E8%B8%8F/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%AE%89%E5%85%A8/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E7%94%B5%E7%93%B6%E8%BD%A6/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/Thread-dump/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E4%B8%A4%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E4%B8%89%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/2PC/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/3PC/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E8%BF%9C%E7%A8%8B%E5%8A%9E%E5%85%AC/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E9%AA%91%E8%BD%A6/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/tags/%E7%9C%8B%E5%89%A7/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/%E8%A3%85%E7%94%B5%E8%84%91/
- 2022-08-26
+ https://nicksxs.me/tags/%E6%8D%A2%E8%BD%A6%E7%89%8C/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/%E8%80%81%E7%94%B5%E8%84%91/
- 2022-08-26
+ https://nicksxs.me/tags/stream/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/360-%E5%85%A8%E5%AE%B6%E6%A1%B6/
- 2022-08-26
+ https://nicksxs.me/tags/zookeeper/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/%E4%BF%AE%E7%94%B5%E8%84%91%E7%9A%84/
- 2022-08-26
+ https://nicksxs.me/tags/%E7%9C%8B%E4%B9%A6/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/%E6%8D%A2%E8%BD%A6%E7%89%8C/
- 2022-08-26
+ https://nicksxs.me/tags/%E9%AB%98%E9%80%9F/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/stream/
- 2022-08-26
+ https://nicksxs.me/tags/%E5%A4%A7%E6%89%AB%E9%99%A4/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/zookeeper/
- 2022-08-26
+ https://nicksxs.me/tags/%E8%A3%85%E7%94%B5%E8%84%91/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/%E7%9C%8B%E4%B9%A6/
- 2022-08-26
+ https://nicksxs.me/tags/%E8%80%81%E7%94%B5%E8%84%91/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/%E9%AB%98%E9%80%9F/
- 2022-08-26
+ https://nicksxs.me/tags/360-%E5%85%A8%E5%AE%B6%E6%A1%B6/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/tags/%E5%A4%A7%E6%89%AB%E9%99%A4/
- 2022-08-26
+ https://nicksxs.me/tags/%E4%BF%AE%E7%94%B5%E8%84%91%E7%9A%84/
+ 2022-08-28
weekly
0.2
@@ -3624,1036 +3624,1036 @@
https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Java/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Java/JVM/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Java/leetcode/
- 2022-08-26
- weekly
- 0.2
-
-
-
- https://nicksxs.me/categories/leetcode/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/2020/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Java/GC/
- 2022-08-26
+ https://nicksxs.me/categories/leetcode/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Binary-Tree/
- 2022-08-26
+ https://nicksxs.me/categories/Java/GC/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/%E8%AF%BB%E5%90%8E%E6%84%9F/
- 2022-08-26
+ https://nicksxs.me/categories/Binary-Tree/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Linked-List/
- 2022-08-26
+ https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/2019/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/C/
- 2022-08-26
+ https://nicksxs.me/categories/Linked-List/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%B9%B4%E4%B8%AD%E6%80%BB%E7%BB%93/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/leetcode/java/
- 2022-08-26
+ https://nicksxs.me/categories/C/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/2020/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Java/%E5%B9%B6%E5%8F%91/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/java/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/%E8%AF%BB%E5%90%8E%E6%84%9F/%E6%9D%91%E4%B8%8A%E6%98%A5%E6%A0%91/
- 2022-08-26
+ https://nicksxs.me/categories/leetcode/java/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Java/Apollo/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Java/%E9%9B%86%E5%90%88/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Java/Dubbo/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%B9%B4%E4%B8%AD%E6%80%BB%E7%BB%93/2020/
- 2022-08-26
+ https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%B9%B4%E4%B8%AD%E6%80%BB%E7%BB%93/2021/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Filter/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/leetcode/java/Binary-Tree/
- 2022-08-26
+ https://nicksxs.me/categories/DP/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/DP/
- 2022-08-26
+ https://nicksxs.me/categories/leetcode/java/Binary-Tree/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/stack/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Java/Apollo/value/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/linked-list/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Java/leetcode/Lowest-Common-Ancestor-of-a-Binary-Tree/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/leetcode/java/Linked-List/
- 2022-08-26
- weekly
- 0.2
-
-
-
- https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/2019/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E5%AD%97%E7%AC%A6%E4%B8%B2-online/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Java/leetcode/Rotate-Image/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%B9%B4%E4%B8%AD%E6%80%BB%E7%BB%93/2021/
- 2022-08-26
+ https://nicksxs.me/categories/Linux/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Linux/
- 2022-08-26
+ https://nicksxs.me/categories/Interceptor-AOP/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Java/Maven/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Interceptor-AOP/
- 2022-08-26
+ https://nicksxs.me/categories/Redis/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/leetcode/java/Binary-Tree/DFS/
- 2022-08-26
+ https://nicksxs.me/categories/leetcode/java/DP/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/data-analysis/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/docker/
- 2022-08-26
+ https://nicksxs.me/categories/leetcode/java/Binary-Tree/DFS/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/leetcode/java/DP/
- 2022-08-26
+ https://nicksxs.me/categories/docker/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Docker/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/leetcode/java/stack/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E6%8C%81%E7%BB%AD%E9%9B%86%E6%88%90/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/leetcode/java/linked-list/
- 2022-08-26
- weekly
- 0.2
-
-
-
- https://nicksxs.me/categories/Redis/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Java/Mybatis/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/nginx/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/php/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/redis/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/leetcode/java/string/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Linux/%E5%91%BD%E4%BB%A4/
- 2022-08-26
+ 2022-08-28
+ weekly
+ 0.2
+
+
+
+ https://nicksxs.me/categories/Spring/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E8%AF%AD%E8%A8%80/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Spring/
- 2022-08-26
+ https://nicksxs.me/categories/Redis/Distributed-Lock/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Java/Spring/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E5%B0%8F%E6%8A%80%E5%B7%A7/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Java/gc/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E8%BF%90%E5%8A%A8/
- 2022-08-26
+ https://nicksxs.me/categories/%E8%AF%BB%E5%90%8E%E6%84%9F/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/MQ/
- 2022-08-26
+ https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E8%BF%90%E5%8A%A8/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/ssh/
- 2022-08-26
+ https://nicksxs.me/categories/MQ/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%90%90%E6%A7%BD/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%85%AC%E4%BA%A4/
- 2022-08-26
+ https://nicksxs.me/categories/Docker/%E4%BB%8B%E7%BB%8D/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/%E8%AF%BB%E5%90%8E%E6%84%9F/%E7%99%BD%E5%B2%A9%E6%9D%BE/
- 2022-08-26
+ https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%85%AC%E4%BA%A4/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/git/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Docker/%E4%BB%8B%E7%BB%8D/
- 2022-08-26
+ https://nicksxs.me/categories/shell/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/shell/
- 2022-08-26
+ https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%BD%B1%E8%AF%84/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%BD%B1%E8%AF%84/
- 2022-08-26
+ https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%B9%B4%E4%B8%AD%E6%80%BB%E7%BB%93/2020/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/%E8%AF%BB%E5%90%8E%E6%84%9F/%E7%94%9F%E6%B4%BB/
- 2022-08-26
+ https://nicksxs.me/categories/Mysql/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Redis/Distributed-Lock/
- 2022-08-26
+ https://nicksxs.me/categories/Java/Mybatis/Mysql/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Mysql/
- 2022-08-26
+ https://nicksxs.me/categories/Mybatis/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Mybatis/
- 2022-08-26
+ https://nicksxs.me/categories/Java/SpringBoot/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Java/SpringBoot/
- 2022-08-26
+ https://nicksxs.me/categories/ssh/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Java/Dubbo/RPC/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Dubbo-RPC/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Redis/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Java/Mybatis/Mysql/
- 2022-08-26
+ https://nicksxs.me/categories/Java/%E7%B1%BB%E5%8A%A0%E8%BD%BD/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Thread-dump/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Dubbo-%E7%BA%BF%E7%A8%8B%E6%B1%A0/
- 2022-08-26
+ https://nicksxs.me/categories/Linux/%E5%91%BD%E4%BB%A4/grep/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Redis/%E5%BA%94%E7%94%A8/
- 2022-08-26
+ https://nicksxs.me/categories/Dubbo-%E7%BA%BF%E7%A8%8B%E6%B1%A0/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Java/Design-Patterns/
- 2022-08-26
+ https://nicksxs.me/categories/Redis/%E5%BA%94%E7%94%A8/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Mac/
- 2022-08-26
+ https://nicksxs.me/categories/Java/Design-Patterns/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/SpringBoot/
- 2022-08-26
+ https://nicksxs.me/categories/Mac/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E6%97%85%E6%B8%B8/
- 2022-08-26
+ https://nicksxs.me/categories/Spring/Servlet/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Linux/%E5%91%BD%E4%BB%A4/grep/
- 2022-08-26
+ https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E6%97%85%E6%B8%B8/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E8%AF%AD%E8%A8%80/Rust/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%BC%80%E8%BD%A6/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Rust/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Spring/Servlet/
- 2022-08-26
+ https://nicksxs.me/categories/C/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Java/%E7%B1%BB%E5%8A%A0%E8%BD%BD/
- 2022-08-26
+ https://nicksxs.me/categories/SpringBoot/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Java/gc/jvm/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/MQ/RocketMQ/
- 2022-08-26
+ https://nicksxs.me/categories/%E8%AF%BB%E5%90%8E%E6%84%9F/%E7%94%9F%E6%B4%BB/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/ssh/%E6%8A%80%E5%B7%A7/
- 2022-08-26
+ https://nicksxs.me/categories/%E8%AF%BB%E5%90%8E%E6%84%9F/%E6%9D%91%E4%B8%8A%E6%98%A5%E6%A0%91/
+ 2022-08-28
+ weekly
+ 0.2
+
+
+
+ https://nicksxs.me/categories/MQ/RocketMQ/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%90%90%E6%A7%BD/%E7%96%AB%E6%83%85/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/%E8%AF%BB%E5%90%8E%E6%84%9F/%E7%99%BD%E5%B2%A9%E6%9D%BE/%E5%B9%B8%E7%A6%8F%E4%BA%86%E5%90%97/
- 2022-08-26
+ https://nicksxs.me/categories/%E8%AF%BB%E5%90%8E%E6%84%9F/%E7%99%BD%E5%B2%A9%E6%9D%BE/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/git/%E5%B0%8F%E6%8A%80%E5%B7%A7/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/shell/%E5%B0%8F%E6%8A%80%E5%B7%A7/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E8%BF%90%E5%8A%A8/%E8%B7%91%E6%AD%A5/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%BD%B1%E8%AF%84/2020/
- 2022-08-26
+ https://nicksxs.me/categories/Linux/%E5%91%BD%E4%BB%A4/echo/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Linux/%E5%91%BD%E4%BB%A4/echo/
- 2022-08-26
+ https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%BD%B1%E8%AF%84/2020/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/C/
- 2022-08-26
+ https://nicksxs.me/categories/Mysql/Sql%E6%B3%A8%E5%85%A5/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Mysql/Sql%E6%B3%A8%E5%85%A5/
- 2022-08-26
+ https://nicksxs.me/categories/Mybatis/%E7%BC%93%E5%AD%98/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Mybatis/%E7%BC%93%E5%AD%98/
- 2022-08-26
+ https://nicksxs.me/categories/ssh/%E6%8A%80%E5%B7%A7/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Java/Dubbo/RPC/SPI/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Dubbo/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Redis/%E6%BA%90%E7%A0%81/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Linux/%E5%91%BD%E4%BB%A4/top/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E9%97%AE%E9%A2%98%E6%8E%92%E6%9F%A5/
- 2022-08-26
+ 2022-08-28
+ weekly
+ 0.2
+
+
+
+ https://nicksxs.me/categories/%E5%B0%8F%E6%8A%80%E5%B7%A7/grep/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Mysql/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Mysql/%E7%B4%A2%E5%BC%95/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Redis/%E7%BC%93%E5%AD%98/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Java/Singleton/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Mac/PHP/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/%E5%B0%8F%E6%8A%80%E5%B7%A7/grep/
- 2022-08-26
+ https://nicksxs.me/categories/Spring/Servlet/Interceptor/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/%E4%B8%A4%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Spring/Servlet/Interceptor/
- 2022-08-26
+ https://nicksxs.me/categories/C/Redis/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%90%90%E6%A7%BD/%E7%96%AB%E6%83%85/%E7%BE%8E%E5%9B%BD/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E5%90%90%E6%A7%BD/%E7%96%AB%E6%83%85/%E5%8F%A3%E7%BD%A9/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E8%BF%90%E5%8A%A8/%E8%B7%91%E6%AD%A5/%E5%B9%B2%E6%B4%BB/
- 2022-08-26
+ https://nicksxs.me/categories/%E8%AF%BB%E5%90%8E%E6%84%9F/%E7%99%BD%E5%B2%A9%E6%9D%BE/%E5%B9%B8%E7%A6%8F%E4%BA%86%E5%90%97/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Docker/%E5%8F%91%E8%A1%8C%E7%89%88%E6%9C%AC/
- 2022-08-26
+ https://nicksxs.me/categories/%E7%94%9F%E6%B4%BB/%E8%BF%90%E5%8A%A8/%E8%B7%91%E6%AD%A5/%E5%B9%B2%E6%B4%BB/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/MQ/RocketMQ/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/
- 2022-08-26
+ https://nicksxs.me/categories/Docker/%E5%8F%91%E8%A1%8C%E7%89%88%E6%9C%AC/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/C/Redis/
- 2022-08-26
+ https://nicksxs.me/categories/Spring/Mybatis/
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/Spring/Mybatis/
- 2022-08-26
+ https://nicksxs.me/categories/MQ/RocketMQ/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Dubbo/SPI/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Dubbo/%E5%AE%B9%E9%94%99%E6%9C%BA%E5%88%B6/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E5%B0%8F%E6%8A%80%E5%B7%A7/top/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E5%B7%A5%E5%85%B7/
- 2022-08-26
+ 2022-08-28
+ weekly
+ 0.2
+
+
+
+ https://nicksxs.me/categories/%E5%B0%8F%E6%8A%80%E5%B7%A7/grep/%E6%9F%A5%E6%97%A5%E5%BF%97/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Mysql/%E6%BA%90%E7%A0%81/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Dubbo/%E7%BA%BF%E7%A8%8B%E6%B1%A0/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/C/Mysql/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E7%BC%93%E5%AD%98/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Mac/Homebrew/
- 2022-08-26
+ 2022-08-28
weekly
0.2
- https://nicksxs.me/categories/%E5%B0%8F%E6%8A%80%E5%B7%A7/grep/%E6%9F%A5%E6%97%A5%E5%BF%97/
- 2022-08-26
+ https://nicksxs.me/categories/Spring/Servlet/Interceptor/AOP/
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/%E4%B8%89%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4/
- 2022-08-26
- weekly
- 0.2
-
-
-
- https://nicksxs.me/categories/Spring/Servlet/Interceptor/AOP/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/RocketMQ/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E5%B0%8F%E6%8A%80%E5%B7%A7/top/%E6%8E%92%E5%BA%8F/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Dubbo/%E7%BA%BF%E7%A8%8B%E6%B1%A0/ThreadPool/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E7%BC%93%E5%AD%98/%E7%A9%BF%E9%80%8F/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/PHP/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/Dubbo/SPI/Adaptive/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E7%BC%93%E5%AD%98/%E7%A9%BF%E9%80%8F/%E5%87%BB%E7%A9%BF/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/PHP/icu4c/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/RocketMQ/
- 2022-08-26
+ 2022-08-28
weekly
0.2
https://nicksxs.me/categories/%E7%BC%93%E5%AD%98/%E7%A9%BF%E9%80%8F/%E5%87%BB%E7%A9%BF/%E9%9B%AA%E5%B4%A9/
- 2022-08-26
+ 2022-08-28
weekly
0.2
diff --git a/tags/java/page/3/index.html b/tags/java/page/3/index.html
index 441cad4f2d..0a551d8775 100644
--- a/tags/java/page/3/index.html
+++ b/tags/java/page/3/index.html
@@ -1 +1 @@
-标签: Java | Nicksxs's Blog
0%
Theme NexT works best with JavaScript enabled
\ No newline at end of file
+标签: java | Nicksxs's Blog
0%
Theme NexT works best with JavaScript enabled
\ No newline at end of file