代理模式, proxy pattern

代理模式, proxy pattern 为其他对象提供一种代理以控制对这个对象的访问。 代理模式,即Proxy,它和 Adapter 模式很类似。我们先回顾Adapter模式,它用于把A接口转换为B接口: Adapter 模式 public BAdapter implements B { private A a; public BAdapter(A a) { this.a = a; } public void b() { a.a(); } } Proxy模式 而Proxy模式不是把A接口转换成B接口,它还是转换成A接口: public AProxy implements A { private A a; public AProxy(A a) { this.a = a; } public void a() { this.a.a(); } } 看起来 Proxy 只是把 A 接口又包了一层,这有什么意义呢? ...

2026-04-16 · 1 min · 117 words · -

外观模式, Facade Pattern

概念 看到"门面"这个词,大家一定都觉得很熟悉。日常生活中的"门面"就是我们买东西的地方,它跟各种商品的生产商打交道,收集商品后再卖给我们。如果没有"门面",我们将不得不直接跟各种各样的生产商买商品。 Facade 模式正是这样一个"门面":我们本来需要与后台的多个类或接口打交道,而 Facade 模式在客户端和后台之间插入一个中间层——门面,这个门面跟后台的多个类或接口打交道,客户端只需要跟门面打交道即可。 Facade 类是一个简化的用户接口,它和后台中的多个类产生依赖关系,而客户类则只跟 Facade 类产生依赖关系。后台的开发者熟悉自己开发的各个类,容易解决与多个类的依赖关系;而使用者不太熟悉后台的各个类,通过 Facade 可以大大降低使用难度。 情况一:功能分布在多个无关类中 客户类要使用的功能分布在多个类中,客户必须先初始化各个类才能使用。这时适合将这些功能集中在一个 Facade 类里,同时替用户做初始化工作。 场景: 商店里出售三种商品——衣服、电脑和手机,分别由各自的生产厂商提供。 各厂商类: public class CoatFactory { public Coat saleCoat() { // ... return coat; } } public class ComputerFactory { public Computer saleComputer() { // ... return computer; } } public class MobileFactory { public Mobile saleMobile() { // ... return mobile; } } 没有商店时,客户需要分别跟各厂商打交道: ...

2026-04-16 · 2 min · 373 words · -

组合模式, Composite Pattern

概念 组合模式(Composite Pattern)将对象组合成树状层次结构,使客户端对单个对象(叶子节点)和组合对象(容器节点)具有一致的访问方式。 核心思想:部分与整体的统一接口。 角色: Component:抽象组件,定义叶子和容器的公共接口 Leaf:叶子节点,没有子节点,实现具体操作 Composite:容器节点,包含子组件,将操作委托给子节点 示例:文件系统 文件系统是 Composite 模式的经典场景——文件夹可以包含文件或其他文件夹,但对外都提供统一的 getSize() 接口。 // Component public interface FileSystemNode { String getName(); long getSize(); void print(String indent); } // Leaf public class File implements FileSystemNode { private final String name; private final long size; public File(String name, long size) { this.name = name; this.size = size; } @Override public String getName() { return name; } @Override public long getSize() { return size; } @Override public void print(String indent) { System.out.println(indent + "📄 " + name + " (" + size + " bytes)"); } } // Composite public class Directory implements FileSystemNode { private final String name; private final List<FileSystemNode> children = new ArrayList<>(); public Directory(String name) { this.name = name; } public void add(FileSystemNode node) { children.add(node); } public void remove(FileSystemNode node) { children.remove(node); } @Override public String getName() { return name; } @Override public long getSize() { return children.stream() .mapToLong(FileSystemNode::getSize) .sum(); } @Override public void print(String indent) { System.out.println(indent + "📁 " + name + "/"); for (FileSystemNode child : children) { child.print(indent + " "); } } } // Client public class Client { public static void main(String[] args) { Directory root = new Directory("root"); Directory src = new Directory("src"); src.add(new File("Main.java", 1200)); src.add(new File("Utils.java", 800)); Directory resources = new Directory("resources"); resources.add(new File("config.yml", 300)); resources.add(new File("banner.txt", 50)); root.add(src); root.add(resources); root.add(new File("README.md", 500)); root.print(""); System.out.println("Total size: " + root.getSize() + " bytes"); } } 输出: ...

2026-04-16 · 2 min · 300 words · -

创建者模式, 建造者模式, Builder

创建者模式, 建造者模式, Builder 定义: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示 使用场景 多个部件或零件,都可以装配到一个对象中,但产生的结果又不相同时。 当初始化一个对象特别复杂的时候,比如参数多,而且很多参数都有默认值。 它分为抽象建造者 (Builder) 角色、具体建造者 (ConcreteBuilder) 角色、导演者 (Director) 角色、产品 (Product) 角色四个角色。 抽象建造者 (Builder) 角色: 给 出一个抽象接口,以规范产品对象的各个组成成分的建造。 具体建造者 (ConcreteBuilder) 角色: 要完成的任务包括: 1.实现抽象建造者Builder所声明的接口,给出一步一步地完成创建产品实例的操作。2.在建造过程完成后,提供产品的实例。 导演者 (Director) 角色: 担任这个角色的类调用具体建造者角色以创建产品对象。 产品 (Product) 角色: 产品便是建造中的复杂对象。 对于Builder模式很简单,但是一直想不明白为什么要这么设计,为什么要向builder要Product而不是向知道建造过程的Director要。刚才google到一篇文章,总算清楚了。在这里转贴一下这位richardluo的比喻。 简单地说,就好象我要装修一套房子,可是我不懂施工——水电怎么走、木作怎么做,每个工种都有自己专注的领域;也不懂整体设计——用什么风格、先做哪道工序、各部分如何协调,这是设计师的职责。于是我需要找一支装修队,各工种各司其职;还得找个设计师,他来规划整体方案、协调各工种按图施工,而设计师本身不动手,只负责下命令。最后,我可以向工人要装修好的房间了。在这个过程中,设计师手里什么都没有,只有图纸和指令,所以要房间也是跟工人要,记住了! 以下是richardluo的代码,我根据他的思路加上了相应的注释。 定义工人接口,即一支装修队所需具备的通用技能。 // 工人接口。水电工负责布线,木工负责木作,油漆工负责涂装。 // 装修完成后,由装修队把房间交还给房主。 public interface Builder { void doElectrical(); // 水电工 void doCarpentry(); // 木工 void doPainting(); // 油漆工 Room getRoom(); } 定义设计师(Director),他持有图纸,协调装修队按方案施工。 // 设计师(Director)。持有图纸,规定施工顺序,但不亲自动手。 public class Designer { public void coordinate(Builder builder) { builder.doElectrical(); builder.doCarpentry(); builder.doPainting(); } } 装修队(ConcreteBuilder),负责具体的施工实施。 // 各专职工人 class ElectricalWorker { public String work() { return "electrical done"; } } class Carpenter { public String work() { return "carpentry done"; } } class Painter { public String work() { return "painting done"; } } // 装修队(ConcreteBuilder)。由水电工、木工、油漆工组成,各司其职。 // 装修完成后,由装修队把房间交还给房主。 public class WorkerTeam implements Builder { private ElectricalWorker electrician = new ElectricalWorker(); private Carpenter carpenter = new Carpenter(); private Painter painter = new Painter(); private String electrical = ""; private String carpentry = ""; private String painting = ""; public void doElectrical() { electrical = electrician.work(); } public void doCarpentry() { carpentry = carpenter.work(); } public void doPainting() { painting = painter.work(); } // 把装修好的房间交还给房主 public Room getRoom() { if (!electrical.equals("") && !carpentry.equals("") && !painting.equals("")) { System.out.println("room ready!"); return new Room(); } else { return null; } } } 房主(Client),雇人、收房。 // 房主。聘请一支装修队和一位设计师,让设计师协调装修队按图施工,最后从装修队手上收房。 public class Client { public static void main(String[] args) { Builder team = new WorkerTeam(); Designer designer = new Designer(); designer.coordinate(team); team.getRoom(); } } 好了,我觉得这样大概能说明白了。不知各位觉得如何呢?或者有更好的应用场景解释,敬请赐教。 ...

2026-04-16 · 4 min · 730 words · -

SOLID 面向对象设计原则

SOLID 是面向对象设计的五大基本原则的首字母缩写,由 Robert C. Martin(Uncle Bob)整理归纳。这五个原则是编写可维护、可扩展代码的基础。 S — 单一职责原则 (Single Responsibility Principle, SRP) 一个类应该只有一个引起它变化的原因。 一个类只做一件事。如果一个类承担了多个职责,那么每个职责的变化都可能影响这个类,导致它越来越难以维护。 反例: 简单工厂模式中的工厂类 Driver 同时负责判断车型和创建所有车对象,违反了单一职责。 O — 开闭原则 (Open-Closed Principle, OCP) 软件实体应该对扩展开放,对修改封闭。 添加新功能时,应该通过新增代码实现,而不是修改已有代码。详见开闭原则。 反例: 简单工厂的 if/else 判断链,每新增一种产品都要修改工厂方法。 L — 里氏替换原则 (Liskov Substitution Principle, LSP) 子类必须能够替换其父类,且程序行为不变。 任何使用父类的地方,换成子类后程序应该仍然正确运行。子类不应该破坏父类的契约。 示例: Benze 和 Bmw 都实现了 Car 接口,客户端代码使用 Car 类型,无论实际是哪种车,drive() 都能正确调用。 I — 接口隔离原则 (Interface Segregation Principle, ISP) 客户端不应该被迫依赖它不使用的方法。 接口应该尽量细化,不要把不相关的方法放在同一个接口里。大接口应该拆分成多个小接口,让实现类只需要关心自己用到的方法。 反例: 一个包含 fly()、swim()、run() 的 Animal 接口,鱼类被迫实现 fly() 和 run()。 D — 依赖倒置原则 (Dependency Inversion Principle, DIP) 高层模块不应该依赖低层模块,两者都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。 ...

2026-04-16 · 1 min · 87 words · -

开闭原则

开闭原则 开闭原则(Open-Close Principle/OCP)是面向对象设计中"可复用设计"的基石,是面向对象设计中最重要的原则之一,其它很多的设计原则都是实现开闭原则的一种手段。 1988年,Bertrand Meyer在他的著作《Object Oriented Software Construction》中提出了开闭原则,它的原文是这样: “Software entities should be open for extension,but closed for modification”。翻译过来就是: “软件实体应当对扩展开放,对修改关闭”。这句话说得略微有点专业,我们把它讲得更通俗一点,也就是: 软件系统中包含的各种组件,例如模块 (Modules) 、类 (Classes) 以及功能 (Functions) 等等,应该在不修改现有代码的基础上,引入新功能。开闭原则中"开",是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的;开闭原则中"闭",是指对于原有代码的修改是封闭的,即不应该修改原有的代码。实现开闭原则的关键就在于"抽象"。把系统的所有可能的行为抽象成一个抽象底层,这个抽象底层规定出所有的具体实现必须提供的方法的特征。作为系统设计的抽象层,要预见所有可能的扩展,从而使得在任何扩展情况下,系统的抽象底层不需修改;同时,由于可以从抽象底层导出一个或多个新的具体实现,可以改变系统的行为,因此系统设计对扩展是开放的。 我们在软件开发的过程中,一直都是提倡需求导向的。这就要求我们在设计的时候,要非常清楚地了解用户需求,判断需求中包含的可能的变化,从而明确在什么情况下使用开闭原则。 关于系统可变的部分,还有一个更具体的对可变性封装原则 (Principle of Encapsulation of Variation, EVP) ,它从软件工程实现的角度对开闭原则进行了进一步的解释。EVP要求在做系统设计的时候,对系统所有可能发生变化的部分进行评估和分类,每一个可变的因素都单独进行封装。 我们在实际开发过程的设计开始阶段,就要罗列出来系统所有可能的行为,并把这些行为加入到抽象底层,根本就是不可能的,这么去做也是不经济的,费时费力。另外,在设计开始阶段,对所有的可变因素进行预计和封装也不太现实,也是很难做得到。所以,开闭原则描绘的愿景只是一种理想情况或是极端状态,现实世界中是很难被完全实现的。我们只能在某些组件,在某种程度上符合开闭原则的要求。 通过以上的分析,对于开闭原则,我们可以得出这样的结论: 虽然我们不可能做到百分之百的封闭,但是在系统设计的时候,我们还是要尽量做到这一点。 对于软件系统的功能扩展,我们可以通过继承、重载或者委托等手段实现。以接口为例,它对修改就是是封闭的,而对具体的实现是开放的,我们可以根据实际的需要提供不同的实现,所以接口是符合开闭原则的。如果一个软件系统符合开闭原则的,那么从软件工程的角度来看,它至少具有这样的好处: 可复用性好。 我们可以在软件完成以后,仍然可以对软件进行扩展,加入新的功能,非常灵活。因此,这个软件系统就可以通过不断地增加新的组件,来满足不断变化的需求。 可维护性好。 由于对于已有的软件系统的组件,特别是它的抽象底层不去修改,因此,我们不用担心软件系统中原有组件的稳定性,这就使变化中的软件系统有一定的稳定性和延续性。开闭原则具有理想主义的色彩,它是面向对象设计的终极目标。因此,针对开闭原则的实现方法,一直都有面向对象设计的大师费尽心机,研究开闭原则的实现方式。后面要提到的里氏代换原则 (LSP) 、依赖倒转原则 (DIP) 、接口隔离原则 (ISP) 以及抽象类 (Abstract Class) 、接口(Interface)等等,都可以看作是开闭原则的实现方法。 http://baike.baidu.com/view/866233.htm

2026-04-16 · 1 min · 53 words · -

设计模式 — 工厂模式, Factory

设计模式 — 工厂模式, Factory 一、引子 话说十年前,有一个老板,他家有三辆汽车 —— Benz奔驰、Bmw宝马、Audi奥迪,还雇了司机为他开车。不过,老板坐车时总是怪怪的: 上Benz车后跟司机说"开奔驰车!",坐上Bmw后他说"开宝马车!",坐上Audi说"开奥迪车!"。你一定说: 这人有病!直接说开车不就行了?! 而当把这个老板的行为放到我们程序设计中来时,会发现这是一个普遍存在的现象。幸运的是,这种有病的现象在 OO (面向对象) 语言中可以避免了。下面就以 Java 语言为基础来引入我们本文的主题: 工厂模式。 二、分类 工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。 工厂模式在《Java与模式》中分为三类: 简单工厂模式 (Simple Factory) 工厂方法模式 (Factory Method) 抽象工厂模式 (Abstract Factory) 这三种模式从上到下逐步抽象,并且更具一般性。 GOF在《设计模式》一书中将工厂模式分为两类: 工厂方法模式 (Factory Method) 与抽象工厂模式 (Abstract Factory) 。将简单工厂模式 (Simple Factory) 看为工厂方法模式的一种特例,两者归为一类。 两者皆可,在本文使用《Java与模式》的分类方法。下面来看看这些工厂模式是怎么来"治病"的。 简单工厂模式, 简单工厂 (Simple Factory) 简单工厂模式又称静态工厂方法模式。从命名上就可以看出这个模式一定很简单。它存在的目的很简单: 定义一个用于创建对象的接口。 先来看看它的组成: 工厂类角色: 这是本模式的核心,含有一定的业务逻辑和判断逻辑。在 java 中它往往由一个具体类实现。 抽象产品角色: 它一般是具体产品继承的父类或者实现的接口。在 java 中由接口或者抽象类来实现。 具体产品角色: 工厂类所创建的对象就是此角色的实例。在 java 中由一个具体类实现。 来用类图来清晰的表示下的它们之间的关系 (如果对类图不太了解,请参考我关于类图的文章) : 那么简单工厂模式怎么来使用呢?我们就以简单工厂模式来改造老板坐车的方式——现在老板只需要坐在车里对司机说句: “开车"就可以了。 // 抽象产品 interface Car { void drive(); } // 具体产品 class Benze implements Car { public void drive() { System.out.println("Driving Benz"); } } // 具体产品 class Bmw implements Car { public void drive() { System.out.println("Driving Bmw"); } } // 工厂类 class Driver { // 工厂方法,注意返回类型为抽象产品 public static Car createCar(String s) throws Exception { // 判断逻辑,返回具体的产品角色给 Client if (s.equalsIgnoreCase("Benz")) { return new Benze(); } else if (s.equalsIgnoreCase("Bmw")) { return new Bmw(); } else { throw new Exception(); } } } // 客户端 public class Magnate { public static void main(String[] args) throws Exception { // 告诉司机我今天坐奔驰 Car car = Driver.createCar("benz"); // 下命令: 开车 car.drive(); car = Driver.createCar("bmw"); car.drive(); } } 将本程序空缺的其他信息填充完整后即可运行。如果你将所有的类放在一个文件中,请不要忘记只能有一个类被声明为public。本程序在jdk1.4 下运行通过。 ...

2026-04-16 · 3 min · 440 words · -

缓存

缓存 https://xie.infoq.cn/article/0134f29b0c0895df548dd929b?utm_source=rss&utm_medium=article 缓存的存在,是为了调和差异。 差异有多种,比如处理器和存储之间的速度差异、用户对产品的使用体验和服务处理效率的差异等等。 CPU 缓存[2]。为了调和 CPU 和内存之间巨大的速度差异,设置了 L1/L2/L3 三级缓存,离 CPU 越近,速度越快。 Ehcache[3]。是最流行了 Java 缓存框架之一。因为其开源属性,在 spring/Hibernate 等框架上被广泛使用。支持磁盘持久化和堆外内存。缓存功能齐全。 Guava cache。灵感来源于 ConcurrentHashMap,但具有更丰富的元素失效策略,功能没有 ehcache 齐全,如只支持 jvm 内存,但比较轻量简洁。 memcached。[5] memcached 是一个高效的分布式内存 cache,搭建与操作使用都比较简单,整个缓存都是基于内存的,因此响应时间很快,但是没有持久化的能力。 Redis 以优秀的性能和丰富的数据结构,以及稳定性和数据一致性的支持,被业内越来越普遍的使用。 缓存预热 缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候, 先查询数据库, 然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据! 解决思路: 直接写个缓存刷新页面,上线时手工操作下; 数据量不大,可以在项目启动的时候自动进行加载; 定时刷新缓存; 缓存更新 除了缓存服务器自带的缓存失效策略之外 (Redis默认的有6中策略可供选择) ,我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种: 定时去清理过期的缓存; 当有用户请求过来时, 再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。 两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪种方案,大家可以根据自己的应用场景来权衡。 缓存穿透 Cache Penetration 穿透形象一点就是: 请求过来了 转了一圈 一无所获 就像穿过透明地带一样。 在高并发系统中缓存穿透,如果一个 req 需要请求的 key 在缓存中没有,这时业务线程就会访问磁盘数据库系统,然而磁盘数据库也没有这个 key,无奈业务线程只能返回 null,白白处理一圈。 查询的是数据库中不存在的数据,没有命中缓存而数据库查询为空,也不会更新缓存。导致每次都查库,如果不加处理,遇到恶意攻击,会导致数据库承受巨大压力,直至崩溃。 解决方案 接口层防护(第一道防线) API 限流、防 DDoS 网关黑名单、用户鉴权 参数合法性校验(如 ID 必须 > 0) 缓存空对象 如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),仍然把这个空结果进行缓存,但它的过期时间会很短,不超过 5 分钟。通过这个直接设置的默认值存放到缓存,这样第二次到缓存中获取就有值了,而不会继续访问数据库。当修改或者新增该 key 的数据信息的时候,需要删除或者更新 null 缓存值。 ...

2026-04-14 · 2 min · 379 words · -

distributed lock 分布式锁

distributed lock 分布式锁 分布式是一种分布式协调技术,控制分布式系统不同进程共同访问共享资源的一种锁的实现。如果不同的系统或同一个系统的不同主机之间共享了某个临界资源,往往需要互斥来防止彼此干扰,以保证一致性。 分布式锁是由于单机锁无法满足分布式系统锁,在多进程/分布式环境下,需要分布式锁来控制共享内容,保证线程的安全。 为何需要分布式锁 Martin Kleppmann 是英国剑桥大学的分布式系统的研究员,之前和 Redis 之父 Antirez 进行过关于 RedLock (红锁,后续有讲到) 是否安全的激烈讨论。 Martin 认为一般我们使用分布式锁有两个场景: 效率: 使用分布式锁可以避免不同节点重复相同的工作,这些工作会浪费资源。比如用户付了钱之后有可能不同节点会发出多封短信。 正确性: 加分布式锁同样可以避免破坏正确性的发生,如果两个节点在同一条数据上面操作, 比如多个节点机器对同一个订单操作不同的流程有可能会导致该笔订单最后状态出现错误,造成损失。 分布式锁的一些特点 当我们确定了在不同节点上需要分布式锁,那么我们需要了解分布式锁到底应该有哪些特点? 分布式锁的特点如下: 互斥性: 和本地锁一样互斥性是锁最基本的特性, 任意时刻,只有一个客户端能持有锁。 可重入性: 同一个节点上的同一个线程如果获取了锁之后那么也可以再次获取这个锁。 超时释放: 锁失效机制, 防止死锁。正常情况下,请求获取锁之后,处理任务,处理完成之后释放锁。 但是如果在处理任务发生服务异常,或者网络异常时,导致锁无法释放。其他请求都无法获取锁,变成死锁。 为了防止锁变成死锁,需要设置锁的超时时间。过了超时时间后,锁自动释放,其他请求能正常获取锁。 自动续期: 锁设置了超时机制后,如果持有锁的节点处理任务的时候过长超过了超时时间,就会发生线程未处理完任务锁就被释放了, 其他线程就能获取到该锁,导致多个节点同时访问共享资源。对此,就需要延长超时时间。 开启一个监听线程,定时监听任务,监听任务线程还存活就延长超时时间。当任务完成、或者任务发生异常就不继续延长超时时间。 高可用, 高性能: 加锁和解锁需要高效,同时也需要保证高可用防止分布式锁失效,可以增加降级。 安全性: 锁只能被持有锁的客户端释放, 不能被其它客户端释放. 支持阻塞和非阻塞: 和 ReentrantLock 一样支持 lock 和 trylock 以及 tryLock(long timeOut)。即没有获取到锁将直接返回获取锁失败 支持公平锁和非公平锁(可选): 公平锁的意思是按照请求加锁的顺序获得锁,非公平锁就相反是无序的。这个一般来说实现的比较少。 分布式锁一般有三种实现方式: 数据库乐观锁 基于 Redis 的分布式锁 基于 ZooKeeper 的分布式锁 可靠性 首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件: 互斥性。在任意时刻,只有一个客户端能持有锁。 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。 具有容错性。只要大部分的 Redis 节点正常运行,客户端就可以加锁和解锁。 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。 Redis 实现分布式锁 Redis 实现分布式锁,性能会比关系式数据库高一些. ...

2026-04-14 · 10 min · 2091 words · -

PostgreSQL

PostgreSQL commands \l 或 \list meta-command 列出所有数据库 sudo -u postgres psql -c "\l" 用 \c + 数据库名 来进入数据库: \dt 列出所有数据库表: # 查看表结构, 索引 \d table0 # 比上面多几个字段 Storage | Stats target | Description \d+ table0 # Turn off printing of column names and result row count footers, etc. This is equivalent to \t or \pset tuples_only. \t tuples only on/off, tuples only on 的时候 select 语句的输出不带 header \h \? \du 列出所有的用户 # 创建用户 CREATE USER user_0 WITH PASSWORD 'password_0'; # create database, 所有者 user_0 create database database_0 OWNER user_0; psql -h 192.168.1.100 -p 5432 -U user_0 -d database_0 PGPASSWORD=password_0 psql -h 192.168.1.100 -p 5432 -U user_0 -d database_0 --command 'select version();' #当表没有其他关系时 TRUNCATE TABLE tablename; #当表中有外键时,要用级联方式删所有关联的数据 TRUNCATE TABLE tablename CASCADE; # 查看 表大小 select pg_size_pretty(pg_relation_size('table0')); # 查看配置文件路径, 切换到 postgres 用户执行 psql -c "show config_file" # 查看版本 select version(); pacman -S postgresql psql -h 127.0.0.1 -p 5432 -d database0 -U user0 # create table create table test(id int, c1 int); create table table0(field0 json); # delete table DROP TABLE table0; # 查看字段类型 select column_name, data_type from information_schema.columns where table_name='table0'; select * length( "abc"::TEXT) insert into test select generate_series(1,10000), random()*10; # 复制表结构到另外一个数据库 pg_dump -U postgres --schema-only source_db | psql -U postgres target_db 导入/导出 export, 导出, 备份 # https://www.postgresql.org/download/linux/ubuntu/ # install pg_dump sudo apt install -y postgresql-common # This script will enable the PostgreSQL APT repository on apt.postgresql.org sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh # ubuntu install pg_dump sudo apt-get install postgresql-client-17 # -h, host 127.0.0.1 # -p, port 5432 # -t, table: table0, 不加 -t 参数时会导出所有表结构 # -s, 不导出数据 # database: database0 # -F : 指定输出文件的格式,它可以是以下格式之一: c: 自定义格式 d: 目录格式存档 t: tar 文件包 p: SQL 脚本文件 # -W 命令执行时提示输入用户密码(不会直接在命令中写密码)。 pg_dump -h 127.0.0.1 -U username -W -F t db_name > foo.tar # 导入 # -c --clean 创建数据库对象前先清理(删除)它们。 pg_restore -h 127.0.0.1 -U username -W -d db_name -c foo.tar pg_dump -h 127.0.0.1 -p 5432 -t table_0 -U postgres database0 > foo.sql pg_dump -h 127.0.0.1 -p 5432 -s -t table_0 -U postgres database0 > foo.sql # 导出并压缩 pg_dump -d db_name | gzip > db.gz pg_dump -a -t table_0 "host=127.0.0.1 hostaddr=127.0.0.1 port=5432 user=user_0 password=password_0 dbname=db_0" # export insert sql pg_dump -a -t table_0 "host=127.0.0.1 hostaddr=127.0.0.1 port=5432 user=user_0 password=password_0 dbname=db_0" --inserts 导入 # sql psql -h 127.0.0.1 -p 5432 -t table0 -U postgres -d database0 -f foo.sql # csv, https://stackoverflow.com/questions/26701735/extra-data-after-last-expected-column-while-trying-to-import-a-csv-file-into-p \COPY agency (agency_name, agency_url, agency_timezone) FROM 'myFile.txt' CSV HEADER DELIMITER ','; 导出指定的行 https://stackoverflow.com/questions/12815496/export-specific-rows-from-a-postgresql-table-as-insert-sql-script ...

2026-04-14 · 22 min · 4514 words · -

k8s

k8s pod Pod 是 Kubernetes 中可部署的最小、最基本对象。一个 Pod 代表集群中正在运行的单个进程实例。 Pod 包含一个或多个容器, 当 Pod 运行多个容器时,这些容器将作为一个实体进行管理并共用 Pod 的资源。 Pod 还包含其容器的共享网络和存储资源: 网络:系统会自动为 Pod 分配独一无二的 IP 地址。各 Pod 容器共用同一网络命名空间,包括 IP 地址和网络端口。Pod 中的各容器在 Pod 内通过 localhost 彼此通信。 存储:Pod 可以指定一组可在各容器之间共用的共享存储卷。 可以将 Pod 视为一个自成一体的独立“逻辑主机”,其中包含该 Pod 所服务于的应用的系统需求。 K8s Service Service 服务可以为一组具有相同功能的容器应用提供一个统一的入口地址。 Service 是 k8s 用来定义和管理网络访问 Pod 的一种资源对象。不同类型的 Service 决定了应用服务的暴露方式和访问范围。 Pod 在摧毁重建时ip地址是会动态变化的,这样通过客户端直接访问不合适了,这时候就可以选择使用服务来和 Pod 建立连接,通过标签选择器进行适配。这样就能有效的解决了Pod ip地址动态变换的问题了。 K8s Service有四种类型 ClusterIP (默认类型) 只在集群内部可访问,外部无法直接访问。 适合微服务之间的内部通信。 示例:type: ClusterIP NodePort 在每个节点(master node and worker node, all node)的指定端口开放服务,外部可以通过节点IP+端口访问服务。 一般用于开发、测试,或与外部负载均衡器配合使用。 示例:type: NodePort LoadBalancer Service 集成云服务商的负载均衡器(如 AWS ELB、GCP LB),自动分配一个外部 IP,通过该 IP 访问服务。 适合生产环境,需要公网访问的服务。 示例:type: LoadBalancer ExternalName 不真正暴露端口,而是把服务名解析为 DNS 名称(如外部数据库)。 适合集群内服务直接访问集群外部服务(如通过 DNS 访问外部资源)。 示例:type: ExternalName headless service 为什么需要无头服务? 客户端想要和指定的的 Pod 直接通信 并不是随机选择 开发人员希望自己控制负载均衡的策略,不使用 Service 提供的默认的负载均衡的功能,或者应用程序希望知道属于同组服务的其它实例。 Headless Service 使用场景 有状态应用,例如数据库 ...

2026-04-10 · 2 min · 410 words · -

LevelDB

使用场景 持久化缓冲队列(削峰) LevelDB 可以作为持久化写缓冲使用,实现「一边写入、一边读出」的管道模式: 写入端(Producer):将数据以递增的序列号或时间戳作为 Key 写入 LevelDB,得益于 LSM-Tree 架构,写入速度极快(顺序写磁盘),可以吸收突发流量峰值 读取端(Consumer):按 Key 顺序扫描读取,处理完毕后删除对应记录,以自身能处理的速率消费数据 数据持久化到磁盘:与纯内存队列不同,LevelDB 的数据落盘,进程崩溃后数据不丢失,重启后可继续消费 这种模式可以起到削峰填谷的作用:写入端的流量高峰被 LevelDB 缓冲到磁盘,读取端按匀速消费,避免下游系统因瞬间流量过大而崩溃。 适合此场景的条件 数据量超出内存容量,需要落盘缓冲 对消息顺序有要求(LevelDB Key 有序) 单机嵌入式场景,不想引入 Kafka/RabbitMQ 等重量级中间件 可以接受 LevelDB 不提供原生队列语义(需要自行管理消费位点和删除逻辑) 注意事项 LevelDB 不是消息队列,没有 ACK、重试、多消费者等内置机制,需要自行实现 频繁删除已消费数据会触发 Compaction,建议批量删除 如果对高吞吐、多消费者、分布式有需求,优先考虑 Kafka 等专用消息队列 其他典型场景 本地缓存层:缓存热点数据到本地磁盘,减少远端数据库访问 索引存储:存储倒排索引、元数据索引等有序 KV 数据 嵌入式数据库:Chrome 浏览器使用 LevelDB 存储 IndexedDB 数据 区块链节点:比特币、以太坊节点使用 LevelDB 存储区块链状态数据 LevelDB 数据写入流程 数据写入的整个流程为: 数据首先会被写入 memtable 和 WAL 当 memtable 达到上限后,会转换为 immutable memtable,之后持久化到 L0 (称为 flush),L0 中每个文件都是一个持久化的 immutable memtable,多个文件间可以有相互重叠的 Key 当 L0 中的文件达到一定数量时,便会和 L1 中的文件进行合并 (称为 compaction) 自 L1 开始所有文件都不会再有相互重叠的 Key,并且每个文件都会按照 Key 的顺序存储。每一层通常是上一层大小的 10 倍,当一层的大小超过限制时,就会挑选一部分文件合并到下一层 ...

2026-04-09 · 3 min · 583 words · -

zsh, oh-my-zsh, oh my zsh

zsh, oh-my-zsh, oh my zsh # 查看已经安装的 shell 列表 cat /etc/shells ## 查看当前 shell echo $SHELL # archlinux install zsh sudo pacman -S git zsh # ubuntu install zsh sudo apt install zsh install oh my zsh # install oh-my-zsh, will set default shell to zsh sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" # after oh my zsh installed, ubuntu user should logout and login. update oh my zsh # need vpn to access github omz update 错过更新提示后手动触发更新,直接运行: ...

2026-04-09 · 3 min · 514 words · -

compact/extract 压缩/解压

compact/extract 压缩/解压 .tar.gz 和 .tgz .tgz 和 .tar.gz 是同一个东西, .tgz 可以认为是 .tar.gz 是简写, 在远古时代比如 DOS 系统, 文件扩展名只能是三个字符, 所以有了 .tgz, 后来限制解除之后就能支持 .tar.gz 这种后缀了, 后者能更清晰地表达打包格式和压缩方式. https://stackoverflow.com/questions/11534918/are-tar-gz-and-tgz-the-same-thing 这种格式是我使用得最多的压缩格式。它在压缩时不会占用太多 CPU 的,而且可以得到一个非常理想的压缩率 默认 tar 打包和系统默认的压缩工具是单线程的,pigz 是 gzip 的多线程实现, 默认用当前逻辑 cpu 个数来并发压缩,无法检测个数的话,则并发8个线程 压缩 tar -czvf all.tar.gz *.jpg # 排除掉文件 tar -czvf tomcat.tar.gz --exclude=tomcat/logs tomcat # 设置压缩级别 GZIP=-9 tar cvzf file.tar.gz /path/to/directory 压缩到指定目录 tar -zcvf /data/tmp/foo.tar.gz /data/server/source # 解压到指定目录 tar Cxzvf /opt/cni/bin cni-plugins-linux-amd64-v1.6.1.tgz tar.gz, tgz 解压 tar -xf foo.tar.gz # 解压 tar.gz 文件时, 不使用 z 参数, 貌似 tar 会检测文件类型 自动 用 gzip 解压... tar xvf all.tar.gz tar zxvf all.tar.gz tar -zxvf all.tar.gz 解压到指定目录 tar zxvf /path/to/foo.tar.gz -C /path/to/target/dir/ tar -zxvf /path/to/foo.tar.gz -C /path/to/target/dir/ sudo pacman -S pigz # 压缩 tar --use-compress-program=pigz -cvpf package.tgz ./package # 解压 tar --use-compress-program=pigz -xvpf package.tgz -C ./package #tar –use-compress-program=pigz表示指定pigz来进行打包 #c表示create创建 x表示extract解压 v表示verbose详细 f表示指定压缩文件 C表示指定目录 #-cvpf package.tgz ./ 表示将./package目录打包为package.tgz #-xvpf package.tgz -C ./表示将package.tgz解压到./package目录下 \-v, --verbose #详细显示处理的文件 \-f, --file [HOSTNAME:]F #指定存档或设备 (缺省为 /dev/rmt0) \-z, --gzip, --ungzip #用 gzip 对存档压缩或解压 \-x, --extract, --get #从存档展开文件 解压多个文件 ls *.gz|xargs -t -n1 gunzip gunzip *.gz .rar # 解压 unrar x foo.rar # 压缩 rar e FileName.rar # 解压 rar a FileName.rar 解压并指定输出目录 gunzip -c /data/tmp/foo.tar.gz | tar xf - -C /data/server/bar .7z yum install p7zip pacman -S p7zip sudo apt install p7zip-full p7zip-rar # 压缩 7z a -t7z -r manager.7z /home/manager/* # a 代表添加文件/文件夹到压缩包 # -t 是指定压缩类型 一般我们定为7z # -r 表示递归所有的子文件夹,manager.7z 是压缩好后的压缩包名,/home/manager/* 是要压缩的目录,*是表示该目录下所有的文件。 7z x filename.7z .zip pacman -S zip unzip zip 压缩 zip all.zip *.jpg # 指定压缩文件目录 zip ~/all.zip *.jpg # 压缩的是个文件夹, -r 表示调用递归压缩 zip -r temp.zip temp # 密码 zip -P password0 foo.zip foo.txt # 压缩目录并加密码 (-r 递归, -P 指定密码) zip -r -P password0 foo.zip foo/ # 交互式输入密码(更安全,密码不会暴露在命令历史中) zip -r -e foo.zip foo/ # 指定压缩某几个文件 zip foo.zip foo.txt bar.txt zip 分卷压缩 # 分卷压缩的话,需要先将文件打包成一个zip包,然后执行 zip -s SIZE origin.zip --out new.zip # SIZE为分卷的大小4m,4g,4t等 # 解压的时候需要先将它合并才能正常解压 zip spiltfile.zip -s=0 --out single.zip zip 解压 unzip all.zip unzip -o -d /home/sunny myfile.zip # 解压 多个文件 ls *.zip | xargs -n1 unzip -o # -o: 不必先询问用户,unzip执行后覆盖原有的文件; # -P<密码>: 使用zip的密码选项; # -d 指定解压的目标目录 # 解压最近4天的zip文件 find . -maxdepth 1 -mtime -4 -type f -name "*.zip"|xargs -t -n1 unzip zip 解压并指定目录 unzip /path/to/source.zip -d /path/to/target/path Zstandard, zstd, .zst 解压 zstd -d foo.zst zstd 不能压缩目录, -r 参数会把目录里的文件压缩成单独的文件 ...

2026-04-07 · 5 min · 1034 words · -

VS Code Agent Hand Off

Hand Off 是什么 VS Code Copilot Chat 在 Agent 模式下提供 “Hand off a session to another agent” 功能,将当前对话的完整上下文移交给另一个 agent 继续处理。 移交时,接收方 agent 会获得: 完整的对话历史记录 已收集的文件上下文 前一个 agent 的中间结论和工具输出 接收方 agent 可以直接从当前状态继续,而不需要从头重建上下文。 与普通切换 Agent 的区别 普通切换 Hand off 上下文 丢失,重新开始 完整传递 用途 新任务 同一任务的延续 历史记录 不带过去 带上全部历史 为什么要切换 Agent,而不是在主 Agent 里继续? Fan-out 并行(最核心的理由) 主 agent 收集完上下文后,同时把同一份上下文发给多个子 agent 做不同的事: 主 agent 分析完代码库 ├── 子 agent A → 写单元测试 ├── 子 agent B → 写文档 └── 子 agent C → 做安全审查 主 agent 是单线程的,无法并行处理多个子任务。Fan-out 到多个子 agent 是最强的切换理由。 ...

2026-04-03 · 3 min · 598 words · -

Shell 创建文件并写入内容

选型总结 场景 推荐方式 单行字符串 printf '%s\n' "..." > file 多行内容 cat > file << 'EOF' ... EOF 追加内容 >> file 需要 root 写入 echo "..." | sudo tee file 脚本内变量展开 heredoc 不加引号:<< EOF 禁止变量展开 heredoc 加单引号:<< 'EOF' 单行字符串:推荐 printf printf '%s\n' 'Hello, World!' > foo.txt printf 简介 printf 在 Linux/Unix 系统上普遍可用,有两种形式: 内建命令(built-in):bash、zsh、dash 等 shell 均内置,不依赖外部程序 外部命令:/usr/bin/printf,由 coreutils 包提供,几乎所有发行版默认安装 printf 的作用是格式化输出,语法来自 C 语言的 printf(): printf FORMAT [ARGUMENTS...] 常用格式占位符: 占位符 含义 %s 字符串 %d 整数 %f 浮点数 \n 换行 \t Tab echo 在 bash 里需要加 -e 才能解析 \n,而 sh/dash 的 echo 行为又不同,跨 shell 不可靠;printf 行为在所有 shell 里一致,脚本中优先使用 printf。 ...

2026-04-03 · 2 min · 251 words · -

linux apps

Linux apps 必装软件(重装系统后优先安装) app source notes kitty pacman terminal neovim pacman 编辑器 flameshot pacman 截图+标注 bitwarden pacman 密码管理 btop pacman 颜值不错的 top 替代 wechat aur visual-studio-code-bin aur VSCode 官方二进制版 说明 pacman — sudo pacman -S aur — yay -S apt — sudo apt install go — go install Browser app source notes chromium pacman 开源版 Chrome google-chrome aur google-chrome-beta aur google-chrome-dev aur ubuntu install chrome curl -O https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb sudo apt install ./google-chrome-stable_current_amd64.deb Terminal app source notes hyper pacman terminal termite pacman 支持32位色 tmux pacman Text Editor / Notes app source notes mousepad pacman 轻量文本编辑器 typora aur markdown 编辑器 obsidian pacman 知识管理 nixnote2-git aur Evernote Linux 客户端 Screenshot / Recording app source notes spectacle pacman KDE 原生截图 simplescreenrecorder pacman 录屏 gtk-recordmydesktop aur 录屏 shutter aur 截图,需配合 perl-goo-canvas System Monitor app source notes ncdu pacman/apt 命令行磁盘空间分析 netdata pacman 系统资源监控 indicator-sysmonitor apt 任务栏系统资源监控 nethogs pacman 网络流量监控 dstat pacman 查看系统性能 dstat -cdlmnpsy slurm aur 网络监控 hardinfo-git aur 硬件信息查看 System Info app source notes fastfetch pacman 打印发行版 logo neofetch pacman/apt 系统信息(已停止维护) stress pacman 压力测试 hdparm pacman 磁盘参数查看 procmon aur 微软的进程监控工具 File Management app source notes filelight pacman 图形化磁盘空间管理 baobab apt 硬盘占用分析工具 catfish pacman 文件搜索 tree apt 树形目录显示 tree -L N nautilus apt Ubuntu 默认文件管理器 Archive / Compression app source notes ark pacman KDE 压缩包管理器 file-roller pacman zip/7z/rar 支持 zstd pacman 多线程快速压缩工具 rar/unrar apt Media / Graphics app source notes digikam pacman KDE 最佳图片管理 gwenview pacman KDE 图片查看 eog apt Eye of Gnome 图片查看 inkscape pacman 矢量图形编辑,SVG audacity pacman 音频处理 okular pacman PDF reader gpick pacman 颜色拾取工具 Download app source notes aria2 pacman 下载工具 axel pacman 下载工具 Remote Desktop app source notes remmina pacman GTK 远程桌面客户端 freerdp pacman remmina RDP 支持包 rdesktop pacman Communication app source notes thunderbird apt 邮件客户端 telegram-desktop pacman feishu aur 飞书 dingtalk aur 钉钉 zoom aur Office app source notes libreoffice-fresh pacman wps-office aur 需 ttf-wps-fonts ttf-wps-fonts aur WPS 字体依赖 Development Tools app source notes git pacman/apt lazygit go go install github.com/jesseduffield/lazygit@latest gitkraken aur Git GUI,Free for non-commercial github-desktop-bin aur base-devel pacman 编译工具链 cmake pacman kotlin pacman iperf3 pacman 网络测试 wireshark-qt pacman lsof pacman hexyl pacman 彩色 hex 编辑器 lrzsz pacman zmodem binutils pacman 二进制文件处理工具集 inetutils pacman telnet 等网络工具 zeal pacman 离线文档 platformio aur 物联网开发生态系统 IDE app source notes code pacman Visual Studio Code vscodium-bin aur VSCode 社区版(无遥测) jetbrains-toolbox aur JetBrains 工具管理器 intellij-idea-community-edition pacman IDEA 社区版 intellij-idea-ultimate-edition aur yay -S intellij-idea-ultimate-edition intellij-idea-ultimate-edition-jre goland aur yay -S goland goland-jre webstorm-jre aur yay -S webstorm webstorm-jre clion aur yay -S clion clion-jre claude-code aur Java app source notes jdk-openjdk pacman Latest OpenJDK jdk8-openjdk pacman OpenJDK 8 openjdk8-src pacman OpenJDK 8 源码 openjdk-8-jdk apt OpenJDK 8 maven pacman gradle pacman graphviz pacman PlantUML 依赖 jd-gui-bin aur Java 反编译 eclipse-mat aur Eclipse Memory Analyzer Node.js / Rust app source notes nvm pacman Node.js 版本管理 rust pacman Database app source notes redisinsight aur Redis GUI redis-desktop-manager aur RDM sqlectron-gui pacman heidisql aur MySQL 客户端 tableplus aur MySQL client datagrip — 官网下载 Fonts app source notes ttf-jetbrains-mono pacman JetBrains Mono adobe-source-code-pro-fonts pacman Adobe 编程字体 ttf-wqy-microhei apt 文泉驿-微米黑 ttf-wqy-zenhei apt 文泉驿-正黑 xfonts-wqy apt 文泉驿-点阵宋体 ttf-consolas-with-yahei-powerline-git aur Consolas+雅黑 KDE sudo pacman -S powerdevil kmix kscreen ark gwenview kcolorchooser app source notes powerdevil pacman 电源管理,休眠按钮 kmix pacman 音量调节 kscreen pacman 多显示器管理 kcolorchooser pacman 颜色拾取 KDE Widget name notes netspeed widget 网络监控 resources monitor CPU/内存监控 Plasma Week Number 显示周数 Virtualization / Container app source notes docker pacman podman pacman podlet aur kvm pacman wine pacman 需开启 Multilib 仓库 playonlinux pacman Wine 图形前端 Utilities app source notes qalculate-gtk pacman 全宇宙最好用的计算器 rsibreak pacman 番茄钟 gnome-shell-pomodoro apt Pomodoro openvpn pacman pavucontrol apt Chrome 音频输出设置 sl pacman 小火车 x11-apps apt xclock, xserver 测试用 pamac-aur aur 图形界面的 pacman libiconv aur 编码转换 Ventoy — 各种 ISO 安装盘引导工具 References Arch Linux List of Applications

2026-04-03 · 3 min · 615 words · w10n

AI agent development

AI Agent 开发的本质 开发 AI Agent 的核心是将已验证的知识和流程固化成可执行的规则和指令。 具体来说: 知识固化 - 把专家经验、最佳实践、业务规则编码成 Agent 可以遵循的指令 流程自动化 - 将重复性的决策流程转化为确定性的执行步骤 质量保障 - 通过固化的规则确保每次执行的一致性,避免人为疏忽 迭代优化 - 每次发现新问题或更好的做法,就更新这些"固化的知识",Agent 的能力随之提升 本质上是把隐性知识显性化,把经验驱动变成规则驱动。 这也意味着 Agent 的质量上限取决于你固化进去的知识质量。垃圾进,垃圾出;好的经验进,稳定的高质量输出。 为什么需要固化知识 LLM 的知识覆盖面极广,解决同一个问题可能有多条路径,但其中只有少部分是最佳实践。具体输出什么内容,很大程度上取决于提示词的质量。 把已验证的解决方案固化到 Agent 里,能让 Agent 在特定领域有更稳定、更优质的表现——相当于给 LLM 划定了一条"黄金路径",避免它在众多可能性中随机游走。 常用技术栈 编程语言 Python(主流,生态丰富) JavaScript/TypeScript(Web/Node.js Agent) Go、Java(高性能/企业级) 大语言模型与 API OpenAI GPT-4/3.5、Claude、Llama Hugging Face Transformers LangChain、LlamaIndex(Agent 框架) Web 框架与服务 FastAPI、Flask(Python) Express.js(Node.js) Django 数据存储 Redis、MongoDB、PostgreSQL、SQLite 向量数据库:Milvus、Pinecone、Weaviate 消息队列与异步任务 Celery、RabbitMQ、Kafka 容器与部署 Docker、Kubernetes 云服务:AWS、Azure、GCP 前端交互 React、Vue.js WebSocket、RESTful API 其他 Prompt 工程、工具插件系统 OAuth2、JWT(安全认证) 日志与监控:Prometheus、Grafana Python 调用模型方式 AI Agent 用 Python 开发时,可以调用本地模型或云端模型: ...

2026-03-31 · 27 min · 5749 words · -

IO, 输入输出

IO, 输入输出 https://my.oschina.net/u/1859679/blog/1839169 同步阻塞 IO 同步非阻塞 IO 异步非阻塞 IO 针对某种 IO 模型,我们如何分类,可以基于 POSIX 对同步/异步的定义来判别: A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes; (同步 I/O 操作会导致发起请求的进程被阻塞,直到该 I/O 操作完成。) An asynchronous I/O operation does not cause the requesting process to be blocked; (异步 I/O 操作不会导致发起请求的进程被阻塞。) 那么从上我们可以看出: 同步: 是否同步体现在消息通信机制上。 阻塞: 是否阻塞主要体现在调用的线程是否可以干别的,关注的是程序的等待状态 也就是说同步和异步说的是消息的通知机制,阻塞非阻塞说的是线程的状态 。 Unix 5种I/O模型 在《UNIX网络编程: 卷一》的第六章书中列出了五种IO模型: 阻塞式I/O; 非阻塞式I/O; I/O复用 (select,poll,epoll…) ; 信号驱动式I/O (SIGIO) ; 异步I/O (POSIX的 aio 系列函数)

2026-03-31 · 1 min · 74 words · -

硬盘扩容, PVE, Archlinux

硬盘扩容 pve ext4 根分区磁盘扩容 虚拟机关机 在 pve 里给硬盘扩容: vm>hardware> Hard Disk> Disk Action> resize: 填写新增的容量 挂载 archlinux iso, 修改引导顺序, 让虚拟机从 iso 启动 启动虚拟机 # 列出所有块设备(磁盘及分区)的信息,以树状结构显示设备名、大小、挂载点等 lsblk # 看到磁盘总大小增加了,但分区大小没变。 fdisk -l # 调整分区表, 扩容, 用 fdisk 调整分区表一般不会丢数据,但是最好备份一下。 fdisk /dev/sda # 如果不是从 iso 引导,直接在操作系统里执行, 会提示 This disk is currently in use - repartitioning is probably a bad idea. It's recommended to umount all file systems, nd swapoff all swap partitions on this disk. # print the partition table Command (m for help): p Disk /dev/sda: 20 GiB, 21474836480 bytes, 41943040 sectors Disk model: QEMU HARDDISK Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0x1bc64ef0 Device Boot Start End Sectors Size Id Type /dev/sda1 * 6144 2103295 2097152 1G b W95 FAT32 /dev/sda2 2103296 20971519 18868224 9G 83 Linux # delete partition 2 Command (m for help): d Partition number (1,2, default 2): 2 Partition 2 has been deleted. Command (m for help): n Partition type p primary (1 primary, 0 extended, 3 free) e extended (container for logical partitions) # 输入 p Select (default p): p # 使用默认值 2 Partition number (2-4, default 2): # 输入分区的起始扇区, 要和删除的分区起始扇区一样, 注意!默认值会是2048, 不要使用这个默认值, 要输入前面打印分区表时看到的分区 2 的起始扇区值, 否则会丢数据, 这里是2103296 First sector (2048-41943039, default 2048): 2103296 # 输入分区的结束扇区, 使用默认值, 即使用所有剩余空间 Last sector, +/-sectors or +/-size{K,M,G,T,P} (2103296-41943039, default 41943039): Created a new partition 2 of type 'Linux' and of size 19 GiB. Partition #2 contains a ext4 signature. # 输入 y 确认删除 ext4 签名 Do you want to remove the signature? [Y]es/[N]o: Y The signature will be removed by a write command. # 再次打印分区表, 应该能看到分区 2 已经变大了 Command (m for help): p Disk /dev/sda: 20 GiB, 21474836480 bytes, 41943040 sectors Disk model: QEMU HARDDISK Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0x1bc64ef0 Device Boot Start End Sectors Size Id Type /dev/sda1 * 6144 2103295 2097152 1G b W95 FAT32 /dev/sda2 2103296 41943039 39839744 19G 83 Linux Filesystem/RAID signature on partition 2 will be wiped. # 输入命令 w 保存分区表 Command (m for help): w The partition table has been altered. Syncing disks. # 强制检查 ext2/ext3/ext4 文件系统的一致性,-f 表示即使文件系统标记为干净也强制执行检查 e2fsck -f /dev/sda2 # 有可能会询问是否优化,输入 y, 后面可能会有很多类似的提示,全部输入 y 即可, 还有可能提示输入 a: 确认全部 root@archiso ~ # e2fsck -f /dev/sda2 e2fsck 1.47.3 (8-Jul-2025) ext2fs_open2: Bad magic number in super-block e2fsck: Superblock invalid, trying backup blocks... Pass 1: Checking inodes, blocks, and sizes Inode 287507 extent tree (at level 1) could be narrower. Optimize<y>? # resize the filesystem # resize2fs 要求文件系统在扩容前必须是干净状态(通过 e2fsck 校验),否则拒绝执行 resize2fs /dev/sda2 # 分区扩容完成,重启虚拟机 PVE ext4 disk resize Online Lossless Expansion of EXT4 Partition ...

2026-03-26 · 8 min · 1651 words · -