阅读视图

发现新文章,点击刷新页面。
🔲 ☆

当你的程序连接Mysql然后崩溃时

之前写过一个监控mysql数据库更新状态的预警程序,总是莫名其妙的报一个连接错误的错,然后程序死掉。后来在系统趋于稳定之后,我就没再继续维护这个工具了。

但是最近我在写另一个工具时,遇到了一个奇怪的问题,就是:tick总在27000多左右的时候崩溃。

我进行了一系列的猜测,比如tick的代码,或者是逻辑有问题,最后我把思路放在了之前遇到的这个错误上。查阅资料后发现,MySQL数据库在连接之后,如果超过一个设定的时间戳之后,会断开。这个值叫WAIT_TIMEOUT,默认值是28800,也就是说如果连上MySQL数据库之后,8小时内没有进行操作,这个连接便会断开。

网上很多连接MySQL数据库的代码没有处理过超时连接的问题,就连JS的官方代码好像也是在17年之后才更新的。以往这个问题,大家都是通过修改这个值来进行规避的。比如改成300天。修改有两种方式,一种是修改配置文件,这样在启动时便会使用这个配置;另一种是修改这个值,或者全局,或者当次生效。

下面是我在使用JavaScript语言链接MySQL数据库时,处理超时重连问题的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
this.config = {
"host": "x.x.x.x",
"port": xxxx,
"user": "root",
"password": "pass",
"database": "name""
}

async connect() {
let self = this;
console.log("connect mysql success with", JSON.stringify(this.config))
// 创建连接
this.db_mysql = mysql.createConnection(
this.config
);
// 连接数据库
await this.db_mysql.connect();
// 错误处理
this.db_mysql.on('error', function(err) {
if (err) {
if (err.code === 'PROTOCOL_CONNECTION_LOST') {
// 处理超时
console.warning("start reconnect mysql");
self.connect();
} else {
console.error(err.stack || err);
console.warning("start reconnect mysql");
self.connect();
}
}
});
}
🔲 ☆

MySQL 自定义数据库路径

最近的一些文章是整理以前的笔记
MySQL 是最常用的数据,有时希望将数据库文件存放在自定义路径,或者在系统中启动多个 MySQL服务。

当然,如果条件允许,建议直接使用 docker

创建 my.cnf 配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[mysqld]
datadir=/home/ruan/data/mysql_data
socket=/home/ruan/data/mysql.sock
user=ruan
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
bind-address=127.0.0.1
port = 12345

character-set-server=utf8
collation-server=utf8_general_ci

[mysqld_safe]
log-error=/home/ruan/data/mysqld.log
pid-file=/home/ruan/data/mysqld.pid

启动和初始化

1
2
3
4
# 启动MySQL
mysqld_safe --defaults-file=my.cnf --user=ruan
# 初始化数据库
mysql_install_db --defaults-file=my.cnf --user=ruan

目录结构

1
2
3
4
5
6
7
8
9
10
$ tree data
data
├── my.cnf
├── mysql_data
│   ├── ibdata1
│   ├── ib_logfile0
│   ├── ib_logfile1
├── mysqld.log
├── mysqld.pid
└── mysql.sock

设置密码

1
mysql> use mysql; update user set password=password('m654321') where user='root'; flush privileges;
🔲 ☆

MySQL 字符集与大小写敏感性解析

在 MySQL 数据库中,UTF-8 及其变体是最常用的字符集。

不同的 UTF-8 编码可能对大小写敏感性产生影响,主要包括以下几种:

  • utf8:MySQL 早期的 UTF-8 实现,最多支持 3 字节,无法存储部分 Emoji 字符。
  • utf8mb4:MySQL 5.5+ 版本推荐使用的 UTF-8 编码,最多支持 4 字节,能够完整存储所有 Unicode 字符。

字符集与排序规则(Collation)

MySQL 字符集搭配不同的排序规则(Collation)可能会影响查询的大小写敏感性。

常见的排序规则包括:

  • utf8_general_ci / utf8mb4_general_ci:不区分大小写(Case Insensitive,ci 代表 Case Insensitive)。
  • utf8_bin / utf8mb4_bin:区分大小写(Binary,bin 代表按二进制存储,严格区分大小写)。
  • utf8_unicode_ci / utf8mb4_unicode_ci:更符合 Unicode 规范的排序方式,不区分大小写。

默认情况下,utf8_general_ciutf8mb4_general_ci 在搜索时是不区分大小写的。

MySQL 大小写搜索问题

当 MySQL 表的字符集设置为 utf8_general_ciutf8mb4_general_ci 时,使用 LIKE= 进行查询时,默认是不区分大小写的。

例如:

SELECT * FROM users WHERE username = 'admin';

如果数据库中存储了 AdminADMIN 等,查询会返回这些所有匹配项。

如果需要执行区分大小写的查询,则需要:

  1. 修改排序规则(Collation)
ALTER TABLE users MODIFY username VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;

这样查询就会严格区分 adminAdmin

  1. 使用 BINARY 关键字
SELECT * FROM users WHERE BINARY username = 'admin';

这样 admin 只会匹配完全相同的字符串,而不会匹配 AdminADMIN 等。

在 ThinkPHP 框架中使用 whereRaw 进行原生查询

在 ThinkPHP 框架中,默认的 where 方法不支持直接使用 BINARY 进行查询,但可以通过 whereRaw 方法执行 MySQL 原生查询。

$result = Db::table('users')
    ->whereRaw("BINARY username = ?", ['admin'])
    ->find();

$result = Db::table('users')
    ->whereRaw("BINARY username LIKE ?", ['%admin%'])
    ->select();

这种方法可以避免默认的大小写不敏感查询,让 MySQL 进行更严格的匹配。

总结

  • MySQL 的 utf8_general_ciutf8mb4_general_ci 默认不区分大小写。
  • 需要区分大小写时,可以修改排序规则(Collation)或使用 BINARY 关键字。
  • 在 ThinkPHP 框架中,可以使用 whereRaw 方法执行 MySQL 原生查询,确保大小写敏感匹配。

这样,你就可以在 ThinkPHP 框架中更灵活地处理 MySQL 字符集大小写敏感的问题。

🔲 ☆

别再滥用 ClickHouse 了!单机每秒狂刷 1800 万条数据,拆解 Go+DuckDB 的“微型数仓”降维打击

本文永久链接 – https://tonybai.com/2026/03/13/go-duckdb-micro-data-warehouse-dimensionality-reduction

大家好,我是Tony Bai。

设想这样一个极其普遍的日常工作场景:

产品经理找到你,希望能给业务后台加一个“简单”的数据看板,用来实时统计用户的 PV/UV 漏斗、Nginx 日志的慢查询分析,或者是 IoT 设备的近期时序数据。

面对每天几百万到上千万条的数据量,你陷入了沉思。

如果直接用 MySQL/PostgreSQL 跑 GROUP BY 和 COUNT(DISTINCT),数据库的 CPU 瞬间飙到 100%,不仅查询要等上十几秒,甚至可能把核心交易业务一起拖死。

如果为了这个需求,去大动干戈地部署一套 ClickHouse、Elasticsearch 、Spark 集群或某个大型时序数据库……不仅运维成本上天,对于这点数据量来说,简直是用高射炮打蚊子。

在“传统关系型数据库跑不动”和“大数据集群太沉重”之间,难道就没有一个恰到好处的方案吗?

今天,我想给你介绍一个在海外工程界使用较多的方案。它不仅能把你从沉重的大数据组件中解救出来,还能在你的 Go 语言单二进制文件中,塞进一个性能恐怖的 OLAP(在线分析处理)引擎。

它就是 DuckDB。结合 Go 语言,它能在普通服务器上跑出每秒 1800 万条记录的写入速度,和毫秒级的亿级数据分析延迟。

这绝对是一场对传统数据架构的降维打击。

为什么 MySQL/PG 做不好数据分析?

很多开发者在职业生涯早期都会踩这个坑:试图用 MySQL 解决一切问题。

当你在 PostgreSQL 或 MySQL 中执行一个跨度为 30 天的聚合分析时,为什么会慢得让人绝望?因为它们的底层是“行式存储(Row-oriented)”

在行式存储中,即使你只需要 user_id 和 timestamp 这两列,数据库也必须把每一行的所有字段(包括那些庞大的 JSON 或 Text 字段)全部从磁盘加载到内存中。大量无用的 I/O 消耗,让分析查询变成了灾难。

为了解决这个问题,我们被迫引入了 ClickHouse 等“列式存储(Column-oriented)”数据库。列式存储让分析查询的速度提升了上百倍,但代价是:你需要额外部署和维护分布式集群、学习复杂的表引擎配置等。

DuckDB——OLAP 界的 SQLite

难道列式存储就必须伴随着复杂的集群部署吗?

DuckDB 给出了一个极其优雅的答案:做 OLAP 领域的 SQLite。

DuckDB 是一个纯粹的嵌入式列式数据库。它没有独立的服务器进程,而是内嵌在你的应用进程中,不需要你配置任何网络端口。它有很多语言的binding,包括Go。

在 Go 项目中,你只需要简单地 import “github.com/duckdb/duckdb-go/v2″,它就会作为动态/静态链接库,直接融入你的 Go Application 进程中。

但千万别因为“嵌入式”三个字就觉得它是玩具。社区的一款开源高性能数据库 Arc(基于 Go + DuckDB)给出了一份令人毛骨悚然的实测数据(基于MacBook Pro M3 Max (14 cores, 36GB RAM, 1TB NVMe)):

  • 写入性能:高达 18.6M+(1860万)记录/秒
  • 写入延迟:P50 < 0.5ms,P99 < 4ms
  • 查询性能:6M+(600万)行/秒扫描 (Arrow格式)

它是怎么做到的?除了列式存储,它底层还偷偷藏着两个大杀器:向量化执行引擎(Vectorized Execution) 和对 Parquet 格式的原生支持

手把手拆解 1800 万/秒的极致写入

口说无凭,我们直接上硬核源码。

很多新手刚接入 DuckDB 时,会习惯性地用标准 SQL 的 INSERT INTO … VALUES 去循环写数据。你会发现速度并不快,一秒钟只能写几万条。

真正的降维打击,藏在 DuckDB 专门为 Go 语言暴露的 Appender API 中。

Appender 绕过了繁琐的 SQL 解析器和规划器,直接将 Go 的内存数据格式,以极低的开销批量“灌”入 DuckDB 的底层列存结构中。来看这段极致狂暴的写入代码:

// https://go.dev/play/p/mHXu-kAydDX
package main

import (
    "context"
    "database/sql"
    "fmt"
    "log"
    "time"

    duckdb "github.com/duckdb/duckdb-go/v2"
)

func main() {
    // 1. 用 NewConnector 创建连接器(指定数据库文件)
    connector, err := duckdb.NewConnector("analytics.db", nil)
    if err != nil {
        log.Fatal(err)
    }
    defer connector.Close()

    // 2. 用 sql.OpenDB 打开标准 db(用于建表等 SQL 操作)
    db := sql.OpenDB(connector)
    defer db.Close()

    _, err = db.Exec(CREATE TABLE IF NOT EXISTS metrics (id INTEGER, name VARCHAR, value DOUBLE, ts TIMESTAMP))
    if err != nil {
        log.Fatal(err)
    }

    // 3. 用 connector.Connect() 获取底层 driver.Conn(Appender 需要这个)
    conn, err := connector.Connect(context.Background())
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    // 4. 直接传 driver.Conn,无需 Raw()
    appender, err := duckdb.NewAppenderFromConn(conn, "", "metrics")
    if err != nil {
        log.Fatal(err)
    }
    defer appender.Close()

    startTime := time.Now()

    for i := 0; i < 100000; i++ {
        err := appender.AppendRow(
            int32(i),
            fmt.Sprintf("metric_%d", i%10),
            float64(i%100),
            time.Now(),
        )
        if err != nil {
            log.Fatal(err)
        }
    }

    elapsed := time.Since(startTime)
    fmt.Printf("插入 10 万条数据耗时: %v\n", elapsed)
    fmt.Printf("吞吐量: %.0f 记录/秒\n", 100000.0/elapsed.Seconds())
}

