Reactor Proactor
发布于
io

I/O 编程模型

用户应用程序运行在用户态,只能访问有限的内存,不能访问外围I/O设备,如硬盘、网卡等; os运行在内核态,可以访问所有内存区域以及所有外围I/O设备

用户态应用程序访问I/O需要发起系统调用,由内核线程(指令)来完成

内核线程完成相应I/O操作(读取/写入),数据需要从内核态复制到用户空间的内存,应用程序从内存获取数据,才能继续完成相应业务逻辑的执行

  1. 用户空间通过系统调用通知内核准备数据 -> 等待数据
  2. 从内核空间复制数据到用户空间 -> 拷贝数据

由于拷贝数据实在内存中进行(或者内核内存空间映射到用户空间就更快了),因此速度较快,而等待数据则是两个阶段中最消耗时间的

《Unix网络编程》 IO模型:阻塞IO、非阻塞IO、IO复用、信号驱动IO和异步IO

五种可单独用其中一种,也可组合使用

POSIX 只分两类:同步IO和异步IO

一个IO操作分成 发起IO请求和实际的IO操作两个步骤

阻塞和非阻塞IO区别在于第一步发起IO请求是否会被阻塞

同步和异步IO区别在于第二个步骤是否阻塞请求进程

非阻塞是os做完IO操作再将结果返回前四是同步模型,最后一种是异步模型

  • 同步:指用户线程发起了I/O请求后,需一直等待或轮询内核I/O操作完成后,才会继续执行
  • 异步:指用户线程发起I/O请求后依然继续执行,当内核完成I/O操作后会通知用户线程,或者回调用户线程已注册的回调函数
  • 阻塞:指只有内核的I/O操作彻底完成后,才会返回用户空间
  • 非阻塞:指I/O操作被调用后,立即返回一个状态值,无需等到I/O操作彻底完成
golang flag and config
发布于
go

flag欠缺的

与命令行Parser的事实标准:Posix getopt(C/C++/Perl/Shell脚本都可用)相比,还有较大差距

  1. 无法区分 long option 和 short option,如:-h 和 –help
  2. 不支持short options合并,如:ls -l -h <=> ls -hl
  3. 位置不能任意放置,如无法放在 non-flag parameter 后面
MySQL 事务
发布于
mysql

事务广泛应用于订单系统、银行系统等多种场景

A给B转账500,需要做以下几件事:

  1. 检查 A 的账户余额 > 500
  2. A 扣 500
  3. B 加 500

正常流程走下来,A扣500,B 加 500,皆大欢喜

如果 A 扣了后,系统出故障了,A 损失 500,而 B 也没有收到本该属于他的 500

以上的案例中,隐藏着一个前提条件:A 扣钱和 B 加钱,要么同时成功,要么同时失败

事务的需求就在于此

spring transaction
发布于
java spring

三个组成部分:DataSource、TransactionManager和代理机制

无论哪种配置方式,一般变化的只是代理机制这部分

DataSource、TransactionManager 只根据数据访问方式有所变化

Hibernate DataSource 实现为 SessionFactory

TransactionManager 实现为 HibernateTransactionManager

MySQL隔离级别,锁与MVCC
发布于
mysql

高并发,高可用可从质和量来评估,mysql中事务保证质MVCC保证量

事务

用事务保证SQL执行符合预期

ACID 中 原子、一致和持久性差不多,保证结果可靠

隔离性比较复杂,描述并发下数据库的表现

四种隔离级别适应不同的并发场景

  • Read Uncommited
  • Read Committed (RC)
  • Repeatable Read (RR)
  • Serializable

隔离级别提高,并发能力也有所下降

隔离级别

只讨论共享锁排它锁两概念,读加共享锁,写加排它锁,忽略了GAP锁(间隙锁、范围锁):

RC级别下,改数据加排它锁,事务结束释放,其他事务不许读,解决脏读问题**(共享锁当场释放)**

RR级别下,读数据加共享锁,事务结束释放,其他事务不许修改,解决不可重复读(共享锁事务结束释放)

