Home News feed Planet MySQL
Newsfeeds
Planet MySQL
Planet MySQL - https://planet.mysql.com

  • How to Extract Data from InnoDB, MyISAM, and MyRocks Files Without a Running Server
    Probably everyone responsible for managing database servers at some point realizes that it would be helpful to have a tool capable of extracting data directly from raw database files. Something completely offline, without the need to have a running database server, and that would work with data directories, files, or binary backups (storage volume snapshots, XtraBackup, etc). There are a couple of use cases for that I could think of: Crashed/corrupted data recovery – if the database service refuses to start or enters a crash-restart loop and there are no good backups (PITR!) available. Backup (additional) verification and data files health check. Extraction of individual databases or tables from a full backup. Quick provisioning of staging/QA instances directly from live production. Recovery of accidentally deleted rows. Forensics (i.e., quick seek for data across many old backups without the need to reload them). Import/migrate data from very old/legacy database versions. Backups! Here, let me drop an obvious yet often neglected statement: nothing will replace good backups! Your database infrastructure should always be planned with the assumption that your data may get damaged or lost at any time. Visit here if you want to check if your current backup policy is up to date: https://www.percona.com/blog/mysql-backup-and-recovery-best-practices/ Now, would an offline tool with the ability to read SQL data from files be another backup method? NO! Modern ACID- and MVCC-compliant data engines are too complex in terms of data durability and consistency. So, reading directly from database files does not guarantee the dump would be consistent. Dedicated backup tools like XtraBackup engage additional steps to ensure consistency, like following the redo log and using backup locks. Corruption and Crash Recovery Typically, when MySQL / MariaDB server detects severely corrupted data, it may need to perform an emergency shutdown with the intention of preventing further damage. A lot depends on the scope of the damage. Some minor issues with secondary indexes could often be fixed by re-creating the index, but clustered key corruption often leads to an engine crash whenever the corrupted page is accessed. You may find yourself in a situation similar to these reports: https://bugs.mysql.com/bug.php?id=111610 https://bugs.mysql.com/bug.php?id=113706 Again, with good backups, this should be no problem. But a bad surprise may force you to try to read all healthy rows in the hope of restoring as much as possible. The task will be, in most cases, very tedious – an attempt to read a row sitting in a corrupted page would crash the server, so you have to wait for a restart before you can try reading the next row. This was one of the reasons for me to try to come up with a tool fully capable of extracting rows without needing to run MySQL. Otherwise, a challenging project was affordable using vibe-coding, where the agent could build data decoding by reading the relevant engine’s source code. So, let me introduce a new open-source project: innodump! Up to this point, I have implemented all three most popular MySQL and MariaDB engines: InnoDB, MyISAM, and MyRocks (RocksDB). The tool can read and dump the whole data directory, or specific tables, either from a common or general tablespace or from a per-table file. It decodes both the table schema and rows into SQL files or a TSV structure compatible with MySQL Shell dump utilities. The latter allows you to load the dump into another instance very efficiently. There is an option relevant from the corruption point of view: --skip-corrupted – in case a corrupted data page is encountered, it prints a comment and skips to the next page, effectively reading as much data as possible. Example run:$ innodump --file innodump_work/instant_t.ibd --verbose --skip-corrupted File: innodump_work/instant_t.ibd (130023424 bytes) Space ID: 409 Pages: 7936 Page size: 16384 bytes FSP flags: 0x00004021 (post_antelope=true atomic_blobs=true data_dir=false shared=false temporary=false encryption=false sdi=true) SDI: present (written by MySQL 8.0.33, dd_version 80023, sdi_version 80019) Tables in SDI: 1 Row format: DYNAMIC Clustered index: "PRIMARY" (id 943), root page 4 Secondary indexes: 5 Physical fields: 31 (29 output) === innodump 0.8.1 === Table: db1.instant_t (row_format=DYNAMIC) Columns: 29 output (31 total incl. system/hidden) warning: corrupted page 100 (index "PRIMARY", id 943): LSN low bytes at the start and end of the page disagree Schema file: sqldump_2026-10-21_16.42/db1.instant_t-schema.sql Data file: sqldump_2026-10-21_16.42/db1.instant_t-data.sql (100680 row(s) written, 1 corrupted page(s) skipped - see warnings above) Data Verification --examine – this option makes the tool read every table and decode every row without storing them; it produces a report of how many rows can be found and how many of them are corrupted. Note: the tool scans only the clustered (primary) index, so it will not find possible secondary index corruptions; it is expected to only ensure which rows can be restored.$ innodump --examine --source-dir /data/sandboxes/msb_11_6_2/data/ === innodump 0.8.1 === Following /data/sandboxes/msb_11_6_2/data/ recursively — recognized as a single instance's datadir (found ibdata1), so every InnoDB table found inside (file-per-table .ibd files, plus any table found only in ibdata1's own shared tablespace) and every MyISAM table (.MYD files), plus every MyRocks table (read from #rocksdb) will be examined (nothing is written). Source: /data/sandboxes/msb_11_6_2/data/ (6 .ibd file(s), 129 .frm-only table(s) to resolve via ibdata1, a MyRocks data directory (#rocksdb) found) db1.t0 InnoDB 8 row(s) OK [row_format=COMPACT] db1.t1 InnoDB 5 row(s) OK [row_format=COMPACT] mysql.gtid_slave_pos InnoDB 0 row(s) OK [row_format=COMPACT] mysql.innodb_index_stats InnoDB 13 row(s) OK [row_format=COMPACT] mysql.innodb_table_stats InnoDB 3 row(s) OK [row_format=COMPACT] mysql.transaction_registry InnoDB 0 row(s) OK [row_format=COMPACT] db1.sbtest1 ROCKSDB 1000000 row(s) OK Summary InnoDB 6 table(s), 29 row(s) ROCKSDB 1 table(s), 1000000 row(s) total 7 table(s), 1000029 row(s) Note: 129 .frm file(s) with no matching .ibd had no InnoDB entry in ibdata1's own dictionary either - likely non-InnoDB tables, VIEWs, or a general tablespace - not examined Result: every table read cleanlyIf you are looking for a way to scan InnoDB files for potential corruption, you should always start with the official innochecksum. With innodump’s examination, the main goal is to show what’s actually readable. Staging/dev/QA Data Provisioned the Easy and Unintrusive Way? Quite often, a DBA may be tasked with setting up a non-production MySQL or MariaDB instance having a recent data snapshot from production. And typically, this kind of data copy for staging purposes does not have to be point-in-time consistent. And note that using this tool on a live instance or on an unprepared backup will mean various data consistency issues, like dirty pages may not be on disk yet, pages can be mid-write, it can produce rows that are missing, stale, or wrongly flagged, and even whole tables can be missing if created recently enough inside a common space. Now, if the last regular available backup is up to date enough, it may still be too costly to restore the full backup while only a handful of databases are needed. Here, innodump can dump from specific database folders or files:$ innodump --source-dir /data/sandboxes/msb_9_7_0/data/db2/ --format tsv --compression zstd === innodump 0.8.1 === Following /data/sandboxes/msb_9_7_0/data/db2/ recursively — recognized as a single database directory (no subdirectories of its own), so every InnoDB table found inside (file-per-table .ibd files, plus any table found only in ibdata1's own shared tablespace) and every MyISAM table (.MYD files) will be dumped. Source: /data/sandboxes/msb_9_7_0/data/db2/ (5 .ibd file(s) found) db2.categories: 10000 row(s) db2.cities: 100000 row(s) db2.products: 100000 row(s) db2.sales: 1049877 row(s) db2.stores: 100000 row(s) Files: 5/5 .ibd file(s) processed Tables: 5 extracted Rows: 1359877 written Output: tsvdump_2026-10-21_16.58The exported folder can use the format compatible with fast MySQL Shell import:$ ls tsvdump_2026-10-21_16.58/ db2@categories.json db2@categories.tsv.zst db2@cities.sql db2.json db2@products.sql db2@sales.json db2@sales.tsv.zst db2@stores.json db2@stores.tsv.zst @.json db2@categories.sql db2@cities.json db2@cities.tsv.zst db2@products.json db2@products.tsv.zst db2@sales.sql db2.sql db2@stores.sql @.done.jsonWhat if no recent backup is available but there is a need to fetch staging data quickly from production? Bearing in mind that reading from a live datadir has the caveats above, which is fine for a throwaway staging copy, the tool can do it gently, as it allows you to limit the IO throughput during exporting. Moreover, it can bypass the OS page cache via the O_DIRECT flag, so as not to trash the server’s cache with garbage and avoid causing additional memory pressure (the kernel might decide to swap out some least recently used user space memory to make room for page cache!). Therefore, to make the export from production with as little impact as possible, I recommend using --io-limit (RATE bytes per second) and --direct-io options, plus being gentle on the CPU side, running the tool with nice. Example run results without these options used:$ time innodump --file /data/sandboxes/msb_9_7_0/data/tpcdh/orders.ibd --format tsv --compression zstd === innodump 0.8.1 === Table: tpcdh.orders (row_format=DYNAMIC) Columns: 9 output (11 total incl. system/hidden) Dump dir: tsvdump_2026-10-21_17.25 (MySQL Shell util.loadDump()-compatible, zstd level 1) Rows: 1500000 written real 0m1.726s user 0m6.601s sys 0m0.362s $ fincore /data/sandboxes/msb_9_7_0/data/tpcdh/orders.ibd RES PAGES SIZE FILE 192.1M 49168 228M /data/sandboxes/msb_9_7_0/data/tpcdh/orders.ibdAnd with these safety settings (time longer bc io limit, and page cache is skipped bc direct io):$ time innodump --file /data/sandboxes/msb_9_7_0/data/tpcdh/orders.ibd --format tsv --compression zstd --direct-io --io-limit 30M === innodump 0.8.1 === Table: tpcdh.orders (row_format=DYNAMIC) Columns: 9 output (11 total incl. system/hidden) Dump dir: tsvdump_2026-10-21_17.28 (MySQL Shell util.loadDump()-compatible, zstd level 1) Rows: 1500000 written real 0m9.241s user 0m8.024s sys 0m0.486s $ fincore /data/sandboxes/msb_9_7_0/data/tpcdh/orders.ibd RES PAGES SIZE FILE 0B 0 228M /data/sandboxes/msb_9_7_0/data/tpcdh/orders.ibd Recover Deleted Rows? InnoDB does not immediately purge or overwrite deleted rows in the table spaces. The record is first only delete-marked and stays in the page. If no new writes have actually reclaimed the space yet, such rows usually can be decoded. So, if done quickly, there is a good chance you can restore those rows, and innodump makes it easy via the --deleted-only option!$ innodump --file /data/sandboxes/msb_8_4_3/data/sbtest/sbtest1.ibd --deleted-only === innodump 0.8.1 === Table: sbtest.sbtest1 (row_format=DYNAMIC) Columns: 4 output (6 total incl. system/hidden) Schema file: sqldump_2026-10-21_17.46/sbtest.sbtest1-schema.sql Data file: sqldump_2026-10-21_17.46/sbtest.sbtest1-data-deleted.sql (2100 delete-marked row(s) written)I hope you may find the tool handy for the cases mentioned above as well as possibly others, like extracting data from some ancient/legacy database versions, where the regular backup tools don’t even work. Note: it is not an officially supported Percona software, but rather an experimental personal project. The tool can be found on GitHub, and all feedback is welcome! https://github.com/PrzemekMalkowski/innodump The article was created by a human. The post How to Extract Data from InnoDB, MyISAM, and MyRocks Files Without a Running Server appeared first on Percona.

  • How to change MySQL account passwords with no downtime
    Changing the password of a MySQL account that an application uses was hard before MySQL 8.0. It is easy in MySQL 8.0 and later, thanks to two clauses of ALTER USER: RETAIN CURRENT PASSWORD and DISCARD OLD PASSWORD.

  • How to change MySQL account passwords with no downtime
    Changing the password of a MySQL account that an application uses was hard before MySQL 8.0. It is easy in MySQL 8.0 and later, thanks to two clauses of ALTER USER: RETAIN CURRENT PASSWORD and DISCARD OLD PASSWORD.

  • Crafting an Extension Framework for MySQL: a talk from MySQL BR Conf 2026
    I opened my MySQL BR Conf 2026 talk creating a new data type that stock MySQL doesn’t have: ROT13. … OK, admittedly, it’s a silly type. With a ROT13 column, you insert plain strings and read back plain strings, but the table stores scrambled text. … OK, it’s very *very* silly. But the point of this example isn’t to convince you extensions are useful; it’s to show a tiny extension that allows exploring design choices we made in designing the VEF (VillageSQL Extension Framework).What the talk coversSimpler functions and aggregates. VDF’s (VEF-Defined Functions) resemble MySQL UDF’s (User-Defined Functions), but are easier to write. I explore how we made it both more ergonomic in C++ and also how we make it harder to mess up.Types that take parameters. Some types take parameters; e.g., vectors need a length. To show how to handle this in our existing silly example, I generalize ROT13 into ROT(K).The escape path. I show how functions that don’t have tidy, compile-time signatures like printf fit into the VEF. I also describe our design pattern for cases like these. In the talk, I link to a blog post on this blog, which I will also now link here in an example of Linkception: “Happy Path, Escape Hatch, and the Space Between”.Porting a PostgreSQL extension. The talk ends with what happened when VillageSQL's Claude Code skill ported PostgreSQL's hstore extension, and what the port cost.About Daniel BentleyDan is a founding engineer at VillageSQL. He has worked at Google and Twitter, and he did a startup called Tilt that focused on Kubernetes developer tools. He is on a quest to make databases delightful to use by translating complex systems into elegant simplicity.Get started at villagesql.com, read the docs, or talk to us on Discord.

  • Kiến Thức Lập Trình Web, PHP, MySQL Cho Người Mới
    Kiến Thức Lập Trình Web, PHP, MySQL Cho Người Mới Kiến thức lập trình web, PHP, MySQL và công cụ phát triển là chủ đề chúng tôi hay được hỏi nhất. Rất nhiều bạn muốn tự học lập trình nhưng không biết bắt đầu từ đâu. Nên học công nghệ gì trước? Dùng công cụ nào để không lạc giữa hàng trăm tài liệu trên mạng? Trong bài này, đội ngũ chúng tôi chia sẻ lộ trình từng áp dụng khi hướng dẫn học viên mới. Lộ trình đi từ những bước sơ khai nhất. Cuối cùng, bạn sẽ tự tay dựng được một website hoàn chỉnh. Vì sao PHP và MySQL vẫn là lựa chọn tốt để bắt đầu học lập trình web Vì sao PHP và MySQL vẫn là lựa chọn tốt để bắt đầu học lập trình web Một trong những lý do khiến chúng tôi luôn khuyên người mới chọn PHP và MySQL là cộng đồng rất lớn. Tài liệu tiếng Việt cho hai công nghệ này cũng rất phong phú. Khi bạn gặp lỗi lúc nửa đêm, gần như chắc chắn đã có người hỏi câu tương tự trước đó. Câu trả lời thường nằm sẵn trên một diễn đàn nào đó, viết dễ hiểu và sát với trình độ người mới. Bên cạnh đó, rất nhiều dự án thực tế hiện nay vẫn chạy ổn định trên nền PHP kết hợp MySQL. Chúng tôi từng hỗ trợ một khách hàng vận hành website bán phụ tùng xe máy suốt gần mười năm. Toàn bộ hệ thống của họ vẫn dựa trên PHP thuần và MySQL cơ bản. Doanh thu tăng đều mỗi năm, trong khi chi phí bảo trì lại thấp hơn nhiều so với việc đập đi xây lại bằng công nghệ mới. Câu chuyện này cho thấy PHP và MySQL không hề lỗi thời như một số người vẫn nghĩ. Đây là công cụ đã được kiểm chứng qua thời gian, đủ ổn định để doanh nghiệp nhỏ yên tâm phát triển lâu dài. Lộ trình học lập trình web PHP, MySQL từ con số 0 Lộ trình học lập trình web PHP, MySQL từ con số 0 Làm quen với PHP thuần trước khi học framework Bước đầu tiên trong lộ trình, bạn cần nắm vững cú pháp PHP cơ bản. Cách khai báo biến, viết vòng lặp, xử lý điều kiện là nền tảng bắt buộc phải vững trước khi đi xa hơn. Sau đó, bạn nên học cách xử lý form nhập liệu và quản lý session để lưu trạng thái đăng nhập người dùng. Chúng tôi luôn khuyên học viên đừng vội học framework ngay từ đầu. Hãy làm quen với PHP thuần trước. Bạn có thể viết vài trang xử lý form đơn giản như form liên hệ hay form đăng ký thành viên. Việc này giúp bạn hiểu rõ luồng dữ liệu đi từ trình duyệt đến server rồi quay lại như thế nào. Khi đã nắm chắc luồng xử lý này, học framework sau đó sẽ nhanh hơn rất nhiều, vì bạn hiểu framework đang làm gì phía sau thay vì chỉ copy code theo hướng dẫn có sẵn. Thiết kế cơ sở dữ liệu MySQL ngay từ đầu Song song với PHP, việc thiết kế cơ sở dữ liệu MySQL cơ bản cũng cần được đầu tư nghiêm túc ngay từ những buổi học đầu tiên. Nhiều bạn mới học thường bỏ qua bước thiết kế bảng hợp lý. Hậu quả là sau này phải sửa đi sửa lại cấu trúc dữ liệu khi dự án đã phát triển lớn hơn, gây mất thời gian và đôi khi làm hỏng dữ liệu cũ. Nếu bạn muốn hiểu sâu hơn về hệ quản trị cơ sở dữ liệu này trước khi bắt tay thực hành, đội ngũ chúng tôi có một bài viết tổng quan về hệ quản trị cơ sở dữ liệu MySQL khá chi tiết. Bài viết này giúp bạn nắm được bức tranh toàn cảnh trước khi đi vào thiết kế bảng cụ thể. Chúng tôi thường khuyên học viên đọc bài này trước, sau đó mới bắt tay vẽ sơ đồ quan hệ giữa các bảng trên giấy, rồi mới chuyển sang viết câu lệnh tạo bảng thật. Luyện tập với dữ liệu thật qua một dự án nhỏ Luyện tập với dữ liệu thật qua một dự án nhỏ Sau khi nắm cú pháp cơ bản, bạn nên thử tạo một cơ sở dữ liệu nhỏ. Ví dụ như quản lý danh sách sản phẩm hoặc danh sách học viên. Từ đó, hãy viết các câu truy vấn thêm, sửa, xóa và tìm kiếm dữ liệu. Việc lặp lại thao tác này nhiều lần sẽ giúp bạn ghi nhớ cú pháp SQL một cách tự nhiên, thay vì chỉ đọc lý thuyết suông rồi quên ngay sau đó. Chúng tôi từng hướng dẫn một bạn học viên làm quen với lập trình web bằng cách xây dựng một trang quản lý chi tiêu cá nhân đơn giản. Bạn ấy chỉ mất khoảng ba tuần để hoàn thành phiên bản đầu tiên, dù trước đó gần như chưa biết gì về PHP. Điều khiến chúng tôi bất ngờ là bạn ấy nhớ cú pháp SQL nhanh hơn hẳn những bạn chỉ xem video mà không tự tay gõ lại từng dòng lệnh. Bài học rút ra khá rõ ràng: học lập trình web không thể chỉ xem, phải tự gõ, tự sai, rồi tự sửa mới nhớ lâu được. Chọn công cụ phát triển phù hợp để tăng tốc quá trình học Khi đã quen dần với PHP và MySQL, việc lựa chọn công cụ phù hợp sẽ giúp quá trình học nhanh và đỡ vất vả hơn rất nhiều. Một công cụ tốt không thay thế được việc thực hành. Nhưng nó giúp bạn tiết kiệm thời gian ở những khâu lặp đi lặp lại như quản lý bảng, xem dữ liệu hay soạn code. Với phần cơ sở dữ liệu, chúng tôi khuyên bạn nên tìm hiểu thêm MySQL Workbench là gì và vì sao nên cài đặt để có công cụ trực quan quản lý cơ sở dữ liệu, thay vì chỉ gõ lệnh dòng lệnh khô khan. Giao diện trực quan giúp bạn dễ hình dung cấu trúc bảng và mối quan hệ giữa chúng, đặc biệt hữu ích khi cơ sở dữ liệu có từ năm bảng trở lên. Với phần viết code PHP, một trình soạn thảo phù hợp cũng quan trọng không kém. Chúng tôi có tổng hợp top công cụ soạn thảo PHP miễn phí tốt nhất mà bạn có thể tham khảo để chọn công cụ hợp với thói quen làm việc của mình. Một số bạn thích giao diện tối giản. Một số khác lại cần nhiều tiện ích hỗ trợ debug. Không có công cụ nào là chuẩn nhất cho tất cả mọi người. Ngoài hai nhóm công cụ trên, bạn cũng nên chuẩn bị: Một môi trường local đầy đủ PHP, MySQL và web server để chạy thử ngay trên máy, không cần đưa code lên hosting mỗi lần kiểm tra. Một file ghi chú riêng để lưu lại lỗi thường gặp, giúp bạn tra cứu nhanh khi gặp lại tình huống tương tự. Một tài khoản trên diễn đàn lập trình để có nơi hỏi đáp khi bí, vì tự học một mình rất dễ nản nếu không có ai trao đổi. Việc tham khảo kiến thức lập trình web, PHP, MySQL và công cụ phát triển từ nhiều nguồn khác nhau cũng giúp bạn có cái nhìn đa chiều hơn. Học lệch theo một nguồn tài liệu duy nhất đôi khi khiến bạn hiểu sai bản chất vấn đề, vì mỗi tác giả thường chỉ trình bày theo góc nhìn quen thuộc của họ. Những nhầm lẫn thường gặp khi tự học PHP và MySQL Một trong những nhầm lẫn phổ biến nhất mà chúng tôi hay gặp ở học viên mới là lẫn lộn giữa MySQL và MySQL Server. Hai khái niệm này nghe gần giống nhau nhưng thực chất phục vụ mục đích khác nhau trong hệ thống. Để tránh mất thời gian tra cứu lại từ đầu, bạn nên đọc qua bài viết sự khác biệt giữa MySQL và MySQL Server ngay từ giai đoạn đầu học tập, thay vì để đến khi làm dự án thật mới bối rối. Một nhầm lẫn khác là cố học quá nhiều thứ cùng lúc. Có bạn vừa học PHP thuần, vừa học framework, vừa học thêm JavaScript và một hệ quản trị cơ sở dữ liệu khác, tất cả trong cùng một tháng. Kết quả thường là không thứ nào được nắm chắc, kiến thức bị trộn lẫn và dễ quên. Chúng tôi khuyên bạn nên đi chậm mà chắc, hoàn thành trọn vẹn một chủ đề trước khi chuyển sang chủ đề tiếp theo. Bước tiếp theo sau khi nắm vững nền tảng PHP và MySQL Khi bạn đã tự tin viết được vài trang PHP tương tác với MySQL, bước tiếp theo là học cách đưa sản phẩm lên môi trường thật. Nhiều bạn học viên dừng lại ở việc chạy code trên máy cá nhân. Họ chưa từng thử đăng ký hosting, trỏ tên miền hay cấu hình SSL cho website. Đây là những kỹ năng tuy không khó nhưng lại cực kỳ cần thiết nếu bạn muốn làm nghề lập trình web một cách nghiêm túc. Chúng tôi khuyên bạn nên thử thuê một gói hosting giá rẻ để thực hành đưa dự án nhỏ của mình lên internet. Cảm giác thấy website do chính tay mình viết chạy được trên một địa chỉ thật, ai cũng truy cập được, sẽ tạo động lực rất lớn để bạn tiếp tục học sâu hơn. Trong quá trình này, bạn cũng sẽ học thêm cách sao lưu dữ liệu, cách xử lý khi server gặp sự cố, và cách bảo vệ website khỏi các lỗi bảo mật cơ bản như SQL Injection, vốn là lỗi rất phổ biến ở những bạn mới học PHP. Kết luận Kiên trì thực hành quan trọng hơn học lý thuyết dàn trải, đặc biệt với người mới bắt đầu học lập trình web bằng PHP và MySQL. Thay vì cố đọc hết mọi tài liệu trước khi bắt tay viết code, bạn nên vừa học vừa làm. Chấp nhận mắc lỗi rồi tự sửa chính là cách kiến thức được ghi nhớ bền vững nhất, theo những gì chúng tôi quan sát được từ rất nhiều học viên đã đi qua con đường này. Nếu bạn đang tìm thêm hướng dẫn cụ thể cho từng bước trong lộ trình học lập trình web PHP, MySQL, đội ngũ chúng tôi luôn sẵn sàng đồng hành cùng bạn qua các bài viết chuyên sâu tiếp theo. Chặng đường tự học có thể dài, nhưng chỉ cần kiên nhẫn đi từng bước nhỏ, bạn sẽ sớm tự tay dựng được website hoàn chỉnh của riêng mình. The post Kiến Thức Lập Trình Web, PHP, MySQL Cho Người Mới appeared first on DBAhire.

Banner
Copyright © 2026 www.pedrinazzi.it. Tutti i diritti riservati.
Joomla! è un software libero rilasciato sotto licenza GNU/GPL.