新项目使用了 Reids 的过期监听来执行延时任务,在不搭建 redis 高可用集群的情况下,还是想尽量保证 Redis 数据不丢失,于是就打算把 Redis 的持久化配置上,研究了下相关资料,这里做下记录:

在配置 Redis 的持久化时,通常会选择 RDB(Redis Database)快照和 AOF(Append Only File)日志的组合,以兼顾性能和数据持久性。以下是一个合理的混合持久化配置示例:

1. RDB 配置

RDB 是 Redis 的快照持久化机制,它会在特定时间间隔内保存数据快照到磁盘。RDB 配置如下:

# RDB 持久化间隔配置(在满足条件时生成快照)
save 900 1  # 900 秒(15 分钟)内至少有 1 个键被修改
save 300 10 # 300 秒(5 分钟)内至少有 10 个键被修改
save 60 10000 # 60 秒内至少有 10000 个键被修改

# RDB 文件名称(可选)
dbfilename dump.rdb

# RDB 文件保存路径(可选)
dir /var/lib/redis

2. AOF 配置

AOF 是 Redis 的追加日志持久化机制,它会记录每个写操作,以便在重启时重放这些操作。AOF 配置如下:

# 启用 AOF 持久化
appendonly yes

# AOF 文件名称
appendfilename "appendonly.aof"

# AOF 写入策略
appendfsync everysec # 每秒同步一次

# AOF 重写配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

3. 混合持久化策略

结合 RDB 和 AOF 的优点,可以配置如下:

# 混合持久化策略
aof-use-rdb-preamble yes

启用 aof-use-rdb-preamble 选项后,AOF 文件在重写时会包含一个 RDB 预备文件,这样可以加快重启速度,并减少 AOF 文件的大小。

4. 完整配置示例

# RDB 配置
save 900 1
save 300 10
save 60 10000
dbfilename dump.rdb
dir /var/lib/redis

# AOF 配置
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

# 混合持久化策略
aof-use-rdb-preamble yes

JVM 就是 java 虚拟机,我们可以把它理解成一个操作系统,每个不同的平台都有不同的 JVM,比如 Linux 系统和 Windows 系统,就是因为这个原因所以 Java 程序就有了一个很突出的特性就是 跨平台性

:在jvm中栈用来存储一些对象的引用、局部变量以及计算过程的中间数据,在方法退出后那么这些变量也会被销毁。它的存储比堆快得多,只比 CPU 里的寄存器慢

:用来存储程序中的一些对象,比如你用 new 关键字创建的对象,它就会被存储在堆内存中,但是这个对象在堆内存中的首地址会存储在栈中。

栈内存在 JVM 中默认是 1M,可以通过下面的参数进行设置:

-Xss

最小堆内存在 JVM 中默认物理内存的 64 分之 1,最大堆内存在 JVM 中默认物理内存 4 分之一,且建议最大堆内存不大于 4G,并且设置 -Xms=-Xmx 避免每次 GC 后,调整堆的大小,减少系统内存分配开销:

-Xms  
-Xmx

在 jvm 的堆内存中有三个区域

1.年轻代:用于存放新产生的对象。
2.老年代:用于存放被长期引用的对象。
3.持久带:用于存放 Class,method元信息。

JVM 内存垃圾收集算法

  • 引用计数器的方法
    类似于 Objective-C 的内存回收,在 OC 语言不使用 arc 机制时,在创建对象,且被引用时候也会对计数器加 1,使用完成后会调用 release 方法就会对计数器减 1

jvm 的这个算法也和上面的机制类似,但是他不能解决对象循环引用问题,也不好解决精确的计算,因为 java 程序开发,内存回收是对我们透明的,而 OC 是在代码层面自己去手动控制

  • 根搜索算法

有向图算法,从 GC Roots 开始向下面搜索,搜索走过的路径叫做引用链接,当一个对象到达 GC Roots 没有任何引用链时,则这个对象就是不可达对象,就可以被回收,但是回收的时候会筛查出覆盖了 finalze() 方法且该对象 finalze() 方法没有被虚拟机调用过放入 F-Queue 队列然后执行 F-Queue 队列中对象的 finalze() 方法后如果该对象重新与某个 GC Roots 对象相关联,那么会将该对象从回收队列中移除