实际上都把操作串行化了!而MySQL对其进行了优化,一个事务读时其他事务不能写,一个事务写时其他事务不能读?MVCC不这么干照样能解决脏读和不可重复读问题

这也使得问题变得越来越复杂,而不一样的地方也开始出现在RR隔离级别下,MySQL默认隔离级别就是RR)

MVCC

MVCC,用双版本来解决数据隔离问题。(“create”一个版本号,“delete”一个版本号,修改操作拆分为“delete”和“create”)

每个事务开始对每张表增删改查操作时都会生成一个版本号,每个事务只能查到“create”小于本版本号和“delete”大于本版本号的数据

这样增删查可以并发进行,只有修改一定要排队。这样就算没有共享锁也解决了不可重复读问题,因为其他事务修改后,数据的版本号比我大,我不会读到

MVCC在RR隔离级别下的并发

引入MVCC之后,两个事务先后对一条数据做更新操作,然后两个事务再读取那条数据,分别读到什么?

这不可能出现,因为修改是串行的,另一个事务必须先commit本事务才能修改

换个问题,两个事务先后对一条数据+1,另一个事务提交后,本事务再+1,再读取那条数据,本事务是读取到+1还是+2的结果?

如果读取到+2,那不是破坏了隔离性,读到了其他事务提交的数据么?

事实确实是这样,其他事务已经提交,本事务也已修改过那条数据了,之后当然要读到+2才行

虽然本来是0,本事务明明只加了1,可读取后却变成2了,有点不适应

在标准的RR隔离级别下,因为操作都是串行的,本事务读取一行数据后,其他事务就不能修改这条数据了,这条数据永远只有本事务在操作,所以严格满足隔离性

但MySQL的RR增强了读与写的并发,只有当两个事务同时修改一条数据需要串行,其他所有操作都可以并行。所以造成了这种结果,好像出现了不可重复读。但是这种不可重复读实际上是符合我们的直观感受的,在本事务对数据修改后,当然要读取到最新的数据

要对其过程进行分析的话:

  • 数据create版本号为0
  • 事务1版本号为1,读取数据value=0
  • 事务2版本号为2,修改数据value+1=1,原数据delete版本号为2,新数据create版本号更新为2,commit
  • 事务1修改数据value+1=2,(由于修改是当前读,永远读取版本号最大的数据,所以读取到value为1)修改后delete版本号为1,新数据create版本号为1
  • 事务1读取数据value=2

深入分析:

其实上面的描述也是有漏洞的,如果有第三个事务版本号为3呢?因为版本号为3,是不是可以直接读取事务1、2未提交的数据?实际上在MVCC中,每个事务还有一个最低可见版本low_limit_id(事务号 >= low_limit_id的记录,对于当前事务都是不可见的),把当前正在执行还没commit的事务给过滤掉了

例如事务3,虽然版本号为3,但是low_limit_id=1,所以事务1和事务2的修改对3都是不可见的

总结

共享锁,排它锁是标准隔离级别采用的方法,共享锁与共享锁不互斥,共享锁与排它锁互斥 快照读,当前读是对MVCC下读行为的描述。快照读不加锁,使用版本号来控制隔离性;当前读忽略版本号,永远只读最新数据,并加排它锁 脏读是指读到其他事务未提交的数据 不可重复读指本事务第二次读,读到了其他事务已提交的修改数据,导致同一查询执行两遍,和第一次读的数据不一致 幻读指本事务第二次读,读到了其他事务已提交的新插入数据,导致同一查询执行两遍,和第一次读的数据量不一致

数据库为了解决隔离性问题,都没有使用完全copy数据这种笨方法

传统数据库使用共享锁和排它锁使读写操作串行;MySQL使用MVCC和排它锁,读写可并行

MySQL在RR隔离级别以下,和传统方式表现一致,在RR隔离级别,和传统方式有差异,体现在本事务更新某条数据后,能读取到其他事务对该条数据已提交的修改

httpclient
发布于
httpclient

httpclient是java里一大坑,出了问题比较难排查,很多时候是对参数不了解,随意设置导致

lombok改造
发布于
java

mvn 和 lombok 使用过程中,最终决定对 maven-source-plugin、maven-javadoc-plugin改造