在我的一台2019款 普通 MBP 笔记本(Intel芯片)上,上述这段代码写入 10 万条数据仅需 69 毫秒

插入 10 万条数据耗时: 69.466586ms
吞吐量: 1439541 记录/秒

换算下来,吞吐量轻松突破 143 万条/秒。如果开启并发和更大批次,逼近千万级似乎也毫无压力。这比传统的 SQL INSERT 快了整整 100 倍

替代 ELK,只需一个 Go 二进制文件

掌握了这把利器,我们该如何在实际业务中发挥它的威力?

假设你有一个 10GB 的 Nginx 日志文件(或者 CSV 文件),老板让你马上查一下昨天的 PV、UV 和慢查询排行。

过去,你需要搭建 Logstash -> Elasticsearch -> Kibana 这一套全家桶。

现在,你只需要写几十行 Go 代码。DuckDB 支持直接查询 CSV 和 Parquet 文件,连数据导入都省了

你可以直接把底层的统计逻辑嵌在你的 Go REST API 里(仅作说明使用):

// 直接在 Go 代码中,把 DuckDB 当作微型分析网关
func (adb *AnalyticsDB) GetHourlyStats() (map[string]interface{}, error) {
    // 惊人特性:直接用 SQL 语法查询本地或 S3 上的 Parquet 压缩文件!
    rows, err := adb.db.Query(
        SELECT
            DATE_TRUNC('hour', timestamp) as hour,
            COUNT(*) as pv,
            COUNT(DISTINCT path) as uv
        FROM read_parquet('s3://my-bucket/nginx_logs/*.parquet')  -- 对 Parquet 格式的原生支持与深度优化(谓词下推、列裁剪),可跳过无关数据块,大幅减少实际 I/O
        WHERE timestamp > NOW() - INTERVAL '24 hours'
        GROUP BY hour
        ORDER BY hour DESC
    )
    // ... 解析并返回给前端
}

通过这种架构,你的 Go 语言 Web 服务瞬间拥有了媲美 ClickHouse 的 OLAP 分析能力。

最绝的是,整个系统的部署产物,仅仅是一个几十 MB 的 Go 二进制文件。没有额外的依赖,丢上服务器就能跑。

小结:它不是万能的银弹

虽然 DuckDB 强到离谱,但作为高级工程师,我们必须理智看待边界。

DuckDB 绝对不适合做高并发的 OLTP(在线事务处理)。

如果你用它来扛电商的下单扣库存、或者多用户的并发更新行数据,它会死得很惨。因为它是一头为了“大吞吐分析”而生的巨兽,并没有针对行级锁和高频短事务做优化。

所以,最完美的现代架构公式应该是:

PostgreSQL/MySQL(负责核心业务流) + Go 应用内嵌 DuckDB(负责旁路日志、报表聚合的简单轻量分析)。


** 今日互动探讨:**

你在公司里遇到过哪些“为了小数据杀鸡用牛刀,强行部署大集群”的奇葩架构?或者你平时处理百万级数据分析时,最爱用什么工具?

欢迎在评论区疯狂吐槽或分享!


认知跃迁:掌控架构降维打击的底层逻辑

看到这里,你是否对日常的业务开发有了全新的视角?

在过去,面对复杂的分析需求,CRUD 程序员的本能反应是“引入一个新的重量级中间件”。

但真正的高级架构师,懂得利用底层技术栈的差异性(如行存与列存、向量化与标量计算),用最轻量、最克制的方案完成“降维打击”。

如果你的 Go 技能依然停留在写写简单的增删改查 API,对更深层的并发控制、内存管理和系统级架构选型感到迷茫——

我的极客时间专栏《Go语言进阶课》正是为你量身打造!

在这 30+ 讲硬核内容中,我将带你剥开语法糖,深入理解 Go 的底层运行机制,不仅教你写代码,更教你像顶级大厂架构师一样思考:如何用最少的组件,设计出极高并发、极低延迟的优雅系统。

目标只有一个:助你完成从“Go 熟练工”到“能做顶级架构决策的 Go 专家”的蜕变!

扫描下方二维码,加入专栏,让我们一起用技术实现“四两拨千斤”的震撼。


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


原「Gopher部落」已重装升级为「Go & AI 精进营」知识星球,快来加入星球,开启你的技术跃迁之旅吧!

我们致力于打造一个高品质的 Go 语言深度学习AI 应用探索 平台。在这里,你将获得:

  • 体系化 Go 核心进阶内容: 深入「Go原理课」、「Go进阶课」、「Go避坑课」等独家深度专栏,夯实你的 Go 内功。
  • 前沿 Go+AI 实战赋能: 紧跟时代步伐,学习「Go+AI应用实战」、「Agent开发实战课」、「Agentic软件工程课」、「Claude Code开发工作流实战课」、「OpenClaw实战分享」等,掌握 AI 时代新技能。
  • 星主 Tony Bai 亲自答疑: 遇到难题?星主第一时间为你深度解析,扫清学习障碍。
  • 高活跃 Gopher 交流圈: 与众多优秀 Gopher 分享心得、讨论技术,碰撞思想火花。
  • 独家资源与内容首发: 技术文章、课程更新、精选资源,第一时间触达。

衷心希望「Go & AI 精进营」能成为你学习、进步、交流的港湾。让我们在此相聚,享受技术精进的快乐!欢迎你的加入!

img{512x368}


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

© 2026, bigwhite. 版权所有.

🔲 ☆

如何开启mysql的optimizer trace

我们在使用mysql的索引的时候一般会使用explain来查看执行计划,用来分析索引使用情况等
但是经常我们也会质疑,为啥没有用预期的索引,反而使用了另一个或者甚至没使用
这样我们就可以开启optimizer

🔲 ☆

浅析下mysql的索引的基数与可选择性

最近也看到了一篇文章,结合一些实际的经验来看下索引的基数和可选择性,
这个基数指的是啥呢,就是索引我们一直在讲的是要字段值的差异度比较大的那种,因为假如这个字段的所有值都是比如0和1的话,那索引的结构BTree就没办法高效的找到所查询的值,这个基数就是可以作为它差异度

🔲 ☆

PostgreSQL 吞噬世界,MongoDB 起诉 Go 开源项目:2025 数据库年度盘点

本文永久链接 – https://tonybai.com/2026/01/08/databases-in-2025-a-year-in-review

大家好,我是Tony Bai。

数据库领域的“毒舌”,CMU教授 Andy Pavlo 再次发布了他的年度回顾(虽然这次是站在 2026 年初的回望)。2025 年对于数据基础设施是疯狂的一年:PostgreSQL 继续确立其霸主地位,引发了巨头间的收购狂潮;AI Agent 通过 MCP 协议正式接管数据库交互;而 Go 社区熟知的 FerretDB 则陷入了与 MongoDB 的法律泥潭。本文将为你深度梳理这份报告背后的技术趋势与行业信号。

img{512x368}

PostgreSQL 的统治:云巨头的“军备竞赛”

如果说 2021 年 Andy Pavlo 首次提出“PostgreSQL 正在吞噬数据库世界”,那么 2025 年则是这一预言的终极验证。PostgreSQL 不再仅仅是一个选项,它已经成为了行业标准,引发了云巨头之间近乎疯狂的并购与研发竞赛。

核心事件与技术演进

  • PostgreSQL v18 发布:终于引入了异步 I/O (Asynchronous I/O) 存储子系统,这意味着 Postgres 终于开始摆脱对操作系统页缓存(OS Page Cache)的依赖,向现代化 DBMS 架构迈出了关键一步。此外还增加了对 Skip Scans 的支持。
  • 天价收购案
    • Databricks 以 10 亿美元收购 Neon:Neon 是著名的“Serverless Postgres”开创者,其存算分离架构是现代云数据库的标杆。
    • Snowflake 以 2.5 亿美元收购 CrunchyData:为了不甘人后,Snowflake 也迅速补齐了其 Postgres 拼图。
    • Microsoft 发布 HorizonDB:作为回应,微软推出了自己的下一代 Postgres DBaaS。

对于后端和 Go 开发者而言,这意味着 PostgreSQL 协议已成为事实上的“通用语”。无论底层是 Aurora、AlloyDB 还是 Neon,应用层都只需通过标准的 pgx 或 lib/pq 驱动进行连接。掌握 Postgres 的深层特性和优化技巧,将成为未来五年内最具价值的技能之一。


MCP:AI Agent 时代的“中间件革命”

2025 年被定义为所有 DBMS 都支持 MCP (Model Context Protocol) 的一年。

什么是 MCP?

MCP 是由 Anthropic 提出,并随后被 OpenAI 采纳的一种标准化客户端-服务器 JSON-RPC 接口。它允许大语言模型(LLM)与外部工具和数据源进行交互,而无需编写定制的胶水代码。

  • 角色定位:MCP 服务器充当了数据库前的中间件。它向 LLM 暴露工具、数据和动作列表。
  • 工作流:LLM (MCP Client) -> MCP Server -> Database Query (SQL)。

Andy Pavlo 指出,除了官方实现外,还有数百个第三方的 MCP Server 实现。这对于 Go 开发者是一个巨大的机会:编写高性能、并发安全的 MCP 中间件是 Go 的拿手好戏

然而,这也带来了安全隐患。Pavlo 警告说,简单的代理只是将 MCP 请求翻译成 SQL,如果没有深度的内省和防护机制,AI Agent 可能会像“在应用里点了 18,000 杯水”一样,意外地摧毁数据库(比如 DROP DATABASE)。企业级 DBMS 开始内置 AI 防火墙,而开源生态则需要更多像 DBHub 这样提供查询限制和超时保护的中间件。


开源与法律:MongoDB v. FerretDB

这是 Go 社区最需要关注的法律纠纷。FerretDB 是一个用 Go 编写的开源项目,它提供了一个 MongoDB 兼容的代理层,后端使用 PostgreSQL 存储数据。这让用户可以用 Mongo 的驱动操作 Postgres。

诉讼焦点

  • 起因:MongoDB Inc. 向 FerretDB 发出停止侵权函,并在 2025 年 5 月正式提起联邦诉讼。
  • 指控:侵犯专利、版权、商标,以及违反 MongoDB 的文档和线协议规范的许可。MongoDB 特别针对 FerretDB 声称自己是“Drop-in replacement”(直接替换)这一点,认为其不仅误导开发者,还损害了 MongoDB 的声誉。
  • 背景:微软也将其 MongoDB 兼容的 DocumentDB 捐赠给了 Linux 基金会,但这似乎没有引发同样的法律反击,可能是因为巨头间的相互制衡。

警示

这一案件可能会成为 API 兼容性实现的法律判例。对于那些致力于编写“兼容层”或“协议转换器”的 Go 开发者来说,这是一个危险的信号:模仿专有软件的 API 和线协议,可能会面临越来越大的法律风险。


文件格式战争:Parquet 的挑战者们

在数据工程领域,Parquet 格式已经统治了近 15 年。但在 2025 年,为了适应现代硬件(NVMe SSD, GPU)和 AI 负载,新的挑战者涌现。

  • 挑战者联盟SpiralDB 的 Vortex(已捐赠给 Linux 基金会)、CWI 的 FastLanes、以及学术界的 F3 和 AnyBlox。
  • 核心痛点:现有的 Parquet 生态过于碎片化。Pavlo 的团队分析发现,94% 的 Parquet 文件仍在使用 2013 年的 v1 特性。
  • 未来趋势F3 格式(由 CMU, 清华大学等合作)提出了一种有趣的思路——在文件中嵌入 WASM (WebAssembly) 解码器。这意味着只要读取端支持 WASM,就可以解析任何自定义编码的数据,无需升级读取器本身。