比如 jvm 中栈中指向堆中对象的指针就可以理解成一种 GC Roots,因为栈中保存的是对象的指针指向的是堆中对象的首地址,当栈中的指针没有了那么堆中的对象就是不可达对象,就会被GC回收

jvm内存垃圾回收算法

  • 复制算法 (用于新生代)

用于新生代从 Eden 区到 survivor0 或者 survivor1 移动的时候

从根集合扫描如果对象被引用,就会被 copy 到 survivor0 或者 survivor1,然后剩下的都是不可达对象,就可以被回收掉,然后 S0 或者 S1 的对象就会到老年代中,在存活对象比较少的时候很高效并且不会产生内存碎片,就是内存需要额外划分一块 survivor 区出来

  • 标记清除算法 (用于老年代)

就是使用根搜索算法扫描,如果可达就标记,当扫描完成后,就对未标记的对象进行回收,它不需要像复制算法一样,需要一个新的内存区,而且不会对对象进行 copy,这种对对象存活的多的情况下很高效,但是这样会产生内存碎片

  • 标记整理压缩算法 (用于老年代)

就是在标记清除算法之后对内存碎片进行整理,只是他会对没有清理的对象进行移动,代价高

1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。

2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,

如:select id from t where num is null可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:select id from t where num=0

3.应尽量避免在 where 子句中使用!=或<>操作符,否则引擎将放弃使用索引而进行全表扫描。

4.应尽量避免在 where 子句中使用or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,

如:select id from t where num=10 or num=20可以这样查询:select id from t where num=10 union all select id from t where num=20

5.in 和 not in 也要慎用,否则会导致全表扫描,

如:select id from t where num in(1,2,3) 对于连续的数值,能用 between 就不要用 in 了:select id from t where num between 1 and 3

6.下面的查询也将导致全表扫描:select id from t where name like '李%'若要提高效率,可以考虑全文检索。

7..避免在索引列上使用计算,也就是说,应尽量避免在 where 子句中对字段进行表达式操作和函数操作,这将导致引擎放弃使用索引而进行全表扫描。

如:select id from t where num/2=100应改为:select id from t where num=100*2

select id from t where substring(name,1,3)='abc' ,name以abc开头的id,应改为:select id from t where name like 'abc%'

8.很多时候用 exists 代替 in 是一个好的选择:exists用于检查子查询是否至少会返回一行数据,该子查询实际上并不返回任何数据,而是返回值true或false。

select num from a where num in(select num from b)

用下面的语句替换:select num from a where exists (select 1 from b where num=a.num)

9.任何地方都不要使用 select from t ,用具体的字段列表代替“”,不要返回用不到的任何字段。

10.用>=替代>

高效: SELECT * FROM EMP WHERE DEPTNO >=4

低效: SELECT * FROM EMP WHERE DEPTNO >3

两者的区别在于, 前者DBMS将直接跳到第一个DEPT等于4的记录,而后者将首先定位到DEPTNO=3的记录并且向前扫描到第一个DEPT大于3的记录。

11.用Where子句替换having子句

问题出现在前司的项目上,虽然已经离职一年多了,但有问题经常会找到我帮忙处理,并支付一点辛苦费。

项目是采用 springCloud 微服务创建的,目前有五六个服务分布在几台服务器上。

这次的问题现象是,用户创建订单后,会莫名其妙自动取消,明白问题后,首先检查该问题订单的服务日志,日志一切正常,正常下单,正常支付,正常修改订单状态。

然后检查代码,发现在另一个服务上,有一个定时任务,采用 spring 的定时任务,通过时间轮来每分钟读取数据库,获取超过设定的事件后还没有支付的订单,然后取消,而这个业务的自动取消设置的未支付 3 分钟后自动取消。顺便提一下,我感觉这种每分钟读取一次数据库来取消订单的做法有点 low,当时在职的时候想去优化一下,但一直没时间,直到拖了我三个月工资后,我提桶跑路(代码和人有一个能跑就行)。

然而这个跑定时任务的服务在另一台服务器上,检查了定时任务的日志,发现定时任务读取未支付订单时,把这单已经支付了的订单给读取出来了,我觉得很不可思议,数据库里检查了一下订单支付时间和订单的取消时间,竟然相差 3 分钟,也就是说,这个订单在支付完成三分钟后,定时任务读取未支付订单把它读取出来了,并做了取消订单的操作。

