Welcome to MikeDaDev's Blog – notes on Java, backend & system design.

Explore
MikeDaDev's Blog

#performance

Things I've written about #performance
  • Thiết kế hệ thống lưu trữ tài liệu trong thời đại số hoá

    Ảnh CCCD lúc mở tài khoản, hợp đồng vay ký điện tử, sao kê tháng: mỗi ngày một ngân hàng vài chục triệu khách sinh ra hàng triệu file, và file nào cũng phải giữ đúng người xem, đúng thời hạn, không được mất. Bài này dựng một kiến trúc tham chiếu cho nền tảng lưu trữ tài liệu cỡ đó: ước lượng tải, upload chia chunk, lưu theo tầng nóng ấm lạnh, phân quyền RBAC kết hợp ABAC, mã hoá envelope, và những kiểu hỏng hay gặp khi chạy thật.

  • in ai / llm

    Chuyện gì xảy ra bên dưới khi bạn prompt xong và nhấn Enter?

    Theo chân một prompt từ lúc bạn nhấn Enter tới khi chữ đầu tiên hiện lên: ghép system prompt, tokenize, xếp hàng chờ GPU, prefill, decode, KV cache, sampling, streaming. Nắm được luồng này là hiểu vì sao output đắt hơn input, vì sao context dài lại chậm, và nên tối ưu chi phí ở đâu.

  • Discord: từ Cassandra sang ScyllaDB

    Nhìn lại chặng đường Discord lưu hàng nghìn tỷ tin nhắn: từ lúc MongoDB hết chịu nổi, qua Apache Cassandra, rồi chuyển hẳn sang ScyllaDB với mô hình thread-per-core, hết JVM GC pause, p99 latency đọc còn khoảng 15ms và số node giảm từ 177 xuống 72.

  • Java 27: Compact Object Headers

    Từ Java 27, Compact Object Headers (Project Lilliput) được bật mặc định: header của object trên HotSpot co từ 12 xuống 8 bytes. Bài mổ xẻ 64 bit header được sắp ra sao, class pointer nén còn 22 bit thế nào, lock và GC xoay xở thế nào, lợi gì cho CPU cache và các cờ JVM cần biết.

  • in java / jvm

    Generational ZGC trong java 21

    Heap lên tới vài chục, vài trăm GB thì mấy GC truyền thống như G1 hay Parallel hay kéo theo những lần Stop-The-World (STW) dài cả giây, dịch vụ đứng hình. Generational ZGC trong Java 21+ dùng Colored Pointers, Load Barriers (và Store Barriers) ra sao để giữ pause GC dưới 1 mili-giây.

  • GraalVM Native Image vs JVM JIT

    GraalVM Native Image khởi động trong vài chục tới vài trăm mili-giây, ăn RAM rất ít nên đang được dân Serverless và Kubernetes chuộng. Nhưng AOT có thay hẳn được HotSpot JIT không? Mổ xẻ kiến trúc, số đo minh hoạ, những thứ bạn buộc phải đánh đổi và các lựa chọn khác như Project Leyden.

  • CDC với Debezium & cache invalidation

    Change Data Capture (CDC) với Debezium nhìn từ bên trong: PostgreSQL logical decoding (pgoutput) chạy ra sao, vì sao dual-write dính partial failure và race condition, cách dùng Outbox Event Router (SMT), chống cache stampede và canh replication slot trên production.

  • map vs flatmap

    map và flatMap trong Java nhìn từ gốc là Monadic Bind: flatMap cấp phát bộ nhớ thế nào trong Stream, thay bằng mapMulti (Java 16) để đỡ GC Churn, thenCompose trong CompletableFuture, và cái bẫy mất thứ tự khi dùng flatMap trong Reactive Streams.

  • leetcode: two sum

    Two Sum nhìn từ góc độ low-latency: Autoboxing và pointer chasing làm HashMap O(N) chậm cỡ nào trên phần cứng thật, khi nào Two-Pointer thắng, cách tự viết Primitive Open-Addressing Hash Map, SIMD Vector API giúp được gì và không giúp được gì, kèm số đo JMH.

  • in aws / cloud

    amazon S3

    Ghi chú dùng Amazon S3 cho hệ thống lớn: Strong Read-After-Write Consistency là gì, dùng Presigned Multipart Upload để backend khỏi gánh băng thông, bảo mật với SSE-KMS và IRSA trên Kubernetes, và cái bẫy tốn tiền mang tên AbortIncompleteMultipartUpload.

  • multi-module spring boot

    Mổ xẻ kiến trúc Multi-Module Spring Boot cho production: chia module theo Hexagonal/Clean Architecture, tách riêng JPA/DataSource cho từng module, tránh mở connection pool tràn lan, xử lý cái bẫy bytea của JDBC PostgreSQL và dùng ArchUnit để giữ ranh giới module.

  • in sql / oracle

    oracle ROWNUM

    ROWNUM trong Oracle chạy bên trong thế nào: vì sao ROWNUM > 1 luôn ra rỗng, COUNT STOPKEY trong execution plan giúp dừng quét sớm ra sao, và nên chọn subquery ROWNUM, window function ROW_NUMBER() hay cú pháp ANSI OFFSET-FETCH của Oracle 12c+.

  • docker & container

    Mổ xẻ container và Docker từ bên trong: Linux Namespaces, Cgroups v2, OverlayFS; viết Dockerfile multi-stage, layered, chạy distroless non-root; xử lý dứt điểm chuyện PID 1 và graceful shutdown; cấu hình JVM nhận biết container để khỏi dính OOMKilled.

  • iterator pattern

    Đào sâu Iterator Pattern trong Java: fail-fast hoạt động nhờ biến modCount ra sao, CopyOnWriteArrayList đổi bộ nhớ lấy an toàn thế nào, Spliterator giúp chạy song song ra sao, và cách tự viết cursor iterator để stream hàng triệu bản ghi mà không OOM.

  • prototype pattern

    Prototype Pattern trong Java: vì sao Cloneable bị coi là lỗi thiết kế của JDK, khác nhau giữa Shallow Copy và Deep Copy, và so sánh 4 cách nhân bản object dùng được trên production (Copy Constructor, Jackson, Kryo, Record Withers).

  • multi-module Spring Boot với Gradle

    Dựng Multi-Module Spring Boot 3 với Gradle hiện đại: bỏ anti-pattern subprojects/allprojects, dùng Convention Plugins để tái sử dụng cấu hình, quản lý dependency tập trung bằng Version Catalog (libs.versions.toml), hiểu compile avoidance giữa api và implementation, và xử lý xung đột bootJar vs jar.

  • functional interfaces & lambda trong java

    Lambda và Functional Interface chạy thế nào bên trong JVM: vì sao lambda không phải Anonymous Inner Class, invokedynamic và LambdaMetafactory làm gì, tại sao biến phải effectively final, và cách dựng một Policy Engine bằng hàm.

  • arraylist vs linkedlist

    Vì sao ArrayList gần như luôn nhanh hơn LinkedList trong Java: chuyện CPU Cache Line và spatial locality, chỗ hao bộ nhớ của Node header, cái hiểu lầm sách giáo khoa về tốc độ chèn của LinkedList, và ArrayDeque, lựa chọn nên dùng thay thế.

  • hibernate ORM

    Hibernate ORM bên trong chạy thế nào: 4 trạng thái vòng đời của Entity (Transient, Persistent, Detached, Removed), Dirty Checking tự động qua Loaded Snapshots, First-Level vs Second-Level Cache, và cách tiết kiệm bộ nhớ với @Transactional(readOnly = true).

  • custom annotations trong java

    Annotation trong Java chạy thế nào bên trong JVM: bytecode attribute, dynamic proxy, meta-annotation của Spring (@AliasFor), rồi tự viết một custom annotation dùng Spring AOP và SpEL để làm rate limiting phân tán dùng được cho production.

  • @ManyToMany trong JPA

    @ManyToMany trong JPA/Hibernate hoạt động ra sao: vì sao Hibernate xóa sạch bảng nối rồi insert lại (Write Amplification), vì sao không thêm được thuộc tính phụ (metadata), và cách làm dùng được trên production: tách thành hai @OneToMany qua Explicit Join Entity với @EmbeddedId và @MapsId.

  • @OneToMany & @ManyToOne trong JPA

    Mấy cái bẫy hay gặp với @OneToMany và @ManyToOne trong JPA/Hibernate: EAGER ngầm, N+1 query và cách xử lý bằng EntityGraph hay BatchSize, lỗi MultipleBagFetchException do Cartesian Product, và StackOverflowError vì lỡ dùng Lombok @Data.

  • @OneToOne trong JPA

    Đào sâu quan hệ @OneToOne trong Hibernate & JPA: vì sao FetchType.LAZY "tịt" ở Non-Owning side và kéo theo N+1, cách dùng chung khoá chính với @MapsId, Bytecode Enhancement, và vì sao gắn Lombok @Data lên Entity là anti-pattern.

  • in java / jvm

    java reflection

    Mổ xẻ Java Reflection bên trong JVM: cơ chế inflation từ JNI sang bytecode accessor (tới JDK 17) và bản viết lại bằng method handle từ JDK 18, vì sao reflection khó cho JIT inline, chuyện setAccessible bị chặn từ JDK 16/17, và vì sao MethodHandles cùng code sinh lúc compile hợp với GraalVM AOT hơn.

  • java stream API

    Nhìn vào bên trong Java Stream API: pipeline được dựng thế nào, Loop Fusion chạy qua interface Sink ra sao, Stateless khác Stateful operation chỗ nào, và vì sao parallelStream xài chung ForkJoinPool có thể làm treo cả hệ thống.

  • in java / jvm

    string vs stringbuilder vs stringbuffer

    Chuỗi ký tự trong JVM hoạt động thế nào: Compact Strings (JEP 254), việc toán tử + chuyển từ StringBuilder sang invokedynamic StringConcatFactory (JEP 280), so sánh String, StringBuilder, StringBuffer và chuyện JIT bỏ lock của StringBuffer nhờ Escape Analysis.

  • in java / jvm

    java string pool

    Đi sâu vào StringTable native trong HotSpot JVM: vì sao String Pool dời từ PermGen sang Java Heap, bảng băm C++ từng tụt từ O(1) về O(N) ra sao và JDK 11 sửa thế nào, cái bẫy của String.intern(), và String Interning khác String Deduplication (JEP 192) chỗ nào.

  • controlled concurrency with semaphores

    A practical look at backpressure and concurrency limits in Java: how java.util.concurrent.Semaphore works on top of AbstractQueuedSynchronizer (AQS), why Virtual Threads waiting on it unmount instead of pinning their carrier, how to size the limit with Little’s Law, and patterns that hold up in production.