行业大洗牌:并购与消亡

  • IBM 的野心:收购了 DataStax ($3B) 和 Confluent (Kafka 商业化公司),试图在数据流和 NoSQL 领域占据高地。
  • 向量数据库的退潮:随着所有主流 DBMS(Postgres, Oracle, Mongo)都内置了向量索引,单纯的“向量数据库”公司生存空间被挤压。Pinecone 正在寻求被收购,而 MyScaleDB 已经关闭。
  • GPU 数据库的黄昏Voltron Data 的倒闭和 HeavyDB 被 Nvidia 收购,似乎宣告了通用 GPU 数据库作为独立商业模式的终结。

总结与展望

Andy Pavlo 的这篇回顾虽然笔调幽默甚至带有讽刺,但其揭示的技术趋势却是严肃的:

  1. 架构趋同:存算分离、基于日志的架构(Log-based architecture)已成为云数据库的标配。
  2. AI 融合:数据库不再只是被动存储,而是通过 MCP 和内置向量能力,主动融入 AI Agent 的工作流。
  3. Go 的角色:在基础设施层(Docker/K8s 之后),Go 正在成为连接 AI 与数据的关键胶水语言(MCP Server, Proxy, 协议转换器)。

对于 Gopher 来说,关注 PostgreSQL 的协议生态、学习构建安全的 MCP 服务、并警惕开源协议的法律边界,将是 2025 年(及以后)的重要课题。

资料链接 – Databases in 2025: A Year in Review by Andy Pavlo


你的数据库“军火库”

数据库的世界正在发生剧变。在你的项目中,PostgreSQL 是否已经成为了默认选择?你如何看待 AI Agent 直接操作数据库的未来?

欢迎在评论区分享你的选型思考或对 FerretDB 事件的看法!让我们一起看清趋势,少走弯路。

如果这篇文章为你打开了数据库领域的新视野,别忘了点个【赞】和【在看】,并转发给你的架构师朋友!


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

© 2026, bigwhite. 版权所有.

🔲 ☆

NOT IN vs LEFT ANTI JOIN: A Performance Comparison

When filtering data based on exclusion criteria, the choice between NOT IN and LEFT ANTI JOIN can significantly impact query performance. This post demonstrates why LEFT ANTI JOIN is typically the better choice.

< Revised and generated with help of Claude >

Original Approach (Inefficient)

SELECT product_id, product_category
FROM products_dim
WHERE region_id = 100
    AND product_id NOT IN (
        SELECT product_id
        FROM products_dim
        WHERE region_id = 200
    )
    AND product_category IS NOT NULL

Optimized Approach (Recommended)

SELECT a.product_id, a.product_category
FROM products_dim a
LEFT ANTI JOIN (
    SELECT DISTINCT product_id
    FROM products_dim
    WHERE region_id = 200
) b ON a.product_id = b.product_id
WHERE a.region_id = 100
    AND a.product_category IS NOT NULL

Why This Works

Both queries return exactly the same result: products from region 100 that don’t exist in region 200.

Key Differences

Aspect NOT IN LEFT ANTI JOIN
Performance Slower, less optimized Faster, better optimized by Spark
Broadcast Risk Can trigger unwanted broadcasts Better control, prevents large broadcasts
Execution Plan Subquery execution Efficient join strategy
NULL Handling Unpredictable with NULLs Predictable behavior

Bottom Line

LEFT ANTI JOIN prevents broadcast errors while delivering the same results faster. When working with large datasets, this optimization can make a substantial difference in query execution time and resource utilization.

🔲 ☆

MySQL 自定义数据库路径

最近的一些文章是整理以前的笔记
MySQL 是最常用的数据,有时希望将数据库文件存放在自定义路径,或者在系统中启动多个 MySQL服务。

当然,如果条件允许,建议直接使用 docker

创建 my.cnf 配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[mysqld]
datadir=/home/ruan/data/mysql_data
socket=/home/ruan/data/mysql.sock
user=ruan
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
bind-address=127.0.0.1
port = 12345

character-set-server=utf8
collation-server=utf8_general_ci

[mysqld_safe]
log-error=/home/ruan/data/mysqld.log
pid-file=/home/ruan/data/mysqld.pid

启动和初始化

1
2
3
4
# 启动MySQL
mysqld_safe --defaults-file=my.cnf --user=ruan
# 初始化数据库
mysql_install_db --defaults-file=my.cnf --user=ruan

目录结构

1
2
3
4
5
6
7
8
9
10
$ tree data
data
├── my.cnf
├── mysql_data
│   ├── ibdata1
│   ├── ib_logfile0
│   ├── ib_logfile1
├── mysqld.log
├── mysqld.pid
└── mysql.sock

设置密码

1
mysql> use mysql; update user set password=password('m654321') where user='root'; flush privileges;
🔲 ☆

MySQL 表分区使用

使用MySQL数据库时,当表的数据条数比较大时(1000w以上),数据查询会很慢,索引的效果也不好。

这时我们可以把表的数据分区存储,安装数据值的前缀或者时间字段来分区。

建表

1
2
3
4
5
6
7
8
9
CREATE TABLE test_part (
appid int(11),
val int(11),
username VARCHAR(25) NOT NULL,
start_time DATETIME
)
PARTITION BY RANGE (TO_DAYS(start_time) )(
PARTITION p20190305 VALUES LESS THAN (TO_DAYS('2019-03-06 00:00:00') )
)

删除分区

alter table test_part drop partition p1;

不可以删除hash或者key分区。

一次性删除多个分区,alter table test_part drop partition p1,p2;

增加分区

ALTER TABLE test_part ADD partition (partition p20190306 VALUES LESS THAN (TO_DAYS(‘2019-03-07 00:00:00’)));

生成测试数据

创建储存过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
DROP PROCEDURE IF EXISTS proc1;
DELIMITER $$
SET AUTOCOMMIT = 0$$
CREATE PROCEDURE proc1()
BEGIN
DECLARE v_cnt DECIMAL (10) DEFAULT 0 ;
dd:LOOP
INSERT INTO test_part VALUES (
FLOOR(RAND()*100),
FLOOR(RAND()*1000),
UUID(),
DATE_ADD('2019-03-04 00:00:00', INTERVAL FLOOR(v_cnt / 5000) MINUTE)
);
COMMIT;
SET v_cnt = v_cnt+1 ;
IF v_cnt = 10000000 THEN LEAVE dd;
END IF;
END LOOP dd ;
END;$$
DELIMITER ;

调用储存过程

1
call proc1;
🔲 ☆

PostgreSQL 实现原理深度剖析:高性能数据库引擎的核心机制

PostgreSQL 作为最先进的开源关系数据库系统,其内部实现原理体现了数据库领域的前沿设计与工程智慧。本文将深入探讨 PostgreSQL 的架构设计、存储机制、并发控制和优化器原理,并通过与 MySQL 的对比展示其独特优势

视频养活国内大半自研数据库团队的PostgreSQL是什么?

I. PostgreSQL 整体架构

PostgreSQL 采用多进程架构 (Process-based),每个客户端连接对应一个独立的服务进程。这种设计虽然比线程模型消耗更多内存,但提供了极高的隔离性和稳定性——一个查询的崩溃绝不会拖垮整个数据库。

graph TD
    Client[客户端应用] -->|TCP/IP| Postmaster[Postmaster 主守护进程]
    
    subgraph "进程模型"
        Postmaster -->|fork| Backend1[服务进程 1: Backend]
        Postmaster -->|fork| Backend2[服务进程 2: Backend]
        Postmaster -->|fork| Backend3[服务进程 3: Backend]
    end

    subgraph "共享内存区域"
        SharedBuffer[Shared Buffer<br/>共享缓冲区]
        WALBuffer[WAL Buffer<br/>日志缓冲区]
        
        Backend1 <--> SharedBuffer
        Backend2 <--> SharedBuffer
        Backend3 <--> SharedBuffer
        SharedBuffer <--> WALBuffer
    end
    
    subgraph "关键后台进程"
        BgWriter[BgWriter<br/>后台写入器]
        Checkpointer[Checkpointer<br/>检查点进程]
        WALWriter[WAL Writer<br/>日志写入器]
        Autovacuum[Autovacuum<br/>自动清理进程]
    end
    
    SharedBuffer --> BgWriter
    WALBuffer --> WALWriter
    SharedBuffer --> Checkpointer
    SharedBuffer --> Autovacuum
    
    BgWriter -->|写入脏页| Storage[持久化存储<br/>数据文件]
    WALWriter -->|写入WAL| WALStorage[WAL文件]
    Checkpointer -->|检查点| Storage
    Autovacuum -->|清理| Storage

核心组件交互流程:

  1. Postmaster:主守护进程 (PID 1 of PG),负责监听端口 (5432)、管理连接请求、恢复系统。
  2. Server Process (Backend):每个客户端连接对应一个 postgres 子进程,负责执行 SQL、排序、计算。
  3. Shared Buffer:所有进程共享的内存池,用于缓存数据页 (Page)。Postgres 依赖 OS Cache 形成双重缓存架构。
  4. 后台进程 (Background Processes)
    • BgWriter:将脏页 (Dirty Pages) 缓慢刷盘,减轻 Checkpoint 压力。
    • Checkpointer:定期执行检查点,确保数据落盘,缩短恢复时间。
    • WAL Writer:将 WAL Buffer 中的日志条目刷入磁盘。
    • Autovacuum:自动发现并清理死元组,更新统计信息。

II. 存储引擎与数据组织

PostgreSQL 的存储层设计精妙,采用 堆表 (Heap Table) 结构,并辅以多种辅助文件。

2.1 页面与元组结构 (Page & Tuple)

Postgres 的标准页面大小为 8KB

classDiagram
    class PageHeaderData {
        +uint16 pd_lower (空闲空间起始)
        +uint16 pd_upper (空闲空间结束)
        +uint16 pd_special (特殊空间起始)
    }

    class LinePointer {
        +uint32 IpId (行指针: 偏移量+长度)
    }

    class HeapTupleData {
        +uint32 t_xmin (创建事务ID)
        +uint32 t_xmax (删除事务ID)
        +ItemPointerData t_ctid (物理位置)
        +uint16 t_infomask (标志位/Hint Bits)
        +data fields[] (实际数据)
    }

    class TablePage {
        PageHeaderData header
        LinePointer[] pointers
        FreeSpace 空闲空间
        HeapTupleData[] tuples
    }

    TablePage "1" *-- "1" PageHeaderData : 包含
    TablePage "1" *-- "n" LinePointer : 从前向后生长
    TablePage "1" *-- "n" HeapTupleData : 从后向前生长

关键机制

  • 行指针 (ItemPointer):索引不直接指向数据行,而是指向 Page 里的行指针。这使得 Page 内部进行碎片整理(移动 Tuple)时,无需修改外部索引,只需更新行指针即可。
  • TOAST 机制:当行大小超过页面的 1/4 (约 2KB) 时,超长字段(如 JSONB, Text)会被压缩并切片存储到专门的 TOAST 表中,主表只留一个指针。

2.2 关键辅助文件

Postgres 除了主数据文件,还维护着两个至关重要的位图文件:

  1. FSM (Free Space Map):
    • 作用: 快速定位哪个页面有足够的空闲空间来插入新数据,避免全表扫描寻找空位。
  2. VM (Visibility Map):
    • 作用: 标记某个页面上的所有元组是否对“所有活跃事务”可见。
    • 核心优化: Index-Only Scan。如果 VM 显示页面全可见,索引扫描可以直接返回数据,无需回表 (Heap Fetch)。

III. 多版本并发控制 (MVCC)

PostgreSQL 的 MVCC 通过保留数据旧版本来实现,而不是像 MySQL (InnoDB) 那样使用 Undo Log 回滚段。

3.1 元组生命周期

stateDiagram-v2
    [*] --> Active : INSERT (创建新行)
    Active --> Committed : COMMIT (事务提交)
    Active --> Aborted : ROLLBACK (事务回滚)
    
    Committed --> Updating : UPDATE (被新事务更新)
    Updating --> Dead : COMMIT (更新提交,旧行失效)
    
    Committed --> Deleting : DELETE (被新事务删除)
    Deleting --> Dead : COMMIT (删除提交)
    
    Dead --> Reusable : VACUUM (清理死元组)
    Reusable --> [*] : 空间重用

3.2 版本链与可见性

