性能优化-索引
- 索引可以提升数据库查询性能
- 索引是预先创建的一种有序的数据结构
创建索引
CREATE INDEX index_name ON table_name (column_name);
验证索引是否生效
EXPLAIN SELECT * FROM table_name WHERE indexed_column = 'some_value';
- Seq Scan表示使用全表扫描没用索引
- Index Scan表示成功使用索引
索引的好处说完了,代价呢?
- 空间成本:会占用额外的硬盘空间
- 维护成本:虽然增加了查询
SELECT的速度,但是当数据库要INSERT,UPDATE,DELETE等操作时,还要额外操作索引,降低写操作的速度
使用原则
- 给谁用
- 出现在
WHERE子句的列
- 使用
JOINd的列
- 使用
ORDER BY的列
- 谁慎用
- 写的操作比较多的列
- 值的重复度比较高的列(比如值是性别)
索引失效
通配符在前面的时候,索引会失效
WHERE name LIKE '张%'; – 能使用索引 (从目录的’Z’部分开始找)。
WHERE name LIKE '%三'; – 无法使用索引 (不知道从哪个字母开始找),退化为全表扫描
为什么有时候创建索引了但是没有使用?
数据库中有一个查询优化器 (Query Optimizer),它会智能地判断使用索引的成本。
核心原因:当数据量非常小时,直接进行全表扫描(一眼看完一张小纸条)比先查索引再找数据(先看目录再翻页)的成本更低,效率更高。因此,优化器会智能地放弃使用索引。只有当数据量大到一定程度,索引的优势才会体现出来。
事务
将多个操作打包,要么全部成功,要么全部失败的机制
核心命令
BEGIN:开启事务
COMMIT:提交事务,将所有更改保存
ROLLBACK:回滚事务
1 2 3 4 5 6 7 8 9 10
| BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE name = '张三'; UPDATE accounts SET balance = balance + 100 WHERE name = '李四';
COMMIT;
ROLLBACK;
|
JDBC
依稀记得自己在寒假学java的时候看过这个课,也清楚的记得都忘光了
什么是JDBC
JDBC (Java Database Connectivity) 是Java提供的一套标准API,用于连接和操作关系型数据库。它本身是规范,需要由具体的JDBC驱动 (Driver) 来实现。
类比: JDBC是“普通话”规范,而PostgreSQL的JDBC驱动就是一个能将“普通话”翻译成“PostgreSQL方言”的翻译官。
使用maven添加依赖
建立连接
1 2 3 4 5 6 7 8 9 10 11
| import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; String url = "jdbc:postgresql://localhost:5432/database_name"; String user = "your_username"; String password = "your_password"; try (Connection conn = DriverManager.getConnection(url, user, password)) { } catch (SQLException e) { e.printStackTrace(); }
|
执行查询
1 2 3 4 5 6 7 8 9 10 11 12
| import java.sql.Statement; import java.sql.ResultSet;
String sql = "SELECT id, name FROM authors;"; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql)) { while (rs.next()) { int id = rs.getInt("id"); String name = rs.getString("name"); System.out.println("ID: " + id + ", Name: " + name); } }
|
执行写入
1 2 3 4 5 6 7 8 9 10 11
| import java.sql.PreparedStatement;
String sql = "INSERT INTO authors(name) VALUES(?);"; try (PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setString(1, "新作者"); int affectedRows = pstmt.executeUpdate(); System.out.println(affectedRows + " 行受影响。"); }
|
为防止SQL注入,必须使用PreparedStatement它允许我们使用 ?作为参数占位符,然后安全地将变量绑定到这些占位符上,而不是像Statement那样手动拼接SQL字符串。这既安全又高效。
在java中管理事务
点击打开
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
| public class Main { private static final String URL = "jdbc:postgresql://localhost:5432/learning_sql"; private static final String USER = "postgres"; private static final String PASSWORD = "123456"; public static void main(String[] args) { setupDatabase(); System.out.println("事务演示"); System.out.println("\n[1.zhangsan 向 lisi 成功转账100.00]"); System.out.println("转账前账户信息:"); selectAccounts(); transMoney("zhangsan","lisi",100.00); System.out.println("转账后账户余额"); selectAccounts(); System.out.println("\n[2.zhangsan 向 lisi 转账10000.00 失败]"); System.out.println("转账前账户信息:"); selectAccounts(); transMoney("zhangsan","lisi",10000.00); System.out.println("转账失败后账户余额"); selectAccounts(); System.out.println("演示结束"); } public static void transMoney (String fromAccount, String toAccount, double amount) { Connection conn = null; try { conn = DriverManager.getConnection(URL, USER, PASSWORD); conn.setAutoCommit(false); String subtractSql = "UPDATE accounts SET balance = balance - ? WHERE name = ? AND balance >= ?;"; try (PreparedStatement pstmt1 = conn.prepareStatement(subtractSql)) { pstmt1.setDouble(1, amount); pstmt1.setString(2, fromAccount); pstmt1.setDouble(3, amount); int affectedRows = pstmt1.executeUpdate(); if (affectedRows == 0) { throw new SQLException("账户 " + fromAccount + " 余额不足,无法转账。"); } System.out.println("从账户 " + fromAccount + " 扣款 " + amount); } String addSql = "UPDATE accounts SET balance = balance + ? WHERE name = ?;"; try (PreparedStatement pstmt2 = conn.prepareStatement(addSql)) { pstmt2.setDouble(1, amount); pstmt2.setString(2, toAccount); pstmt2.executeUpdate(); System.out.println("向账户 " + toAccount + " 增加 " + amount); } conn.commit(); System.out.println("转账成功:事务已经提交"); } catch (SQLException e) { System.err.println(e.getMessage()); if (conn != null) { try { System.out.println("转账失败:正在回滚事务..."); conn.rollback(); System.out.println("回滚成功"); } catch (SQLException ex) { System.err.println("回滚失败"); ex.printStackTrace(); } finally { if (conn != null) { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException ex) { ex.printStackTrace(); } } } } } } public static void setupDatabase() { try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD); Statement stmt = conn.createStatement()) { stmt.execute("DROP TABLE IF EXISTS accounts;"); stmt.execute("CREATE TABLE accounts (id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL UNIQUE, balance NUMERIC(10,2) NOT NULL);"); stmt.execute("INSERT INTO accounts (name, balance) VALUES ('zhangsan',1000.00);"); stmt.execute("INSERT INTO accounts (name, balance) VALUES ('lisi', 500.00);"); System.out.println("数据库初始化成功"); } catch (SQLException e) { System.err.println("数据库初始化失败"); e.printStackTrace(); } } public static void selectAccounts() { String sql = "SELECT name, balance FROM accounts ORDER BY name;"; try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql)) { System.out.println("--------------------------------"); while (rs.next()) { System.out.printf("账户: %s, 余额: %.2f\n", rs.getString("name"), rs.getDouble("balance")); } System.out.println("--------------------------------"); } catch (SQLException e) { e.printStackTrace(); } } }
|