MySQL 的容器化部署与数据库初始化

01 概述 1.1 环境 CentOS 8 $ docker --version Docker version 20.10.11, build dea9396 $ docker-compose --version Docker Compose version v2.16.0 1.2 Docker 安装 MySQL 的初始化原理 MySQL Docker 官方介绍:Initializing a refsh instance When a container is started for the first time, a new database with the specified name will be created and initialized with the provided configuration variables. Furthermore, it will execute files with extensions ++.sh, .sql and .sql.gz++ that are found in /docker-entrypoint-initdb.d. Files will be executed in alphabetical order. You can easily populate your mysql services by mounting a SQL dump into that directory and provide custom images with contributed data. SQL files will be imported by default to the database specified by the MYSQL_DATABASE variable. ...

May 20, 2023 · 2 min · 417 words · Spring

Windows 的路由配置

00 Windows 下的路由 路由是什么? 路由是指在计算机网络中将数据包从源地址传输到目标地址的过程,通过路由器根据网络拓扑和路由表中的信息进行决策和转发。 Windows 的路由命令格式如下: Manipulates network routing tables. 操作网络路由表 ❯ route /? ROUTE [-f] [-p] [-4|-6] command [destination] [MASK netmask] [gateway] [METRIC metric] [IF interface] -f:清除所有路由表项。如果与其中一个命令一起使用,则在运行命令之前清除表。 -p:与 ADD 命令结合使用,标识添加为永久路由 METRIC:跃点数,网络接口顺序,值越小越优先(会自动调整) command:命令,有四个可选命令,分别对应路由的增删改查 PRINT:打印路由 ADD:添加路由 DELETE:删除路由 CHANGE:修改路由 01 查看路由表(print) 查看路由的命令格式如下: route print:查看所有 route print -4:查看 ipv4 路由表 route print -6:查看 ipv6 路由表 route print 192.168.*:查看匹配的路由 示例:查看 172.16.30 相关的路由信息 ❯ route print -4 172.16.30* =========================================================================== Interface List 48...00 15 00 00 00 00 ......Hyper-V Virtual Ethernet Adapter 12...50 eb 00 00 00 00 ......Intel(R) Wireless-AC 9462 1...........................Software Loopback Interface 1 =========================================================================== IPv4 Route Table =========================================================================== Active Routes: # 动态路由表 Network Destination Netmask Gateway Interface Metric 172.16.30.0 255.255.254.0 On-link 172.16.30.76 291 172.16.30.76 255.255.255.255 On-link 172.16.30.76 291 =========================================================================== Persistent Routes: # 永久路由表 None On-link:在链路上,即目标 ip(Network Destination)与接口(Interface)在同一网段 Interface List 中,前面的数字为「接口号码」,接下来是 MAC 地址,后面是接口名称 02 添加路由(add) 添加路由的命令格式如下,如果没有给定 IF,则默认为 gateway 查找一个最合适的接口: ...

May 14, 2023 · 2 min · 323 words · Spring

Windows 的端口代理配置(端口映射)