当一行数据被更新时,新旧版本通过 ctid 指针连接形成版本链。

graph TB
    Query[查询事务 Snapshot<br/>Xmin=250]
    
    subgraph VersionChain [元组版本链]
        V1["<b>版本 1</b><br/>xmin=100 (Committed)<br/>xmax=200 (Committed)"]
        V2["<b>版本 2</b><br/>xmin=200 (Committed)<br/>xmax=300 (Running)"]
        V3["<b>版本 3</b><br/>xmin=300 (Running)<br/>xmax=0"]
    end

    Query --> V1
    Query --> V2
    Query --> V3
    
    V1 -->|ctid| V2
    V2 -->|ctid| V3
    
    %% 添加可见性标签
    V1_Label[不可见: xmax已提交]
    V2_Label["<b>可见</b>: xmin已提交 & xmax未提交"]
    V3_Label[不可见: xmin未提交]
    
    V1 -.-> V1_Label
    V2 -.-> V2_Label  
    V3 -.-> V3_Label
    
    class V2 highlight
    classDef highlight fill:#d5f5e3,stroke:#2ecc71,stroke-width:2px

可见性规则的核心

  • xmin < 当前快照 && (xmax == 0 || xmax > 当前快照)
  • 简而言之:我只能看到在我开始之前已经提交的数据,且这些数据在我开始之前没有被删除。

IV. 查询处理与优化器

PostgreSQL 拥有极其强大的查询优化器,支持遗传算法 (GEQO) 和复杂代价模型。

4.1 查询执行流水线

sequenceDiagram
    participant Client
    participant Parser as 解析器 (Parser)
    participant Analyzer as 分析器 (Analyzer)
    participant Rewriter as 重写器 (Rewriter)
    participant Planner as 规划器 (Planner)
    participant Executor as 执行器 (Executor)
    
    Client->>Parser: 发送 SQL 查询
    Parser->>Analyzer: 生成原始解析树 (Parse Tree)
    Analyzer->>Rewriter: 语义分析 & 生成查询树 (Query Tree)
    Rewriter->>Planner: 应用规则 (如视图展开)
    Planner->>Planner: 逻辑优化 & 物理优化
    Note right of Planner: 成本估算 (CBO)<br/>遗传算法 (GEQO)<br/>连接顺序选择
    Planner->>Executor: 生成最优执行计划 (Plan Tree)
    Executor->>Executor: 递归执行节点 (Scan/Join/Sort)
    Executor-->>Client: 返回结果集

4.2 优化器黑科技

  1. 遗传查询优化 (GEQO): 当连接的表数量非常多(默认 >= 12)时,穷举所有连接顺序太慢,PG 会自动切换到遗传算法来搜索近似最优解。
  2. JIT (即时编译): 对于复杂的大型 OLAP 查询,PG 可以利用 LLVM 将 SQL 表达式动态编译为机器码,加速执行。
  3. 并行查询: 自动利用多核 CPU 并行执行扫描、连接和聚合操作。

V. 索引机制详解

PG 的索引是开放接口的,支持极其丰富的数据结构。

5.1 B-tree 索引结构 (Lehman & Yao 算法)

graph TD
    Root[Root Node] --> I1[Internal Node]
    Root --> I2[Internal Node]
    I1 --> L1[Leaf Node 1<br/>Data: 1..10]
    I1 --> L2[Leaf Node 2<br/>Data: 11..20]
    I2 --> L3[Leaf Node 3<br/>Data: 21..30]
    
    L1 -.->|Right Link| L2
    L2 -.->|Right Link| L3
    
    classDef leaf fill:#e8f8f5,stroke:#1abc9c
    class L1,L2,L3 leaf
  • Right-Link 机制: 允许读操作在节点分裂 (Split) 时无需加锁,极大提升并发性能。

5.2 索引类型全景

索引类型 原理 杀手级应用场景
B-tree 平衡树 绝大多数常规查询 (=, >, <, BETWEEN)
GIN 倒排索引 (Inverted) JSONB 包含查询、数组搜索、全文检索
GiST 通用搜索树 GIS 地理数据 (PostGIS)、几何图形重叠判断
SP-GiST 空间分区树 非平衡数据集,如 URL 路由、分区数据
BRIN 块范围索引 时序数据、超大数据集的日志表 (极省空间)

VI. 事务管理与 WAL

6.1 ACID 的基石:WAL

sequenceDiagram
    participant Tx as 事务
    participant WALBuf as WAL Buffer
    participant WALFile as 磁盘 WAL 文件
    participant SharedBuf as Shared Buffer
    participant DataFile as 磁盘数据文件

    Note over Tx, WALFile: 预写日志原则 (Write-Ahead Logging)

    Tx->>WALBuf: 1. 写入变更日志 (XLogRecord)
    Tx->>SharedBuf: 2. 修改内存中的数据页 (Dirty Page)
    
    Tx->>Tx: 3. 提交事务 (COMMIT)
    Tx->>WALBuf: 4. 写入 Commit Record
    WALBuf->>WALFile: 5. 刷盘 (fsync/fdatasync)
    WALFile-->>Tx: 6. 提交成功确认

    Note over SharedBuf, DataFile: 此时数据页可能还没写入磁盘!
    
    loop Checkpoint / BgWriter
        SharedBuf->>DataFile: 7. 异步将脏页写入数据文件
    end
  • Full Page Write (全页写入): 在 Checkpoint 后,第一次修改页面会记录整个页面内容,防止 OS 崩溃导致的“半页写入”损坏。

VII. 扩展性与高级特性

7.1 强大的分区表

graph TD
    Parent[主表: Sales] -->|Partition Key: Date| P1[分区: Sales_Jan]
    Parent --> P2[分区: Sales_Feb]
    Parent --> P3[分区: Sales_Mar]
    
    Client[查询: WHERE date='Jan-01'] -.->|Partition Pruning| P1
    Client -.->|忽略| P2
    Client -.->|忽略| P3
  • Partition Pruning (分区剪枝): 查询优化器能自动跳过不需要扫描的分区。

7.2 物理复制 vs 逻辑复制

graph TB
    subgraph "物理流复制 (HA)"
        P1[主库] -->|WAL Stream| P2[备库]
    end

    subgraph "逻辑复制 (ETL/微服务)" 
        L1[发布端] -->|Logical Decoding| L2[订阅端]
    end

    %% 为两个子图添加描述性文本
    P_Desc[整实例复制<br/>只读副本<br/>故障切换] 
    L_Desc[表级粒度<br/>跨版本/跨OS<br/>数据汇聚]

VIII. PostgreSQL vs MySQL 核心对比

8.1 功能与理念对比

特性 PostgreSQL MySQL (InnoDB) 核心差异点
架构 多进程 多线程 PG 隔离性强,连接开销大 (需连接池);MySQL 连接轻量。
MVCC append-only (新旧版本共存) undo log (回滚段) PG 写不阻塞读,清理依赖 VACUUM;MySQL 空间回收由 Purge 线程自动处理。
Join 算法 Nested Loop, Hash Join, Merge Join 长期仅 Nested Loop (8.0+ 加入 Hash Join) PG 处理复杂多表关联性能极强 (OLAP 能力更优)。
数据类型 JSONB (二进制+索引), GIS, 数组 JSON (文本), GIS 较弱 PG 是 NoSQL + SQL 的完美结合体。
许可证 PostgreSQL (类 BSD) GPL PG 商业化更自由。

8.2 选型建议图谱

journey
    title 数据库选型决策路径
    section 业务场景分析
      简单 CRUD, 互联网高并发: 5: MySQL
      复杂 SQL, 数据分析, 报表: 5: PostgreSQL
      需要 GIS 地理信息: 5: PostgreSQL (PostGIS)
      需要 JSON 文档存储: 4: PostgreSQL (JSONB)
    section 运维考量
      DBA 人才储备充足: 5: MySQL
      需要极致的数据一致性: 5: PostgreSQL
      需要 Oracle 迁移: 4: PostgreSQL

总结

PostgreSQL 能够成为当今最流行的数据库之一,归功于其学院派的严谨工程派的实用的完美结合:

  1. 真正的 MVCC 实现:提供无阻塞读取和严格的快照隔离。
  2. 可扩展架构:通过 Extension (如 PostGIS, TimescaleDB) 让数据库能力无限延伸。
  3. 强大的优化器:能够驾驭极其复杂的查询逻辑,不仅仅是简单的 Key-Value 查询。

专家建议:如果你的业务涉及复杂的数据模型、混合负载 (HTAP) 或地理空间数据,PostgreSQL 是不二之选;如果是纯粹的高并发简单点查询 (Point Select),MySQL 依然表现优异。

参考&致谢

系列教程

全部文章RSS订阅

数据库系列

🔲 ☆

PostgreSQL 使用全面指南:从入门到企业级应用

PostgreSQL 是世界上最先进的开源关系型数据库,以其强大的功能高度的扩展性严格的SQL标准兼容性著称。本教程将带你从基础到高级全面掌握PostgreSQL的使用技巧,涵盖安装配置、核心操作、高级特性和性能优化等各个方面。


一、PostgreSQL概述

PostgreSQL(常简称为Postgres)是一个功能强大的开源对象关系数据库系统,具有以下核心特点:

  • 高度符合SQL标准:支持SQL:2016标准的绝大部分功能
  • 可扩展性:支持自定义数据类型、函数、操作符和索引方法
  • ACID兼容:完全支持事务处理
  • 丰富的特性
  • 复杂查询
  • 外键
  • 触发器
  • 可更新的视图
  • 事务完整性
  • 多版本并发控制(MVCC)
  • 多语言支持:PL/pgSQL、PL/Python、PL/Perl、PL/Tcl等
  • 强大的扩展:PostGIS(地理空间)、pgRouting(路径规划)、TimescaleDB(时序数据)等
graph TD
PostgreSQL --> 核心功能[核心功能]
PostgreSQL --> 扩展模块[扩展模块]

核心功能 --> SQL标准
核心功能 --> ACID事务
核心功能 --> MVCC
核心功能 --> 复杂索引

扩展模块 --> PostGIS
扩展模块 --> TimescaleDB
扩展模块 --> pg_partman
扩展模块 --> PL/Python

二、安装与配置

不同平台安装

# Ubuntu/Debian
sudo apt update
sudo apt install postgresql postgresql-contrib

# CentOS/RHEL
sudo yum install postgresql-server postgresql-contrib
sudo postgresql-setup --initdb

# macOS (Homebrew)
brew install postgresql
brew services start postgresql

# Windows
下载安装包:https://www.postgresql.org/download/windows/

基本配置

  1. 初始化数据库集群
sudo -u postgres initdb -D /var/lib/postgres/data
  1. 启动服务
sudo systemctl start postgresql
sudo systemctl enable postgresql
  1. 配置访问权限
    编辑 pg_hba.conf
# 允许本地所有连接
localallalltrust
# 允许网络连接
hostallall0.0.0.0/0md5
  1. 修改监听地址
    编辑 postgresql.conf
listen_addresses = '*'

创建用户和数据库

-- 连接PostgreSQL
sudo -u postgres psql

-- 创建用户
CREATE USER myuser WITH PASSWORD 'securepassword';

-- 创建数据库
CREATE DATABASE mydb OWNER myuser;

-- 授予权限
GRANT ALL PRIVILEGES ON DATABASE mydb TO myuser;

三、数据库基础操作

表操作

-- 创建表
CREATE TABLE employees (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE,
department VARCHAR(50),
salary NUMERIC(10,2),
hire_date DATE DEFAULT CURRENT_DATE,
skills TEXT[]
);

-- 修改表
ALTER TABLE employees ADD COLUMN phone VARCHAR(15);
ALTER TABLE employees ALTER COLUMN department SET NOT NULL;

-- 删除表
DROP TABLE IF EXISTS temp_employees;

数据操作(CRUD)

-- 插入数据
INSERT INTO employees (name, email, department, salary)
VALUES
('张伟', 'zhang@company.com', '研发部', 15000),
('李娜', 'li@company.com', '市场部', 12000);

-- 查询数据
SELECT * FROM employees WHERE department = '研发部' AND salary > 13000;