我又检查了几遍日志和代码,发现代码确实没有问题,订单被取消前,也没有其他的地方修改订单的状态,但一个支付成功的订单就这么被识别为未支付的订单了,这就奇怪了,花费了两三个小时反复检查日志和代码后,我给前司对接的人说,这大概是个灵异事件。

前司同事提议要不换成测试环境再试试,我说可以,然后切换到测试服务器准备查看实时日志,过了一会儿,前司同事给我发来一个截图,上面是新下的一个订单,她特别标记出来了下单时间,和她当时的系统时间,竟然对不上,下单时间竟然比当前时间还早三分钟。

我一下想到一个可能,我去那台业务服务器上查看了当前时间,又去定时任务那台服务器看了下时间,竟然相差 3 分钟,而且定时任务的服务器时间是正确的北京时间,定时任务服务器的时间比北京时间慢了 3 分钟!

一下子就都说得通了,这三分钟的时间差让我耗了两个小时检查代码和日志,业务服务器下单后,下单时间比实际的当前时间早了三分钟,在用户支付的间隙,正常时间服务器跑的定时任务读取超过三分钟的订单自动取消,读取到了这个订单,就造成了用户那边支付完成,定时任务又执行读取好的订单列表去取消。

真相大白后,我准备修改时间不对的服务器时间,检查时区,发现这台服务器的时区是:Time zone: Asia/Shanghai (CST, +0800),而那台正常时间的服务器,时区也是:Time zone: Asia/Shanghai (CST, +0800),但同一个时区,两台服务器获取到的 Universal time 竟然会相差三分钟,网上搜索了这个问题,发现并没有搜索到结果,只好手动卡点设置时间与北京时间同步,如果有知道这个问题原因的小伙伴,麻烦给我解惑一下,谢谢~

一、创建start.bat文件

二、以文本编辑打开文件,编辑如下命令

@echo off
%1 mshta vbscript:CreateObject("WScript.Shell").Run("%~s0 ::",0,FALSE)(window.close)&&exit
java -jar xxx.jar > log.log 2>&1 &
exit

三、如果要指定配置文件,配置Jvm 环境,则编辑如下命令

@echo off
%1 mshta vbscript:CreateObject("WScript.Shell").Run("%~s0 ::",0,FALSE)(window.close)&&exit
set JAVA_OPTS=-Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -jar xxx.jar  --spring.config.location=./conf/application.yml  >  log.log 2>&1 &
exit

四、创建stop.bat 关闭特定端口的java程序

@echo off
set port=xxx(项目端口号)
for /f "tokens=1-5" %%i in ('netstat -ano^|findstr ":%port%"') do taskkill /f /pid %%m

五、创建restart.bat ,重启jar包

@echo off
%1 mshta vbscript:CreateObject("WScript.Shell").Run("%~s0 ::",0,FALSE)(window.close)&&exit
set port=xxx(项目端口号)
for /f "tokens=1-5" %%i in ('netstat -ano^|findstr ":%port%"') do taskkill /f /pid %%m &
set JAVA_OPTS=-Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m
java -jar xxx.jar  --spring.config.location=./conf/application.yml  >  log.log 2>&1 &
exit

原文地址:https://blog.csdn.net/Crazy_Cw/article/details/125690560

为啥要改?

Docker安装后默认下载的位置在/var/lib/docker ,如果/var分区没有独立分出来,Linux下默认是与/根分区在一起。一般我们装Linux系统的时候,除了做邮件服务器外,都不会把/var分区独立分出来,而且/分区一般不会太大,比如我现在用的这台根分区50G的,在拉镜像的时候提示硬盘空间不足的问题,而其它分区还有很大空间。基于此情此景,我们都要把这个目录改一下

查看当前Docker目录位置

#展示当前docker的配置信息
docker info
-------------------------------------------------------------------
#在信息找到Docker Root Dir,对应的就是了,默认为:
Docker Root Dir: /var/lib/docker

几种改法

注意以下几种方式是互斥的,我折腾了一会,才发现,如你已经注意到,那此坑已平。