在调试本地程序,或者需要把 WSL 的服务暴露出去,使用端口映射会更加方便。 Windows 的端口映射命令格式如下: ❯ netsh interface portproxy The following commands are available: Commands in this context: show - Displays information. add - Adds a configuration entry to a table. delete - Deletes a configuration entry from a table. dump - Displays a configuration script. reset - Resets portproxy configuration state. set - Sets configuration information. portproxy:端口代理,它是在强大的网络管理工具 netsh 中,命令本身比较简单,主要就是端口代理的增删改查。 01 查看映射(show) show 可以查看已存在的端口转发规则,没有给定参数会输出以下帮助信息: ❯ netsh interface portproxy show The following commands are available: Commands in this context: show all - Shows all port proxy parameters. show v4tov4 - Shows parameters for proxying IPv4 connections to another IPv4 port. show v4tov6 - Shows parameters for proxying IPv4 connections to IPv6. show v6tov4 - Shows parameters for proxying IPv6 connections to IPv4. show v6tov6 - Shows parameters for proxying IPv6 connections to another IPv6 port. show 命令需要指定查看范围,all 表示查看所有,其他的顾名思义表示 ipv4 和 ipv6 之间的转发规则: ❯ netsh interface portproxy show all Listen on ipv4: Connect to ipv4: Address Port Address Port --------------- ---------- --------------- ---------- * 1024 192.168.198.200 1024 02 添加映射(add) add 用于添加端口转发规则,需要指定 ipv4/ipv6 转发类型: ❯ netsh interface portproxy add help The following commands are available: Commands in this context: add v4tov4 - Adds an entry to listen on for IPv4 and proxy connect to via IPv4. add v4tov6 - Adds an entry to listen on for IPv4 and proxy connect to via IPv6. add v6tov4 - Adds an entry to listen on for IPv6 and proxy connect to via IPv4. add v6tov6 - Adds an entry to listen on for IPv6 and proxy connect to via IPv6. 具体用法如下: ❯ netsh interface portproxy add v4tov4 help Usage: add v4tov4 [listenport=]<integer>|<servicename> [connectaddress=]<IPv4 address>|<hostname> [[connectport=]<integer>|<servicename>] [[listenaddress=]<IPv4 address>|<hostname>] [[protocol=]tcp] Parameters: Tag Value listenport - IPv4 port on which to listen.(必须) connectaddress - IPv4 address to which to connect.(必须) connectport - IPv4 port to which to connect.(必须) listenaddress - IPv4 address on which to listen.(可选,默认所有 *) protocol - Protocol to use. Currently only TCP is supported.(目前仅支持 TCP,不用填) 示例:把端口 1025 的流量转发到 wsl 的 1025 端口上,让外部机器能直接访问到 wsl 子系统(默认到 0.0.0.0) ...

May 14, 2023 · 3 min · 592 words · Spring

GBK 和 Unicode 之间的转换问题——“锟斤拷”的由来