-- 更新数据
UPDATE employees SET salary = salary * 1.1 WHERE department = '研发部';

-- 删除数据
DELETE FROM employees WHERE id = 5;

事务管理

BEGIN;

UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;

-- 提交事务
COMMIT;

-- 出错时回滚
ROLLBACK;

四、数据类型详解

PostgreSQL提供丰富的数据类型:

类别 数据类型 描述
数值 SMALLINT, INTEGER, BIGINT 整数类型
NUMERIC(precision, scale) 精确小数
REAL, DOUBLE PRECISION 浮点数
字符 VARCHAR(n), TEXT 变长字符串
CHAR(n) 定长字符串
日期/时间 DATE, TIME, TIMESTAMP 日期时间类型
布尔 BOOLEAN true/false
二进制 BYTEA 二进制数据
几何 POINT, LINE, CIRCLE 几何图形
网络 INET, CIDR, MACADDR 网络地址
JSON JSON, JSONB JSON数据
数组 INT[], TEXT[] 数组类型
范围 INT4RANGE, TSRANGE 范围类型

JSONB示例

CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
attributes JSONB
);

INSERT INTO products (name, attributes)
VALUES ('Laptop', '{"color": "silver", "memory": "16GB", "ports": ["USB-C", "HDMI"]}');

-- 查询JSON字段
SELECT name, attributes->>'color' AS color
FROM products
WHERE attributes @> '{"memory": "16GB"}';

五、高级查询技术

窗口函数

SELECT
name,
department,
salary,
AVG(salary) OVER (PARTITION BY department) AS avg_dept_salary,
RANK() OVER (ORDER BY salary DESC) AS salary_rank
FROM employees;

CTE(公共表表达式)

WITH regional_sales AS (
SELECT region, SUM(amount) AS total_sales
FROM orders
GROUP BY region
),
top_regions AS (
SELECT region
FROM regional_sales
WHERE total_sales > (SELECT SUM(total_sales)/10 FROM regional_sales)
)
SELECT region, product, SUM(quantity) AS product_units
FROM orders
WHERE region IN (SELECT region FROM top_regions)
GROUP BY region, product;

全文搜索

-- 创建全文搜索索引
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
title TEXT,
body TEXT
);

CREATE INDEX idx_documents_search ON documents USING GIN (to_tsvector('english', body));

-- 执行搜索
SELECT title, ts_headline(body, q) AS highlight
FROM documents, to_tsquery('english', 'database & performance') q
WHERE to_tsvector('english', body) @@ q;

六、索引与性能优化

索引类型

索引类型 适用场景 示例
B-tree 默认索引,适用于等值查询和范围查询 CREATE INDEX idx_name ON table (column)
Hash 等值查询(仅内存表) CREATE INDEX idx_name ON table USING HASH (column)
GIN JSONB、数组、全文搜索 CREATE INDEX idx_gin ON table USING GIN (jsonb_column)
GiST 几何数据、全文搜索 CREATE INDEX idx_gist ON table USING GiST (geom_column)
SP-GiST 空间分区数据 CREATE INDEX idx_spgist ON table USING SP-GiST (phone)
BRIN 大型表,按物理顺序存储 CREATE INDEX idx_brin ON table USING BRIN (timestamp)

查询优化技巧

  1. 使用EXPLAIN分析
EXPLAIN ANALYZE SELECT * FROM large_table WHERE category_id = 10;
  1. **避免SELECT ***
-- 不推荐
SELECT * FROM orders;

-- 推荐
SELECT order_id, customer_id, order_date FROM orders;
  1. 批量操作优化
-- 使用COPY导入数据
COPY large_table FROM '/path/to/data.csv' DELIMITER ',' CSV HEADER;
  1. 分区表
-- 创建分区表
CREATE TABLE sales (
id SERIAL,
sale_date DATE,
amount NUMERIC
) PARTITION BY RANGE (sale_date);

-- 创建子分区
CREATE TABLE sales_2023 PARTITION OF sales
FOR VALUES FROM ('2023-01-01') TO ('2024-01-01');

七、事务与并发控制

事务隔离级别

-- 查看当前隔离级别
SHOW default_transaction_isolation;

-- 设置隔离级别
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

锁机制

-- 显式锁定
BEGIN;
LOCK TABLE accounts IN EXCLUSIVE MODE;
-- 执行操作
COMMIT;

-- 行级锁
SELECT * FROM orders WHERE id = 100 FOR UPDATE;

MVCC(多版本并发控制)

PostgreSQL使用MVCC处理并发,避免读写冲突:

stateDiagram-v2
[*] --> Active
Active --> Committed
Active --> Aborted
Committed --> [*]
Aborted --> [*]

八、存储过程与函数

PL/pgSQL函数

CREATE OR REPLACE FUNCTION calculate_tax(amount NUMERIC)
RETURNS NUMERIC AS $$
DECLARE
tax_rate NUMERIC := 0.1;
tax_amount NUMERIC;
BEGIN
tax_amount := amount * tax_rate;
RETURN tax_amount;
EXCEPTION
WHEN division_by_zero THEN
RAISE NOTICE 'Division by zero occurred';
RETURN 0;
END;
$$ LANGUAGE plpgsql;

-- 调用函数
SELECT calculate_tax(1000);-- 返回100.00

返回结果集

CREATE OR REPLACE FUNCTION get_employees(dept_name VARCHAR)
RETURNS TABLE (id INT, name VARCHAR, salary NUMERIC) AS $$
BEGIN
RETURN QUERY
SELECT e.id, e.name, e.salary
FROM employees e
WHERE e.department = dept_name;
END;
$$ LANGUAGE plpgsql;

-- 调用
SELECT * FROM get_employees('研发部');

九、触发器与规则

创建触发器

-- 审计日志表
CREATE TABLE audit_log (
id SERIAL PRIMARY KEY,
table_name VARCHAR(100),
action VARCHAR(10),
old_data JSONB,
new_data JSONB,
change_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 触发器函数
CREATE OR REPLACE FUNCTION log_employee_changes()
RETURNS TRIGGER AS $$
BEGIN
IF (TG_OP = 'DELETE') THEN
INSERT INTO audit_log (table_name, action, old_data)
VALUES (TG_TABLE_NAME, 'DELETE', to_jsonb(OLD));
ELSIF (TG_OP = 'UPDATE') THEN
INSERT INTO audit_log (table_name, action, old_data, new_data)
VALUES (TG_TABLE_NAME, 'UPDATE', to_jsonb(OLD), to_jsonb(NEW));
ELSIF (TG_OP = 'INSERT') THEN
INSERT INTO audit_log (table_name, action, new_data)
VALUES (TG_TABLE_NAME, 'INSERT', to_jsonb(NEW));
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- 创建触发器
CREATE TRIGGER employees_audit
AFTER INSERT OR UPDATE OR DELETE ON employees
FOR EACH ROW EXECUTE FUNCTION log_employee_changes();

十、备份与恢复

逻辑备份(pg_dump)

# 备份单个数据库
pg_dump -U username -d dbname -f backup.sql

# 备份所有数据库
pg_dumpall -U postgres -f alldb.sql

# 压缩备份
pg_dump -U username -d dbname | gzip > backup.gz

物理备份(PITR)

# 开启WAL归档
# postgresql.conf中设置:
wal_level = replica
archive_mode = on
archive_command = 'cp %p /path/to/wal_archive/%f'

# 创建基础备份
pg_basebackup -D /path/to/backup -U replicator -P -Fp -Xs -R

恢复数据库

# 从逻辑备份恢复
psql -U postgres -d newdb -f backup.sql

# 时间点恢复(PITR)
1. 恢复基础备份
2. 创建recovery.conf文件:
restore_command = 'cp /path/to/wal_archive/%f %p'
recovery_target_time = '2023-05-01 12:00:00'
3. 启动PostgreSQL

十一、高可用与复制

流复制配置

# 主库配置 (postgresql.conf)
wal_level = replica
max_wal_senders = 10
hot_standby = on

# 创建复制用户
CREATE USER replicator WITH REPLICATION ENCRYPTED PASSWORD 'replpass';

# 备库配置
pg_basebackup -h master-host -U replicator -D /var/lib/postgres/data -P -Xs -R

# 备库postgresql.auto.conf中自动生成:
primary_conninfo = 'host=master-host user=replicator password=replpass'

使用pgPool-II负载均衡

# 安装pgpool-II
sudo apt install pgpool2

# 配置/etc/pgpool2/pgpool.conf
backend_hostname0 = 'master-host'
backend_port0 = 5432
backend_weight0 = 1
backend_hostname1 = 'replica1-host'
backend_port1 = 5432
backend_weight1 = 1

# 启动pgpool
systemctl start pgpool

十二、Python集成

import psycopg2
from psycopg2 import sql

# 连接数据库
conn = psycopg2.connect(
dbname="mydb",
user="myuser",
password="mypassword",
host="localhost",
port="5432"
)

try:
# 创建游标
with conn.cursor() as cursor:
# 执行查询
cursor.execute("SELECT * FROM employees WHERE department = %s", ('研发部',))
for record in cursor.fetchall():
print(f"ID: {record[0]}, Name: {record[1]}, Salary: {record[4]}")

# 插入数据
insert_query = sql.SQL("""
INSERT INTO employees (name, email, department, salary)
VALUES (%s, %s, %s, %s)
RETURNING id
""")
cursor.execute(insert_query, ('王芳', 'wang@company.com', '市场部', 13000))
new_id = cursor.fetchone()[0]
print(f"New employee ID: {new_id}")

# 提交事务
conn.commit()

except Exception as e:
print(f"Database error: {e}")
conn.rollback()

finally:
# 关闭连接
if conn:
conn.close()

十三、学习资源

  1. 官方文档 - 最权威的参考资料
  2. PGExercises - PostgreSQL交互式练习平台
  3. PostgreSQL Tutorial - 免费教程网站
  4. pgAdmin - 官方图形化管理工具
  5. PostgreSQL Weekly - 每周更新资讯
graph LR
A[学习基础SQL] --> B[掌握PostgreSQL特性]
B --> C[性能优化]
C --> D[高可用架构]
D --> E[扩展开发]
E --> F[成为PostgreSQL专家]

PostgreSQL作为功能最强大的开源数据库,适用于从嵌入式系统到企业级应用的各种场景。通过本教程,你已经掌握了其核心功能和高级技巧,建议结合实际项目深入实践。记住:正确的数据库设计比后期优化更重要

参考&致谢

🔲 ☆

MySQL 使用全面指南:从入门到高级实践

MySQL 是全球最流行的开源关系型数据库管理系统,广泛应用于 Web 应用开发。本教程将全面介绍 MySQL 的安装配置、核心操作、高级功能及最佳实践。

📦 一、MySQL 安装与配置

安装 MySQL

# Ubuntu/Debian
sudo apt update
sudo apt install mysql-server

# CentOS/RHEL
sudo yum install mysql-server

# macOS (Homebrew)
brew install mysql

# Windows
下载官方安装包:https://dev.mysql.com/downloads/mysql/

初始配置

# 安全配置向导(设置root密码等)
sudo mysql_secure_installation

# 启动/停止服务
sudo systemctl start mysql
sudo systemctl stop mysql

# 设置开机启动
sudo systemctl enable mysql

配置文件详解(my.cnf)

[mysqld]
# 数据存储目录
datadir=/var/lib/mysql

# 允许最大连接数
max_connections=200

# 查询缓存大小
query_cache_size=128M

# 字符集设置
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci

# InnoDB 缓冲池大小 (推荐70-80%内存)
innodb_buffer_pool_size=1G

# 错误日志路径
log-error=/var/log/mysql/error.log

🔌 二、MySQL 基础操作

连接 MySQL

# 本地连接
mysql -u root -p

# 远程连接
mysql -h 192.168.1.100 -P 3306 -u username -p

数据库操作

-- 创建数据库
CREATE DATABASE company_db;

-- 查看所有数据库
SHOW DATABASES;

-- 选择数据库
USE company_db;

-- 删除数据库
DROP DATABASE legacy_db;

表操作

-- 创建员工表
CREATE TABLE employees (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE,
department VARCHAR(50),
salary DECIMAL(10,2),
hire_date DATE DEFAULT (CURRENT_DATE)
);

-- 查看表结构
DESCRIBE employees;

-- 修改表结构
ALTER TABLE employees ADD COLUMN phone VARCHAR(15);
ALTER TABLE employees MODIFY COLUMN salary FLOAT;

-- 删除表
DROP TABLE temp_table;

📊 三、数据操作(CRUD)

插入数据

-- 单条插入
INSERT INTO employees (name, email, department, salary)
VALUES ('张三', 'zhang@company.com', '技术部', 15000);

-- 批量插入
INSERT INTO employees (name, email, department, salary) VALUES
('李四', 'li@company.com', '市场部', 12000),
('王五', 'wang@company.com', '财务部', 13000),
('赵六', 'zhao@company.com', '技术部', 16000);

查询数据

-- 基本查询
SELECT * FROM employees;

-- 条件查询
SELECT name, salary
FROM employees
WHERE department = '技术部' AND salary > 14000;

-- 排序与分页
SELECT * FROM employees
ORDER BY hire_date DESC
LIMIT 5 OFFSET 0;

-- 模糊查询
SELECT * FROM employees
WHERE name LIKE '张%' OR email LIKE '%@company.com';

更新数据

-- 更新单条记录
UPDATE employees
SET salary = salary * 1.1
WHERE id = 1;

-- 批量更新
UPDATE employees
SET department = '研发部'
WHERE department = '技术部';

删除数据

-- 删除单条记录
DELETE FROM employees
WHERE id = 5;

-- 清空表数据
TRUNCATE TABLE temp_employees;

🔗 四、高级查询技巧

多表连接

-- 创建部门表
CREATE TABLE departments (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
location VARCHAR(100)
);

-- 内连接
SELECT e.name, e.salary, d.name AS dept_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.id;

-- 左连接
SELECT e.name, d.name AS dept_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id;

聚合函数

-- 基本聚合
SELECT
COUNT(*) AS total_employees,
AVG(salary) AS avg_salary,
MAX(salary) AS max_salary,
MIN(hire_date) AS oldest_hire
FROM employees;

-- 分组统计
SELECT
department,
COUNT(*) AS employee_count,
AVG(salary) AS avg_salary
FROM employees
GROUP BY department
HAVING COUNT(*) > 3;

子查询

-- 单行子查询
SELECT name, salary
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);