1、最简单也是最暴力的方式——修改 /etc/systemd/system/multi-user.target.wants/docker.service

sudo vim /etc/systemd/system/multi-user.target.wants/docker.service
-------------------------------------------------------------------
#找到ExecStart部分,在此行末尾添加--graph=你的目录,我的如下
[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock 
--graph=/home/hellxz/docker-home
-------------------------------------------------------------------
#保存退出,接着让systemd重新读取下这些service等的配置
sudo systemctl daemon-reload
#重启docker服务
sudo systemctl restart docker

--graph=/path/to/path可以替换成-g /path/to/path 效果等同

2、官方文档中的方式,修改 /etc/docker/daemon.json

{
  "registry-mirrors": ["http://hub-mirror.c.163.com"],
  "data-root": "/home/hellxz/docker-home"
}

保存退出,重启docker服务

sudo systemctl restart docker

主要是用data-root来修改docker的工作目录

另外要提到的一点是,我尝试了drop-in文件方式,没有作用。

原文链接:https://www.cnblogs.com/ellisonzhang/p/15152402.html

公司最近为了节约成本,将原来部署测试环境的阿里云的服务器停止续费,从腾讯云新购了四台服务器。

为了尽可能的节约成本,四台服务器,只购买了一个公网 IP,其他服务器计划通过内网桥接之类的联网,实际操作后,发现事情没有这么简单,折腾了几天才解决,现在记录一下。

一开始搜了很多教程,都是 window 宿主机安装 VM 虚拟机进行桥接的教程,最后找到一个内网服务器通过代理使用有外网的服务器下载公网数据,但是这样不能 ping 通外网,好像速度也有限制,代理的教程链接如下:

内网服务器如何设置代理访问外网

最后感觉一些 docker 相关的东西还是联网困难,还是需要一个完美的联网方式,又搜了很久,搜到这篇利用建立虚拟网关进行联网的教程,测试一切联网正常,也可以在没有公网的服务器里 ping 通 baidu.com ,教程链接如下:

无公网服务器通过另一台有公网服务器联网

这里也搬运一下教程,防止链接失效

有公网服务器操作:

1、安装

yum install -y pptpd

2、修改配置

vim /etc/pptpd.conf

在编辑的文件里最下面添加以下代码(原文:代码意思是通过虚拟网关实现,localip即本地的虚拟网关,remoteip是开放的网关ip,类似于路由器一样,分配给链接的服务器。):

localip 192.168.0.1
remoteip 192.168.0.234-238,192.168.0.245

3、增加配置

打开 /etc/ppp/chap-secrets 配置文件,执行以下命令。

//用户名    pptpd    密码    *
root    pptpd    123456    *

4、启动pptpd服务

systemctl start pptpd

若想添加自启则执行以下命令,关闭则将on改为off

chkconfig pptpd on

5、开启转发

同时还需要开启转发,及iptables功能。

echo 1 > /proc/sys/net/ipv4/ip_forward

以上命令为临时开启,重启消失。如想永久开启,则执行以下命令编辑,将 net.ipv4.ip_forward=0 改为 net.ipv4.ip_forward=1 即可。

vim /etc/sysctl.conf

6、开启iptables nat功能

iptables -t nat -A POSTROUTING -o eth0 -s 192.168.0.0/24 -j MASQUERADE

若想自动生效,则安装iptables-services,之后开启功能,然后保存配置。并配置自启。

yum install iptables-services
iptables -t nat -A POSTROUTING -o eth0 -s 192.168.0.0/24 -j MASQUERADE
service iptables save
systemctl enable iptables.service

内网服务器操作

1、安装PPTP和PPTP-SETUP

安装PPTP和PPTP-SETUP,在centos无法安装pptp-setup,需要自行配置拨号文件(详见下面)。

yum install -y pptp pptp-setup

2、配置拨号文件

首先要知道 公网服务器的内网ip ,替换下面的 xxx.xxx.xxx.xxx 。这里创建了一个名为test的拨号文件

pptpsetup --create test --server xxx.xxx.xxx.xxx --username root --password 123456 --encrypt

其代表含义为:

pptpsetup --create 配置文件的名称 --server 有公网 IP 的云服务器的内网 IP --username 连接 PPTP 的用户名 --password 连接 PPTP 的密码 --encrypt

3、进行拨号

pppd call test

4、设置路由

route add -net 0.0.0.0 dev ppp0

Ps. 3,4步骤开机自动生效

若想3,4步骤开机自动生效。则在cd /etc/rc.d/init.d并创建个.sh文件,例如叫 a.sh,内容如下:

#!/bin/bash
#chkconfig:2345 65 65 //
pppd call test
sleep 5s
route add -net 0.0.0.0 dev ppp0

之后执行以下代码,即可实现自启。

chkconfig --add a.sh
chkconfig a.sh on

5、测试网络

最后ping www.baidu.com,若能够ping通,则证明联网成功。

最后

经实测、全程按照教程设置,三台内网服务器都能通过有外网的服务器 ping 通 baidu.com 了,但是大量下载时,会出现拨号断开的情况,需要重复操作上面内网服务器的 3、4 步骤操作恢复拨号,网上能搜到拨号断开自动重新拨号的教程,没测试,可以自己搜搜测试一下。

我理解很多人做出选择是为了逃避恐惧,怕寂寞,怕失去,怕错过,怕得不到自己想要的东西,怕想不明白自己到底想要什么。

混沌也有混沌的意义,我不做选择的时候,也就意味着我没有选择,与其说我还在等待,倒不如说我只是在碰运气,当一个人没了期待也就没了恐惧与焦虑。

近两年很多事情发生在我身上,不管好的坏的,我都一一接受,因为我知道这些屁事不会让我变的更好,也不会让我坏的彻底。

记不清什么时候开始,一空闲下来,就觉得世界前所未有的割裂,但仔细想一想其实又没什么大的改变,大家也都在正常的生活,谈了恋爱,买了房,结了婚,生了小孩,养了猫猫养了狗狗,没有一个人说过再见。

但周末睡到下午一觉醒来,我感到离所有人都很远。

他们都是做出了选择的人,他们的生活也并没有变的糟糕。一直都以为孤独是我的选择而不是遭遇,事实是我一直在逃避,未知的领域带给我过多的恐惧,而我又放弃了选择面对。

Everything is a choice between lonely and fear.
总结就是:活着归根结底还是别怕。

学习分布式的时候,了解到 Redis 分布式锁的时候,觉得这是个很实用的功能,于是实操了一下,使用 Jmeter 进行线程压测,记录一下结论

Synchronized 只能锁当前服务的线程,锁不了其他服务,所以在分布式情况下不实用,而且大并发使用 Synchronized 也存在性能问题

Redis 使用 SetNX 做一把锁,setnx 只有在key不存在的情况下,才会设置value ,已经存在key,不做任何操作,利用这个特性来制作一把锁。

大致流程如下:
使用 StringRedisTemplate 对象的 opsForValue().setIfAbsent(K,V) 方法创建一把锁,类似于 jedis.setnx(K,V),方法返回值的返回值是布尔值,如果结果 false 就不执行逻辑代码返回自定义 error
线程在拿到锁在执行完后,必须去释放锁,使用 StringRedisTemplate 对象的 delete(K) 释放锁,让其他线程来争抢锁

问题一,如果一个线程拿到锁,中途逻辑代码抛异常,导致该线程不能正常释放锁,就会导致锁一直不能释放,解决办法是用 try finally 来释放锁,加锁和逻辑代码放在 try 捕获里,释放锁放在 finally 里。

问题二,线程拿到锁,中途程序挂掉,导致不能执行 finally ,也将不能释放锁,解决办法是使用 StringRedisTemplate 对象的 expire(K,timeout) 设置超时时间,比如设置10秒,不管程序是否挂掉,都能释放锁

问题三,如果线程刚拿到锁就挂了,没来得及设置超时,也就不能释放锁,解决办法是使用 StringRedisTemplate 的 opsForValue.setIfAbSent(K,V,timeout,timeunit) 设置锁的超时,保证加锁和超时是一个原子执行

问题四,如果逻辑代码执行时间超过了超时时间,代码没执行完,锁就被释放掉了,下一个线程又来加锁,最后第一个线程执行完后会去释放锁,但它释放的是其他线程加的锁,多个线程反复,锁会永久失效,解决办法是给当前线程锁设置一个token,比如使用 UUID,当成 value 保存,释放锁时判断 key 取到的 value 是否等于生成的token,不相等就不去释放

问题五,超市时间设置合理问题,解决办法是每10秒分线程检测锁是否存在,存在就续期

最终方案,使用 Redisson 客户端,实现了最完善的分布式锁,加锁之前 redissonLock.getLock(K) 拿锁, try 里加锁 redissonLock.lock() 方法,finally 里面释放锁,redissonLock.unlock() 方法。

游泳一直是我想学习的基本技能,奈何小时候家里管教比较严,即使住的村里有两个鱼塘加一公里远的嘉陵江支流河边,也没有下过一次水,甚至跟着小伙伴去河边玩一次,回来都要挨打。

17年跟同事去水上乐园玩,在大泳池里被他们怂恿在水里扑腾呛水后,就在心里留了一个心理阴影,也导致了产生学游泳的念头,结果说了几年都没去学,一个是经济能力不够,每个月基本都是月光甚至负资产,而是时间条件和心理建设都不充分。

今年稍微有点点经济基础了,965 的上班制度也让时间多了起来,然后趁着夏天的炎热,心一横就下定决心学习游泳了,去了两个健身房,一个在楼下,两三百米的距离,很方便,但是一去咨询就报很高的价格,差不多 3000 左右,加上一直给我推年卡,让我很反感;第二家有两公里左右,下班顺路去问了问,教练直接开门见山说 1800 “包教会”,加送 4 张周卡。

回家考虑了一下,第二天就去第二家报名了,签合同的时候告知,1800 其实是 12 节课的价格,三个月内有效。因为有把握在12节课内教会,所以一直用“包教会”在宣传。我其实能想到是这个样子,就签了合同交了钱。

加了教练,直接就约了三天后开始上课,我在网上买了一套装备,背漂、浮板、泳镜、泳裤、耳塞等。后来才发现耳塞根本用不上,教练也不让用,说只要没有中耳炎和鼻炎,什么耳塞鼻塞都不要用,以后如果遇到紧急情况是没有这些东西的,要学着适应。二十多块钱的耳塞白买了.....

开始上课后,先学习蛙泳动作,然后下水适应,背上绑着背漂,手上拿着浮板,人就可以轻松飘在水面,但一开始太害怕,动作做不好,在水里很容易呛水,练了两节课后还是不行,于是教练提前教了换气,戴上泳镜直接把头埋在水里学习换气,练了一节课动作加换气后,呛水的情况有所改善了,但还是不熟练,导致一会儿就喝一肚子气下去,练一会儿就要上岸打嗝.....

用浮板练了几节课,动作和换气练的差不多后,就要去掉浮板了,没有了工具的帮助,一下子又乱套了,开始呛水,教练亲自下水指导了一会儿后才适应过来。

后面的流程就是一点点去掉工具,背上的浮板有三块,每次进度有所改善就去掉一块,而我就在这个步骤卡了最久,潜意识里对水的恐惧占据了我的思维,每次在水里就忍不住的去想呛水的情形,导致真的呛水,进度一度停滞不前,课程用掉大半,还没去掉一块浮板。

教练用了很多方式帮我拜托对水的恐惧,包括水下捡表,在水里抱成一个球,在水里跳,但成效甚微,我过不了自己潜意识里对水恐惧的那一个坎,甚至害怕去上课,在家里想到在水里的画面甚至会不由自觉的扣紧脚趾。还好教练每次都微信催我去上课,甚至微信上安慰我,给我说一些技巧。

后面我在呛水时,强迫自己不去慌张,用蛙泳的腿上动作帮助自己去抓岸边,脸了几次成功后,我发现自己不在那么恐惧呛水了,有了这一步,后面的进度就稍微快一些了。

最后三节课,我在不熟练的情况下去掉了三块浮板,能够勉强游两圈,还是会偶尔呛水,但成就感还是很满足的。课程结束了,我也没去过泳池了,教练叮嘱我没事一定要多练,我也不知道自己什么时候有勇气再去了....

所以这一次学习的经历下来,我发现学习游泳最重要的还是克服自己的恐惧是最重要的,不怕呛水,才会做到真的不呛水,当然,动作的标准也很关键。