GBK 和 Unicode 之间的转换问题 1. GBK 被解密为 UTF-8 正常的 GBK 字节流,以为是 UTF-8,所以用 UTF-8 去解码: 输出特点:一堆的黑色菱形+问号 输出示例:�Ҳ����� @Test public void test() throws UnsupportedEncodingException { String str = "我不是锟斤拷"; byte[] buff = str.getBytes("GBK"); // 这里只要不抛异常,数据一定不会被破坏 String str1 = new String(buff, "UTF-8"); } 2. UTF-8 被解密为 GBK 正常的 UTF-8 字节流,以为是 GBK,所以用 GBK 去解码: 输出示例:鎴戜笉鏄敓鏂ゆ嫹 @Test public void test() throws UnsupportedEncodingException { String str = "我不是锟斤拷"; byte[] buff = str.getBytes("UTF-8"); // 这里只要不抛异常,数据一定不会被破坏 String str1 = new String(buff, "GBK"); // 这里破坏了 } 3. “锟斤拷"的由来 正常的 GBK 字节流,中途被 UTF-8 解码了,又用 GBK 解码一遍: ...

May 25, 2022 · 1 min · 141 words · Spring

Redis 的缓存穿透、缓存击穿和缓存雪崩问题

从第一个缓存框架 Memcached 诞生以来,缓存就广泛地存在于互联网应用中。如果你的应用流量很小,那么使用缓存可能并不需要做多余的考虑。但如果你的应用流量达到了成百上千万,那么你就不得不考虑深层次的缓存问题:缓存穿透、缓存击穿与缓存雪崩。 缓存穿透 缓存穿透是指查询一个一定不存在的数据,因为这个数据不存在,所以永远不会被缓存,所以每次请求都会去请求数据库。 例如我们请求一个 UserID 为 -1 的用户数据,因为该用户不存在,所以该请求每次都会去读取数据库。在这种情况下,如果某些心怀不轨的人利用这个存在的漏洞去伪造大量的请求,那么很可能导致DB承受不了那么大的流量就挂掉了。 对于缓存穿透,有几种解决方案,一种是事前预防,一种是事后预防。 事前预防 事前预防其实就是对所有请求都进行参数校验,把绝大多数非法的请求抵挡在最外层。在我们举的这个例子中,那么就是做参数校验,对于 UserID 小于 0 的请求全部拒绝。但即使我们做了全面的参数校验,还是可能存在漏网之鱼,会出现一些我们没想到的情况。 例如我们的 UserID 是递增的,那么如果有人请求一个 UserID 很大的用户信息(例如:1000000),而我们的 UserID 最大也就 10000。这个时候,你不可能限制 UserID 大于 1 万的就是非法的,或者说大于 10 万就是非法的,所以该用户ID肯定可以通过参数校验。但该用户确实不存在,所以每次请求都会去请求数据库。 其实上面只是我所能想到的一种情况,我们没想到的情况肯定还有很多。对于这些情况,我们能做的就是事后预防。 事后预防 事后预防说的就是当查询到一个空的结果时,我们仍然将这个空的结果进行缓存,但是设置一个很短的过期时间(例如一分钟),但是这种办法还是没办法预防非常多的非法值。 另外一个比较有效的办法是,将这个字段里在数据库中的所有值存在布隆过滤器中。当一个查询请求过来时,先经过布隆过滤器进行查,如果判断请求查询值存在,则继续查数据库。如果判断请求查询不存在,直接丢弃。 通过上面这两种处理方式,我们基本可以解决缓存穿透的问题。事前预防解决 80% 的非法请求,剩下的 20% 非法请求则使用 Redis 转移风险。 缓存击穿 如果你的应用中有一些访问量很高的热点数据,我们一般会将其放在缓存中以提高访问速度。另外,为了保持时效性,我们通常还会设置一个过期时间。但是对于这些访问量很高的KEY,我们需要考虑一个问题:当热点KEY在失效的瞬间,海量的请求会不会产生大量的数据库请求,从而导致数据库崩溃? 例如我们有一个业务 KEY,该 KEY 的并发请求量为 10000。当该 KEY 失效的时候,就会有 1 万个线程会去请求数据库更新缓存。这个时候如果没有采取适当的措施,那么数据库很可能崩溃。 其实上面这个问题就是缓存击穿的问题,它发生在缓存 KEY 的过期瞬间。对于这种情况,现在常用的解决方式有这么两种:互斥锁、永远不过期。 互斥锁 互斥锁指的是在缓存 KEY 过期去更新的时候,先让程序去获取锁,只有获取到锁的线程才有资格去更新缓存 KEY。其他没有获取到锁的线程则休眠片刻之后再次去获取最新的缓存数据。通过这种方式,同一时刻永远只有一个线程会去读取数据库,这样也就避免了海量数据库请求对于数据库的冲击。 而对于上面说到的锁,我们可以使用缓存提供的一些原则操作来完成。例如对于 redis 缓存来说,我们可以使用其 SETNX 命令来完成。 public String get(key) { String value = redis.get(key); if (value == null) { //缓存过期 if (redis.setnx(key_mutex, 1, 1 * 60) == 1) { value = db.get(key); redis.set(key, value, expireTime); redis.del(key_mutex); } else { //休眠片刻后重试 sleep(50); get(key); } } else { return value; } } 上面的 key_mutex 其实就是一个普通的 KEY-VALUE 值,我们使用 setnx 命令去设置其值为 1。如果这时候已经有人在更新缓存 KEY 了,那么 setnx 命令会返回 0,表示设置失败。 ...

December 27, 2021 · 1 min · 200 words · 陈树义

curl 于 wget 的区别(下载文件)

01 curl 和 wget 的区别 wget 是个专职的下载利器,简单,专一,极致; curl 可以下载,但是长项不在于下载,而在于模拟提交 web 数据,POST/GET 请求,调试网页,等等。 02 具体使用(下载) 下载文件 curl -O http://man.linuxde.net/text.iso # O大写,不用O只是打印内容不会下载 wget http://www.linuxde.net/text.iso # 不用参数,直接下载文件 下载文件并重命名 curl -o rename.iso http://man.linuxde.net/text.iso # o小写 wget -O rename.zip http://www.linuxde.net/text.iso # O大写 断点续传 curl -O -C - http://man.linuxde.net/text.iso # O大写;C大写,- 表示不指定续传的偏移量,默认从本地文件计算 wget -c http://www.linuxde.net/text.iso # c小写 限速下载 curl --limit-rate 50k -O http://man.linuxde.net/text.iso wget --limit-rate=50k http://www.linuxde.net/text.iso 显示响应头部信息 curl -I http://man.linuxde.net/text.iso wget --server-response http://www.linuxde.net/test.iso wget 利器 → 打包下载网站 wget --mirror -p --convert-links -P /var/www/html http://man.linuxde.net/ References [1] https://www.zhihu.com/question/19598302 [2] https://www.cnblogs.com/lsdb/p/7171779.html ...

May 11, 2021 · 1 min · 83 words · Spring

Java 8 Functional 函数式接口

01 函数式接口 1.1 什么是函数式接口 函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。 函数式接口可以被隐式转换为 Lambda 表达式。 Lambda 是一个匿名函数,返回一个实现了接口的对象。方法引用实际上也可认为是 Lambda 表达式(简化版)。 1.2 自定义函数式接口 如果要实现自己的函数式接口,步骤很简单。 首先,定义一个接口,并声明一个抽象方法。然后给接口添加注解 @FunctionalInterface,该接口即为函数式接口: @FunctionalInterface interface Converter<T, R> { R convert(T from); } 使用同样也很简单,创建一个 Lambda 表达式(要实现的方法)并赋值给该接口的一个变量,然后直接调用定义的那个抽象方法: // 创建一个函数:字符串转整型 Converter<String, Integer> converter = (from) -> Ingeter.valueOf(from); converter.convert("123") // 123 02 Java(8)提供的函数式接口 2.1 Converter<T, R>,如上 2.2 Predicate 作用:断言,判断是与非。 调用方式:predicate.test(OBJ)。 示例: Predicate<String> predicate = (s) -> s.length() > 0; predicate.test("foo"); // true predicate.negate().test("foo"); // false 2.3 Function<T, R> 作用:接口一个参数,返回一个结果,可以链式调用。 ...

November 24, 2020 · 2 min · 259 words · Spring

MySQL 去重之 distinct

在使用 MySQL 时,有时需要查询出某个字段不重复的记录,这时可以使用 MySQL 提供的 distinct 这个关键字来过滤重复的记录[1] 。 语法格式: select distinct expression[,expression...] from tables [where conditions]; 01 distinct 的用法 例如,我们有表如下: mysql> select * from user; +----+----------+----------+ | id | username | password | +----+----------+----------+ | 1 | taylor | pass123 | | 2 | spring | pass456 | | 3 | Yahto | taf23 | | 4 | Lillie | flowwer | | 5 | Lia | wherend | | 6 | Lia2 | wherend | | 7 | Lia3 | wherend | | 8 | Lia4 | wherend | +----+----------+----------+ 1.1 简单的用法 用 distinct 返回不重复的密码: ...

August 22, 2020 · 3 min · 435 words · Spring

GO 语言快速入门

00 Go 环境配置 首先,下载 Golang 安装包,地址:https://go.dev/dl/。 接着,将 Golang 安装包解压到指定路径。 然后,把 Go 安装路径配置到环境变量。有如下两个环境变量: GOROOT:可选,主要是将 $GOROOT/bin 配置到环境变量中以便直接使用 go 命令及其工具等 GOPATH:Go 1.11 之后引入 Go Modules,不再完全依赖 GOPATH 了 Go 1.11 之前,go get 下载的包会被存放在 $GOPATH/src 目录下,与项目无关 Go 1.11 之后(使用了 Go Modules),依赖包会被存放在项目的 go.mod 指定的缓存目录中,默认情况下是 $GOPATH/pkg/mod 01 Go 基础 1.1 变量 类型:放在变量名后面 变量声明:var NAME TYPE 简单变量声明:NAME := VALUE(只允许在函数内部使用,且不能用于声明静态变量) 数据类型 bool string // 不出初始化,则默认空字符串:"" int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr byte // alias for uint8 rune // alias for int32 // represents a Unicode code point float32 float64 complex64 complex128 // 复数 var x complex128 = complex(1, 2) // 1+2i var y complex128 = complex(3, 4) // 3+4i fmt.Println(x*y) // "(-5+10i)" fmt.Println(real(x*y)) // "-5" fmt.Println(imag(x*y)) // "10" 1.2 循环 Go 只有 for 循环,且格式固定: ...

August 16, 2020 · 10 min · 1920 words · Spring

Learning programming is different from learning a programming language

We are all programmers, and we are all learners. It’s surprising to see how many people(learners) who are bashing their head against the walls thinking they are learning to program. You might be learning a programming language instead of programming itself Don’t feel surprised to know that computer science does not study computers. Instead, it’s the study of automated problem solving using computers. Problem-solving is computer science, not programming. That’s why many computer science students seem not to understand why they learn algorithm or mathematics. ...

March 20, 2019 · 5 min · 891 words · zooboole