-- IN子查询
SELECT name
FROM employees
WHERE department_id IN (
SELECT id
FROM departments
WHERE location = '北京'
);

-- EXISTS子查询
SELECT d.name
FROM departments d
WHERE EXISTS (
SELECT 1
FROM employees e
WHERE e.department_id = d.id
);

⚡ 五、索引与性能优化

索引类型

-- 创建普通索引
CREATE INDEX idx_email ON employees(email);

-- 创建唯一索引
CREATE UNIQUE INDEX uidx_email ON employees(email);

-- 创建复合索引
CREATE INDEX idx_dept_salary ON employees(department, salary);

-- 查看索引
SHOW INDEX FROM employees;

-- 删除索引
DROP INDEX idx_email ON employees;

查询优化技巧

-- 使用EXPLAIN分析查询
EXPLAIN SELECT * FROM employees WHERE department = '技术部';

-- 避免SELECT *
SELECT id, name, department FROM employees;

-- 合理使用LIMIT
SELECT * FROM large_table LIMIT 100;

-- 避免在WHERE子句中使用函数
-- 不推荐
SELECT * FROM employees WHERE YEAR(hire_date) = 2023;
-- 推荐
SELECT * FROM employees
WHERE hire_date BETWEEN '2023-01-01' AND '2023-12-31';

👥 六、用户与权限管理

用户管理

-- 创建用户
CREATE USER 'dev_user'@'localhost' IDENTIFIED BY 'secure_password';

-- 修改密码
ALTER USER 'dev_user'@'localhost' IDENTIFIED BY 'new_password';

-- 删除用户
DROP USER 'old_user'@'%';

权限管理

-- 授予权限
GRANT SELECT, INSERT, UPDATE ON company_db.* TO 'dev_user'@'localhost';

-- 授予所有权限
GRANT ALL PRIVILEGES ON *.* TO 'admin'@'%' WITH GRANT OPTION;

-- 查看权限
SHOW GRANTS FOR 'dev_user'@'localhost';

-- 撤销权限
REVOKE DELETE ON company_db.* FROM 'dev_user'@'localhost';

-- 刷新权限
FLUSH PRIVILEGES;

💾 七、备份与恢复

备份数据库

# 备份单个数据库
mysqldump -u root -p company_db > company_db_backup.sql

# 备份所有数据库
mysqldump -u root -p --all-databases > all_dbs_backup.sql

# 压缩备份
mysqldump -u root -p company_db | gzip > company_db_backup.sql.gz

恢复数据库

# 恢复数据库
mysql -u root -p company_db < company_db_backup.sql

# 从压缩文件恢复
gunzip < company_db_backup.sql.gz | mysql -u root -p company_db

# 单表恢复
mysql -u root -p company_db employees < employees_backup.sql

定时备份(Cron Job)

# 添加定时任务
crontab -e

# 每天凌晨2点备份
0 2 * * * /usr/bin/mysqldump -u root -pPASSWORD company_db | gzip > /backups/company_db_$(date +\%F).sql.gz

🔄 八、事务与锁机制

事务处理

-- 开始事务
START TRANSACTION;

-- 执行操作
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;

-- 提交事务
COMMIT;

-- 回滚事务
ROLLBACK;

事务隔离级别

-- 查看当前隔离级别
SELECT @@transaction_isolation;

-- 设置隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

锁机制

-- 手动加锁
SELECT * FROM orders WHERE id = 100 FOR UPDATE;

-- 查看当前锁
SHOW OPEN TABLES WHERE In_use > 0;

🧩 九、存储过程与函数

创建存储过程

DELIMITER //

CREATE PROCEDURE IncreaseSalaries(IN dept_name VARCHAR(50), IN increase_percent FLOAT)
BEGIN
UPDATE employees
SET salary = salary * (1 + increase_percent/100)
WHERE department = dept_name;
END //

DELIMITER ;

-- 调用存储过程
CALL IncreaseSalaries('技术部', 10);

创建函数

DELIMITER //

CREATE FUNCTION GetEmployeeCount(dept_name VARCHAR(50))
RETURNS INT
DETERMINISTIC
BEGIN
DECLARE emp_count INT;
SELECT COUNT(*) INTO emp_count
FROM employees
WHERE department = dept_name;
RETURN emp_count;
END //

DELIMITER ;

-- 使用函数
SELECT GetEmployeeCount('市场部');

⏰ 十、触发器与事件调度

创建触发器

-- 审计日志表
CREATE TABLE audit_log (
id INT AUTO_INCREMENT PRIMARY KEY,
action VARCHAR(20),
table_name VARCHAR(50),
record_id INT,
change_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 创建触发器
DELIMITER //

CREATE TRIGGER after_employee_update
AFTER UPDATE ON employees
FOR EACH ROW
BEGIN
INSERT INTO audit_log (action, table_name, record_id)
VALUES ('UPDATE', 'employees', NEW.id);
END //

DELIMITER ;

事件调度

-- 启用事件调度器
SET GLOBAL event_scheduler = ON;

-- 创建定期清理事件
CREATE EVENT daily_audit_cleanup
ON SCHEDULE EVERY 1 DAY
STARTS CURRENT_TIMESTAMP
DO
DELETE FROM audit_log WHERE change_time < NOW() - INTERVAL 30 DAY;

🐍 十一、Python 中使用 MySQL

import mysql.connector
from mysql.connector import Error

try:
# 创建连接
connection = mysql.connector.connect(
host='localhost',
user='python_user',
password='secure_pass',
database='company_db'
)

if connection.is_connected():
cursor = connection.cursor()

# 执行查询
cursor.execute("SELECT * FROM employees")
employees = cursor.fetchall()

# 插入数据
insert_query = """
INSERT INTO employees (name, email, department, salary)
VALUES (%s, %s, %s, %s)
"""
employee_data = ('钱七', 'qian@company.com', '人事部', 11000)
cursor.execute(insert_query, employee_data)
connection.commit()

# 更新数据
update_query = "UPDATE employees SET salary = salary * 1.05 WHERE department = %s"
cursor.execute(update_query, ('技术部',))
connection.commit()

except Error as e:
print("数据库错误:", e)

finally:
if connection.is_connected():
cursor.close()
connection.close()

🚨 十二、常见问题解决

忘记 root 密码

# 停止MySQL服务
sudo systemctl stop mysql

# 启动安全模式
sudo mysqld_safe --skip-grant-tables &

# 登录并修改密码
mysql -u root
FLUSH PRIVILEGES;
ALTER USER 'root'@'localhost' IDENTIFIED BY 'new_password';
FLUSH PRIVILEGES;
exit;

# 重启MySQL
sudo systemctl restart mysql

性能问题诊断

-- 查看慢查询
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';

-- 查看进程列表
SHOW PROCESSLIST;

-- 优化表
OPTIMIZE TABLE large_table;

-- 分析表
ANALYZE TABLE employees;

连接问题排查

# 错误:ERROR 1045 (28000)
# 解决方案:检查用户名/密码,确保主机权限

# 错误:ERROR 2002 (HY000)
# 解决方案:确认MySQL服务运行中,检查socket路径

# 错误:ERROR 2013 (HY000)
# 解决方案:增加连接超时时间 wait_timeout

📚 学习资源推荐

  1. MySQL 官方文档 - 最权威的参考资料
  2. MySQL Tutorial - 免费教程网站
  3. LeetCode 数据库题库 - 实战练习
  4. MySQL Workbench - 官方图形化管理工具
  5. Percona Toolkit - 高级管理工具集
graph TD
A[安装MySQL] --> B[创建数据库]
B --> C[设计表结构]
C --> D[数据操作CRUD]
D --> E[高级查询]
E --> F[性能优化]
F --> G[备份恢复]
G --> H[事务管理]
H --> I[程序集成]

MySQL 作为最流行的关系型数据库之一,掌握其核心功能对开发者和DBA至关重要。本教程覆盖了从基础到高级的全面知识体系,建议结合实际项目实践加深理解。记住,数据库设计优化往往比硬件升级更能提升性能!

参考&致谢

系列教程

全部文章RSS订阅

数据库系列

🔲 ☆

SQLite使用全面教程:轻量级数据库的终极指南

SQLite是世界上最广泛部署的数据库引擎,以其轻量、零配置和嵌入式特性著称。本教程将带你从基础到高级全面掌握SQLite的使用技巧,特别强化了数据库连接和命令行操作部分。

一、安装与配置

Windows安装

  1. 访问 SQLite官网
  2. 下载 sqlite-tools-win32-*.zip
  3. 解压后添加目录到系统PATH
  4. 命令行输入 sqlite3 验证安装

Linux/macOS安装

# Debian/Ubuntu
sudo apt install sqlite3

# macOS (需先安装Homebrew)
brew install sqlite

二、数据库连接与基本操作

创建并连接数据库

# 创建或打开数据库(不存在则创建)
sqlite3 mydatabase.db

成功连接后,提示符变为:

sqlite>

实时操作示例

-- 创建用户表
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- 插入数据
INSERT INTO users (name, email) VALUES
('张三', 'zhangsan@example.com'),
('李四', 'lisi@example.com');

-- 查询数据
SELECT * FROM users;

-- 退出SQLite命令行
.quit

连接到现有数据库

# 连接到已存在的数据库
sqlite3 existing_db.db

内存数据库(临时数据库)

# 创建内存中的临时数据库(关闭后消失)
sqlite3 :memory:

三、SQLite命令行工具高级用法

常用点命令

命令 说明
.open test.db 打开/创建数据库
.tables 显示所有表
.schema users 显示表结构
.mode column 列模式显示
.headers on 显示列名
.show 显示当前配置
.quit 退出命令行
.help 显示帮助信息

数据导入导出实战

导出数据到CSV:

sqlite3 mydatabase.db
.headers on
.mode csv
.output users.csv
SELECT * FROM users;
.output stdout

从CSV导入数据:

-- 创建临时表
CREATE TABLE temp_import(
name TEXT,
email TEXT
);

-- 导入数据
.mode csv
.import /path/to/new_users.csv temp_import

-- 插入到主表
INSERT INTO users (name, email)
SELECT name, email FROM temp_import;

-- 清理临时表
DROP TABLE temp_import;

备份与恢复

完整备份:

sqlite3 mydatabase.db ".backup 'backup.db'"

增量备份(使用WAL模式):

# 启用WAL模式
sqlite3 mydatabase.db "PRAGMA journal_mode=WAL;"

# 创建备份
cp mydatabase.db backup.db
cp mydatabase.db-wal backup.db-wal
cp mydatabase.db-shm backup.db-shm

四、Python集成实战

import sqlite3
from datetime import datetime

# 连接数据库(不存在则创建)
conn = sqlite3.connect('mydatabase.db')
cursor = conn.cursor()

# 创建表
cursor.execute('''CREATE TABLE IF NOT EXISTS orders (
id INTEGER PRIMARY KEY,
user_id INTEGER,
product TEXT,
amount REAL,
order_date TIMESTAMP,
FOREIGN KEY(user_id) REFERENCES users(id))''')

# 插入订单数据
user_id = 1
orders = [
(user_id, 'Laptop', 1200.50, datetime.now()),
(user_id, 'Mouse', 25.99, datetime.now())
]
cursor.executemany("INSERT INTO orders (user_id, product, amount, order_date) VALUES (?, ?, ?, ?)", orders)

# 复杂查询:用户及其订单
cursor.execute('''SELECT users.name, orders.product, orders.amount
FROM users
JOIN orders ON users.id = orders.user_id''')
for row in cursor.fetchall():
print(f"{row} purchased {row} for ${row:.2f}")

# 提交并关闭连接
conn.commit()
conn.close()

五、高级特性与优化

事务处理

BEGIN TRANSACTION;

-- 转账操作
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;

-- 提交事务
COMMIT;

-- 出错时回滚
-- ROLLBACK;

性能优化技巧

  1. 索引优化
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_orders_date ON orders(order_date);
  1. WAL模式
PRAGMA journal_mode=WAL;-- 启用写前日志
  1. 缓存调整
PRAGMA cache_size = -2000;-- 设置2000页缓存(约3.2MB)
  1. 查询优化
EXPLAIN QUERY PLAN SELECT * FROM users WHERE email = 'test@example.com';

全文搜索

-- 创建虚拟表
CREATE VIRTUAL TABLE docs USING fts5(title, content);

-- 插入数据
INSERT INTO docs VALUES
('SQLite Guide', 'Comprehensive guide to SQLite database'),
('Python Tutorial', 'Learn Python programming language');

-- 全文搜索
SELECT * FROM docs WHERE docs MATCH 'guide database';

六、图形化管理工具推荐

  1. DB Browser for SQLite
  1. SQLiteStudio

七、常见问题解决方案

数据库锁定问题

# 查看锁定进程
lsof mydatabase.db

# 安全解锁
cp mydatabase.db unlocked.db
sqlite3 unlocked.db "PRAGMA wal_checkpoint;"

数据库修复

# 导出数据
sqlite3 corrupt.db .dump > recovery.sql

# 重建数据库
sqlite3 new.db < recovery.sql

跨数据库操作

-- 附加另一个数据库
ATTACH DATABASE 'another.db' AS other;

-- 跨数据库查询
SELECT * FROM main.users
UNION ALL
SELECT * FROM other.users;

-- 分离数据库
DETACH DATABASE other;

八、实际应用场景

移动应用数据存储

// Android示例
SQLiteDatabase db = openOrCreateDatabase("app_data.db", MODE_PRIVATE, null);
db.execSQL("CREATE TABLE IF NOT EXISTS settings (key TEXT PRIMARY KEY, value TEXT)");
ContentValues values = new ContentValues();
values.put("key", "theme");
values.put("value", "dark");
db.insert("settings", null, values);

网站数据分析

# 日志分析示例
import sqlite3

conn = sqlite3.connect('weblog.db')
conn.execute('''CREATE TABLE IF NOT EXISTS visits
(id INTEGER PRIMARY KEY,
ip TEXT,
url TEXT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP)''')

# 模拟插入访问记录
conn.execute("INSERT INTO visits (ip, url) VALUES (?, ?)",
('192.168.1.1', '/homepage'))
conn.commit()

学习资源推荐

  1. SQLite官方文档 - 最权威的参考指南
  2. SQLite Tutorial - 交互式学习平台
  3. SQLite Fiddle - 在线SQLite测试环境
  4. SQLite Viewer - 在线查看SQLite文件
graph TD
A[启动SQLite] --> B{数据库存在?}
B -->|是| C[打开数据库]
B -->|否| D[创建新数据库]
C --> E[执行SQL操作]
D --> E
E --> F[保存更改]
F --> G[退出SQLite]

通过本增强版教程,你不仅掌握了SQLite的核心操作,还学习了实际开发中的高级技巧。SQLite的轻量级特性使其成为嵌入式系统、移动应用和小型网站的完美选择,立即开始你的SQLite项目实践吧!

参考&致谢

系列教程

全部文章RSS订阅

数据库系列

🔲 ☆

MySQL命令行使用全面教程:从入门到精通

MySQL是最流行的关系型数据库之一,掌握其命令行工具是每位开发者和DBA的必备技能。本教程将带你系统学习MySQL命令行的核心功能和实用技巧。

🚀 连接MySQL服务器

基础连接方式

# 连接到本地MySQL服务器
mysql -u root -p

# 指定主机和端口
mysql -h 127.0.0.1 -P 3306 -u username -p

# 连接后直接选择数据库
mysql -u root -p database_name

连接参数说明

参数 说明 示例
-u 用户名 -u admin
-p 密码提示 (回车后输入密码)
-h 主机地址 -h db.example.com
-P 端口号 -P 3307
-D 指定数据库 -D mydb

🔍 数据库与表操作

数据库管理

-- 显示所有数据库
SHOW DATABASES;

-- 创建新数据库
CREATE DATABASE school_db;

-- 选择数据库
USE school_db;

-- 删除数据库
DROP DATABASE old_db;

表操作

-- 创建学生表
CREATE TABLE students (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
age INT CHECK (age >= 15),
email VARCHAR(100) UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 显示所有表
SHOW TABLES;

-- 查看表结构
DESCRIBE students;

-- 修改表结构
ALTER TABLE students ADD COLUMN major VARCHAR(50);
ALTER TABLE students MODIFY COLUMN age TINYINT;
ALTER TABLE students DROP COLUMN email;

📊 数据操作(CRUD)

插入数据

INSERT INTO students (name, age)
VALUES
('张三', 18),
('李四', 19),
('王五', 17);

查询数据

-- 查询所有数据
SELECT * FROM students;

-- 条件查询
SELECT name, age FROM students WHERE age > 18;

-- 排序和限制
SELECT * FROM students ORDER BY created_at DESC LIMIT 5;

-- 模糊查询
SELECT * FROM students WHERE name LIKE '张%';

更新数据

UPDATE students
SET age = 20
WHERE name = '张三';

删除数据

DELETE FROM students
WHERE name = '王五';

🔧 高级查询技巧

聚合函数

SELECT
COUNT(*) AS total,
AVG(age) AS avg_age,
MIN(created_at) AS oldest,
MAX(age) AS max_age
FROM students;

分组统计

SELECT
major,
COUNT(*) AS student_count,
AVG(age) AS avg_age
FROM students
GROUP BY major
HAVING COUNT(*) > 5;

表连接

-- 创建课程表
CREATE TABLE courses (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
instructor VARCHAR(50)
);

-- 内连接查询
SELECT s.name, c.name AS course
FROM students s
JOIN courses c ON s.id = c.student_id;

👥 用户与权限管理

用户管理

-- 创建新用户
CREATE USER 'dev'@'localhost' IDENTIFIED BY 'password123';

-- 修改密码
ALTER USER 'dev'@'localhost' IDENTIFIED BY 'newpassword456';

-- 删除用户
DROP USER 'dev'@'localhost';

权限管理

-- 授予权限
GRANT SELECT, INSERT, UPDATE ON school_db.* TO 'dev'@'localhost';

-- 查看权限
SHOW GRANTS FOR 'dev'@'localhost';

-- 撤销权限
REVOKE UPDATE ON school_db.* FROM 'dev'@'localhost';

📤 数据导入导出

导出数据

# 导出整个数据库
mysqldump -u root -p school_db > school_db.sql

# 导出特定表
mysqldump -u root -p school_db students > students.sql

# 导出为CSV格式
mysql -u root -p -e "SELECT * FROM students" school_db > students.csv

导入数据

# 导入SQL文件
mysql -u root -p school_db < backup.sql

# 导入CSV数据
mysql -u root -p school_db -e "
LOAD DATA INFILE '/path/to/students.csv'
INTO TABLE students
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'
IGNORE 1 ROWS;"

⚙️ 配置与优化

配置文件(my.cnf)

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
log-error=/var/log/mysql/error.log
pid-file=/var/run/mysql/mysql.pid

# 性能优化
innodb_buffer_pool_size=1G
max_connections=200
query_cache_size=128M

性能分析

-- 查看查询执行计划
EXPLAIN SELECT * FROM students WHERE age > 18;

-- 查看进程列表
SHOW PROCESSLIST;

-- 优化表
OPTIMIZE TABLE students;

🚨 常见问题解决

忘记root密码

  1. 停止MySQL服务
  2. 启动安全模式:
mysqld_safe --skip-grant-tables &
  1. 登录并修改密码:
FLUSH PRIVILEGES;
ALTER USER 'root'@'localhost' IDENTIFIED BY 'newpassword';
  1. 重启MySQL服务

连接错误

# 错误:ERROR 1045 (28000)
# 解决方案:检查用户名/密码,确保主机权限

# 错误:ERROR 2002 (HY000)
# 解决方案:确认MySQL服务运行中,检查socket路径

备份与恢复

# 定时备份脚本
mysqldump -u root -p --all-databases | gzip > /backups/mysql-$(date +%F).sql.gz

# 恢复备份
gunzip < /backups/mysql-2023-01-01.sql.gz | mysql -u root -p

💡 实用技巧

批处理模式

mysql -u root -p -e "SHOW DATABASES; SELECT VERSION();"

# 执行SQL脚本
mysql -u root -p < script.sql

输出格式化

-- 垂直显示结果
SELECT * FROM students \G

-- 表格格式
SELECT * FROM students \T

-- 导出为HTML
SELECT * FROM students --html

命令历史

# 查看历史命令
cat ~/.mysql_history

# 实时监控查询
mysqladmin -u root -p processlist

📚 学习资源推荐

  1. MySQL官方文档
  2. MySQL Tutorial
  3. SQLZoo MySQL练习
  4. MySQL Workbench图形工具

通过掌握这些MySQL命令和技巧,你将能够高效地管理和操作数据库。实践是最好的学习方式 - 立即创建你的第一个数据库项目吧!

附录:常用命令速查表

类别 命令 说明
连接 mysql -u user -p 连接数据库
数据库 SHOW DATABASES; 显示所有数据库
CREATE DATABASE dbname; 创建数据库
USE dbname; 选择数据库
表操作 SHOW TABLES; 显示所有表
DESCRIBE tablename; 查看表结构
ALTER TABLE ... 修改表结构
数据 SELECT ... FROM ... 查询数据
INSERT INTO ... VALUES ... 插入数据
UPDATE ... SET ... WHERE ... 更新数据
DELETE FROM ... WHERE ... 删除数据
用户 CREATE USER ... 创建用户
GRANT ... ON ... TO ... 授予权限
维护 SHOW PROCESSLIST; 查看活动连接
EXPLAIN SELECT ... 分析查询性能

参考&致谢

系列教程

全部文章RSS订阅

数据库系列

🔲 ☆

PostgreSQL命令行使用教程:掌握 psql 工具

掌握PostgreSQL的命令行工具psql是高效管理数据库的关键。本文将带你全面了解这个强大的交互式终端工具的各种使用方法,包括各种常见问题和技巧总结。

🚀 启动psql

基本连接方式

# 连接到本地默认数据库
psql

# 指定用户名和数据库
psql -U username -d dbname

# 指定主机和端口
psql -h localhost -p 5432 -U postgres -d mydb

连接参数说明

参数 说明 示例
-U 用户名 -U admin
-d 数据库名 -d sales_db
-h 主机地址 -h db.example.com
-p 端口号 -p 6432
-W 强制密码提示
-f 执行SQL文件 -f setup.sql

🔍 常用元命令(以\开头)

数据库操作

-- 列出所有数据库
\l

-- 切换数据库
\c dbname

-- 创建新数据库
CREATE DATABASE newdb;

-- 删除数据库
DROP DATABASE olddb;

表操作

-- 列出当前数据库所有表
\dt

-- 查看表结构
\d users

-- 显示表详细信息
\d+ orders

-- 显示表、视图和序列
\dv

用户和权限管理

-- 列出所有用户
\du

-- 创建新用户
CREATE USER dev WITH PASSWORD 'secret123';

-- 修改密码
ALTER USER dev WITH PASSWORD 'newsecret456';

-- 授予权限
GRANT SELECT, INSERT ON customers TO dev;

信息查询

-- 显示当前连接信息
\conninfo

-- 显示SQL版本
SELECT version();

-- 显示当前时间
SELECT NOW();

-- 显示当前数据库
SELECT current_database();

文件操作

-- 执行SQL文件
\i path/to/script.sql

-- 将查询结果输出到文件
\o output.txt
SELECT * FROM products;
\o

帮助系统

-- 显示所有元命令帮助
\?

-- 显示SQL命令帮助
\h

-- 查看特定命令帮助
\h CREATE TABLE

📊 查询与编辑

基本查询

-- 简单查询
SELECT * FROM employees;

-- 条件查询
SELECT name, salary FROM employees WHERE salary > 50000;

-- 排序
SELECT * FROM orders ORDER BY order_date DESC;

-- 限制结果
SELECT * FROM products LIMIT 10;

多行编辑

-- 开启多行模式
\set PROMPT1 '%/%R%# '

-- 输入多行SQL语句
SELECT
first_name,
last_name,
department
FROM employees
WHERE hire_date > '2020-01-01';

-- 结束多行输入(分号后回车)
;

查询历史

-- 查看历史命令
\s

-- 保存历史到文件
\s history.sql

-- 执行历史命令(!后跟行号)
\! 15

🔄 导入导出数据

导出数据

# 导出整个数据库
pg_dump dbname > dbname.sql

# 导出特定表
pg_dump -t tablename dbname > table.sql

# 导出为CSV格式
\copy (SELECT * FROM users) TO '/path/to/users.csv' CSV HEADER

导入数据

# 导入SQL文件
psql -d dbname -f backup.sql

# 导入CSV文件
\copy users FROM '/path/to/users.csv' CSV HEADER

# 使用COPY命令(需要超级用户权限)
COPY employees FROM '/path/to/employees.csv' DELIMITER ',' CSV HEADER;

⚙️ 配置与定制

配置文件(.psqlrc)

在用户主目录创建.psqlrc文件:

-- 设置时间格式
\set HISTFILE ~/.psql_history- :DBNAME
\set HISTSIZE 1000

-- 自定义提示符
\set PROMPT1 '%[%033[1;33m%]%/%R%#%[%033[0m%] '

-- 自动提交
\set AUTOCOMMIT on

-- 常用别名
\set QUIET on
\echo '常用别名:'
\echo 'e: \\dt'
\echo 's: SELECT * FROM'
\set QUIET off

常用配置选项

-- 显示查询执行时间
\timing

-- 扩展显示模式
\x auto

-- 设置空值显示
\pset null '[NULL]'

-- 设置边框样式
\pset border 2

🛠️ 高级技巧

事务管理

-- 开始事务
BEGIN;

-- 执行多个操作
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;

-- 提交或回滚
COMMIT;
-- ROLLBACK;

性能分析

-- 查看查询计划
EXPLAIN SELECT * FROM large_table WHERE condition = 'value';

-- 详细分析(实际执行)
EXPLAIN ANALYZE SELECT * FROM large_table;

-- 索引使用情况
SELECT * FROM pg_stat_user_indexes;

维护操作

-- 重建索引
REINDEX TABLE large_table;

-- 清理表空间
VACUUM FULL ANALYZE;

-- 收集统计信息
ANALYZE VERBOSE;

🚨 常见问题解决

连接问题

# 认证失败
psql: FATAL: password authentication failed for user "user"

# 解决方案:
# 1. 检查pg_hba.conf配置
# 2. 重置密码:ALTER USER user WITH PASSWORD 'newpassword';

权限问题

-- 权限拒绝
ERROR: permission denied for table tablename

# 解决方案:
GRANT SELECT, INSERT ON tablename TO username;

特殊字符处理

-- 包含引号的字符串
SELECT * FROM comments WHERE text E'contains \'quote\'';

-- 使用$tag$语法
DO $$
BEGIN
INSERT INTO logs (message) VALUES ('This contains ''quotes''');
END $$;

💡 实用技巧总结

  1. 历史命令:使用上下箭头键浏览历史命令
  2. 自动补全:Tab键补全对象名(表名、列名等)
  3. 变量设置:使用:variable存储和重用值
\set id 100
SELECT * FROM users WHERE id = :id;
  1. 快速编辑:使用\e打开编辑器编辑当前查询
  2. 执行系统命令\!后跟系统命令
\! ls -l /backups

📚 学习资源

  1. PostgreSQL官方文档
  2. psql命令行备忘单
  3. pgAdmin - PostgreSQL图形化管理工具

通过掌握这些psql命令和技巧,你将能够高效地管理和操作PostgreSQL数据库。不断练习和实践是成为数据库管理专家的关键!

参考&致谢

🔲 ☆

SQL命令使用教程:从入门到精通

作为一个程序员,不掌握SQL怎么行?掌握SQL是数据管理和分析的必备技能,本教程将带你系统学习SQL的核心命令和实用技巧,从入门到精通。

什么是SQL?

SQL(Structured Query Language)是用于管理和操作关系型数据库的标准语言。通过SQL,你可以:

  • 创建和管理数据库和表
  • 插入、更新和删除数据
  • 查询和分析数据
  • 设置访问权限
  • 管理事务

SQL主要分为四类命令:

  1. DDL(数据定义语言) - 创建/修改数据库结构
  2. DML(数据操作语言) - 操作数据
  3. DQL(数据查询语言) - 查询数据
  4. DCL(数据控制语言) - 控制访问权限

一、数据库操作

创建数据库

CREATE DATABASE school_db;

查看所有数据库

SHOW DATABASES;

选择数据库

USE school_db;

删除数据库

DROP DATABASE school_db;

二、表操作

创建表

CREATE TABLE students (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
age INT CHECK (age >= 15),
email VARCHAR(100) UNIQUE,
enrollment_date DATE DEFAULT CURRENT_DATE
);

查看表结构

DESCRIBE students;

修改表结构

-- 添加新列
ALTER TABLE students ADD COLUMN major VARCHAR(50);

-- 修改列类型
ALTER TABLE students MODIFY COLUMN age SMALLINT;

-- 删除列
ALTER TABLE students DROP COLUMN email;

删除表

DROP TABLE students;

三、数据操作(CRUD)

插入数据

INSERT INTO students (name, age, enrollment_date)
VALUES
('张三', 18, '2023-09-01'),
('李四', 19, '2023-08-25'),
('王五', 17, '2023-09-05');

查询数据

-- 查询所有列
SELECT * FROM students;

-- 查询特定列
SELECT name, age FROM students;

-- 带条件查询
SELECT * FROM students WHERE age > 18;

更新数据

UPDATE students
SET age = 20
WHERE name = '张三';

删除数据

DELETE FROM students
WHERE name = '王五';

四、高级查询技巧

排序结果

SELECT * FROM students
ORDER BY age DESC, name ASC;

限制结果数量

SELECT * FROM students
ORDER BY enrollment_date DESC
LIMIT 5;

模糊查询

SELECT * FROM students
WHERE name LIKE '张%';

聚合函数

SELECT
COUNT(*) AS total_students,
AVG(age) AS average_age,
MIN(enrollment_date) AS earliest_enrollment,
MAX(age) AS max_age
FROM students;

分组统计

SELECT
major,
COUNT(*) AS student_count,
AVG(age) AS avg_age
FROM students
GROUP BY major
HAVING COUNT(*) > 5;

五、表连接

创建课程表

CREATE TABLE courses (
course_id INT PRIMARY KEY AUTO_INCREMENT,
course_name VARCHAR(50),
instructor VARCHAR(50)
);

CREATE TABLE enrollments (
student_id INT,
course_id INT,
enrollment_date DATE,
FOREIGN KEY (student_id) REFERENCES students(id),
FOREIGN KEY (course_id) REFERENCES courses(course_id)
);

内连接

SELECT s.name, c.course_name
FROM students s
INNER JOIN enrollments e ON s.id = e.student_id
INNER JOIN courses c ON e.course_id = c.course_id;

左连接

SELECT s.name, c.course_name
FROM students s
LEFT JOIN enrollments e ON s.id = e.student_id
LEFT JOIN courses c ON e.course_id = c.course_id;

六、子查询

单行子查询

SELECT * FROM students
WHERE age > (SELECT AVG(age) FROM students);

多行子查询

SELECT * FROM courses
WHERE course_id IN (
SELECT course_id FROM enrollments
GROUP BY course_id
HAVING COUNT(*) > 30
);

七、索引优化

创建索引

-- 单列索引
CREATE INDEX idx_student_name ON students(name);

-- 复合索引
CREATE INDEX idx_student_age_name ON students(age, name);

-- 唯一索引
CREATE UNIQUE INDEX idx_unique_email ON students(email);

查看索引

SHOW INDEX FROM students;

删除索引

DROP INDEX idx_student_name ON students;

八、视图

创建视图

CREATE VIEW student_summary AS
SELECT
s.id,
s.name,
COUNT(e.course_id) AS courses_enrolled
FROM students s
LEFT JOIN enrollments e ON s.id = e.student_id
GROUP BY s.id, s.name;

使用视图

SELECT * FROM student_summary
WHERE courses_enrolled > 3;

删除视图

DROP VIEW student_summary;

九、事务管理

START TRANSACTION;

-- 一系列操作
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;

-- 提交事务
COMMIT;

-- 或回滚事务
-- ROLLBACK;

十、最佳实践

  1. 命名规范:使用有意义的表名和列名(如student_id而非sid
  2. **避免SELECT ***:只选择需要的列
  3. 使用参数化查询:防止SQL注入攻击
  4. 索引优化:为经常查询的列创建索引
  5. 定期备份:使用mysqldump或类似工具
mysqldump -u username -p database_name > backup.sql

常见错误处理

权限问题

-- 授予权限
GRANT SELECT, INSERT ON school_db.* TO 'user'@'localhost';

-- 撤销权限
REVOKE INSERT ON school_db.* FROM 'user'@'localhost';

保留关键字冲突

-- 使用反引号包裹关键字
CREATE TABLE `user` (
`id` INT PRIMARY KEY,
`name` VARCHAR(50)
);

学习资源推荐

  1. SQLZoo - 交互式SQL学习平台
  2. W3Schools SQL教程 - 基础到进阶教程
  3. SQL Fiddle - 在线SQL测试环境
  4. LeetCode数据库题库 - 实战练习题

通过本教程,你已经掌握了SQL的核心概念和常用命令。持续练习是提高SQL技能的关键 - 尝试创建自己的数据库项目,解决实际问题,你将成为SQL专家!

参考&致谢

系列教程

全部文章RSS订阅

数据库系列

❌