数据库与ORM技术总结
数据库是后端系统的核心组件,负责数据的存储、管理和检索。ORM(对象关系映射)框架则简化了应用代码与数据库的交互,提高了开发效率。本文档全面总结了数据库设计和ORM框架的核心概念、最佳实践和面试重点。
数据库设计 = 规范化 + 性能优化 + 数据一致性 + 可扩展性 ORM框架 = 简化开发 + 类型安全 + 数据库无关 + 性能平衡
1. 数据库设计体系
1.1 关系型数据库设计
关系型数据库是最传统和广泛使用的数据库类型,基于关系模型组织数据。
设计层次
1-- 1. 概念设计层:实体关系模型(ER图)2-- 识别实体、属性和关系3-- 绘制ER图,确定实体间的联系45-- 2. 逻辑设计层:关系模式设计6-- 将ER图转换为关系模式7-- 应用规范化理论优化设计89-- 3. 物理设计层:存储和索引设计10-- 选择存储引擎和文件组织11-- 设计索引策略和分区方案规范化理论
1-- 第一范式(1NF):原子性2-- 每个字段都是不可分割的原子值3CREATE TABLE users (4 id INT PRIMARY KEY,5 name VARCHAR(100), -- 原子值6 email VARCHAR(100) -- 原子值7);89-- 第二范式(2NF):消除部分依赖10-- 非主键字段完全依赖于主键11CREATE TABLE orders (12 id INT PRIMARY KEY,13 user_id INT,14 order_date DATE,15 total_amount DECIMAL(10,2)16);1718CREATE TABLE order_items (19 id INT PRIMARY KEY,20 order_id INT,21 product_id INT,22 quantity INT,23 price DECIMAL(10,2),24 FOREIGN KEY (order_id) REFERENCES orders(id)25);2627-- 第三范式(3NF):消除传递依赖28-- 非主键字段不依赖于其他非主键字段29CREATE TABLE users (30 id INT PRIMARY KEY,31 name VARCHAR(100),32 department_id INT,33 FOREIGN KEY (department_id) REFERENCES departments(id)34);3536CREATE TABLE departments (37 id INT PRIMARY KEY,38 name VARCHAR(100),39 location VARCHAR(100)40);索引设计原则
1-- 1. 主键索引(自动创建)2CREATE TABLE users (3 id INT PRIMARY KEY AUTO_INCREMENT, -- 自动创建主键索引4 username VARCHAR(50) UNIQUE,5 email VARCHAR(100)6);78-- 2. 唯一索引(业务唯一性约束)9CREATE UNIQUE INDEX idx_users_email ON users(email);10CREATE UNIQUE INDEX idx_users_username ON users(username);1112-- 3. 普通索引(查询优化)13CREATE INDEX idx_users_department_id ON users(department_id);14CREATE INDEX idx_users_created_at ON users(created_at);1516-- 4. 复合索引(多字段查询优化)17CREATE INDEX idx_users_department_status ON users(department_id, status);18CREATE INDEX idx_users_name_email ON users(last_name, first_name, email);1920-- 5. 部分索引(条件索引)21CREATE INDEX idx_active_users ON users(email) WHERE status = 'active';1.2 非关系型数据库设计
NoSQL数据库提供了更灵活的数据模型和更好的可扩展性。
数据模型分类
1// 1. 键值存储(Key-Value)2// Redis、Memcached、DynamoDB3{4 "user:1001": "John Doe",5 "user:1002": "Jane Smith",6 "session:abc123": "{userId: 1001, loginTime: '2023-08-07'}"7}89// 2. 文档存储(Document)10// MongoDB、CouchDB、Elasticsearch11{12 "_id": "1001",13 "name": "John Doe",14 "email": "john@example.com",15 "profile": {16 "age": 30,17 "address": {18 "street": "123 Main St",19 "city": "New York"20 }21 },22 "orders": [23 {"orderId": "ORD001", "amount": 150.00},24 {"orderId": "ORD002", "amount": 200.00}25 ]26}2728// 3. 列族存储(Column Family)29// Cassandra、HBase、ScyllaDB30// 按列族组织数据,支持宽行和窄行3132// 4. 图数据库(Graph)33// Neo4j、ArangoDB、Amazon Neptune34// 以节点和边表示实体和关系设计原则
1// 1. 数据模型设计2// 根据查询模式设计数据模型3// 避免复杂的关联查询45// 2. 分片策略6// 水平分片:按数据范围或哈希值分片7// 垂直分片:按功能模块分片89// 3. 一致性模型10// 强一致性:ACID事务11// 最终一致性:BASE理论12// 因果一致性:部分顺序保证1314// 4. 可用性设计15// 多副本:主从复制、多主复制16// 故障转移:自动检测和切换17// 负载均衡:读写分离、分片路由1.3 关系型与NoSQL数据库特性对比
在选择数据库时,需要根据应用场景和需求考虑不同类型数据库的特性。
数据库类型对比
| 特性 | MySQL | PostgreSQL | MongoDB | Redis |
|---|---|---|---|---|
| 数据模型 | 关系表 | 关系表+JSON | 文档(BSON) | 键值+数据结构 |
| 查询语言 | SQL | SQL+JSON | 类JSON查询 | 命令+Lua |
| 事务支持 | 完整ACID | 完整ACID | 4.0+支持多文档事务 | 有限支持(Redis 7.0+) |
| 一致性 | 强一致性 | 强一致性 | 可配置一致性 | 单机强一致/集群弱一致 |
| 可扩展性 | 主从复制 | 主从复制+逻辑复制 | 分片+副本集 | 集群模式 |
| 数据类型 | 标准类型 | 丰富类型+扩展 | 灵活模式+BSON类型 | 多种数据结构 |
| 索引支持 | B+树 | B+树+GIN/GiST | B树+多种索引 | 有限索引 |
| 特色功能 | 简单易用 | PostGIS/JSON/全文搜索 | 文档灵活性/地理查询 | 高性能/数据结构 |
| 适用场景 | 通用应用 | 复杂查询/地理信息 | 大数据/灵活模式 | 缓存/实时应用 |
PostgreSQL核心特性
PostgreSQL是一个功能强大的开源对象关系型数据库系统,提供了众多企业级特性:
1-- 1. JSON/JSONB支持2CREATE TABLE products (3 id SERIAL PRIMARY KEY,4 name TEXT NOT NULL,5 attributes JSONB6);78-- 查询JSONB数据9SELECT * FROM products 10WHERE attributes @> '{"color": "red", "size": "large"}';1112-- 2. 高级数据类型13CREATE TABLE location_data (14 id SERIAL PRIMARY KEY,15 name TEXT,16 location GEOGRAPHY(POINT), -- 地理类型17 time_range TSRANGE, -- 时间范围18 tags TEXT[] -- 数组类型19);2021-- 3. 全文搜索22CREATE TABLE articles (23 id SERIAL PRIMARY KEY,24 title TEXT,25 body TEXT,26 ts_vector TSVECTOR -- 全文搜索向量27);2829CREATE INDEX idx_articles_fts ON articles USING GIN(ts_vector);3031SELECT title FROM articles 32WHERE ts_vector @@ to_tsquery('english', 'database & performance');3334-- 4. 表继承35CREATE TABLE devices (36 id SERIAL PRIMARY KEY,37 name TEXT NOT NULL38);3940CREATE TABLE phones (41 carrier TEXT,42 model TEXT43) INHERITS (devices);4445-- 5. 物化视图46CREATE MATERIALIZED VIEW sales_summary AS47SELECT 48 product_id, 49 SUM(quantity) as total_sold, 50 AVG(price) as avg_price51FROM sales52GROUP BY product_id;5354-- 6. 扩展系统55CREATE EXTENSION postgis; -- 地理信息系统56CREATE EXTENSION pg_stat_statements; -- SQL统计MongoDB核心特性
MongoDB是领先的文档数据库,具有高可扩展性和灵活的数据模型:
1// 1. 文档数据模型2db.users.insertOne({3 name: "John Doe",4 email: "john@example.com",5 profile: {6 age: 30,7 interests: ["coding", "music", "hiking"],8 address: {9 city: "New York",10 zip: "10001"11 }12 },13 created_at: new Date()14});1516// 2. 灵活模式17// 同一集合中的文档可以有不同的字段18db.products.insertMany([19 { name: "Phone", price: 699, specs: { ram: "8GB", storage: "128GB" } },20 { name: "Laptop", price: 1299, specs: { cpu: "i7", ram: "16GB" }, colors: ["silver", "black"] }21]);2223// 3. 强大的查询语言24db.orders.find({25 status: "completed",26 "customer.country": "US",27 total: { $gt: 100 },28 items: { $elemMatch: { product_id: "xyz123", quantity: { $gte: 2 } } }29}).sort({ created_at: -1 }).limit(10);3031// 4. 聚合框架32db.sales.aggregate([33 { $match: { date: { $gte: ISODate("2023-01-01") } } },34 { $group: {35 _id: "$product_id",36 totalSales: { $sum: "$amount" },37 count: { $sum: 1 }38 }},39 { $sort: { totalSales: -1 } },40 { $limit: 5 }41]);4243// 5. 索引支持44db.users.createIndex({ email: 1 }, { unique: true }); // 单字段索引45db.products.createIndex({ category: 1, name: 1 }); // 复合索引46db.stores.createIndex({ location: "2dsphere" }); // 地理空间索引47db.articles.createIndex({ title: "text", content: "text" }); // 全文索引4849// 6. 分片集群50// 水平扩展能力51sh.shardCollection("database.collection", { shardKey: 1 });2. ORM框架体系
2.1 JPA规范
JPA(Java Persistence API)是Java EE的持久化规范,定义了对象关系映射的标准。
核心注解
1// 1. 实体映射2@Entity3@Table(name = "users")4public class User {5 @Id6 @GeneratedValue(strategy = GenerationType.IDENTITY)7 private Long id;8 9 @Column(name = "username", nullable = false, length = 50)10 private String username;11 12 @Column(name = "email", nullable = false, unique = true)13 private String email;14 15 @Column(name = "created_at")16 @Temporal(TemporalType.TIMESTAMP)17 private Date createdAt;18}1920// 2. 关系映射21@Entity22public class User {23 @Id24 private Long id;25 26 // 一对一关系27 @OneToOne(cascade = CascadeType.ALL)28 @JoinColumn(name = "profile_id")29 private UserProfile profile;30 31 // 一对多关系32 @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)33 private List<Order> orders;34 35 // 多对一关系36 @ManyToOne37 @JoinColumn(name = "department_id")38 private Department department;39 40 // 多对多关系41 @ManyToMany42 @JoinTable(43 name = "user_roles",44 joinColumns = @JoinColumn(name = "user_id"),45 inverseJoinColumns = @JoinColumn(name = "role_id")46 )47 private Set<Role> roles;48}查询方式
1// 1. JPQL查询2@Query("SELECT u FROM User u WHERE u.email = :email")3User findByEmail(@Param("email") String email);45@Query("SELECT u FROM User u WHERE u.department.name = :deptName")6List<User> findByDepartmentName(@Param("deptName") String deptName);78// 2. 原生SQL查询9@Query(value = "SELECT * FROM users WHERE created_at >= :startDate", nativeQuery = true)10List<User> findUsersCreatedAfter(@Param("startDate") Date startDate);1112// 3. 命名查询13@NamedQuery(name = "User.findByStatus", query = "SELECT u FROM User u WHERE u.status = :status")14List<User> findByStatus(@Param("status") String status);1516// 4. Criteria API17public List<User> findUsersByCriteria(String username, String email) {18 CriteriaBuilder cb = entityManager.getCriteriaBuilder();19 CriteriaQuery<User> query = cb.createQuery(User.class);20 Root<User> user = query.from(User.class);21 22 List<Predicate> predicates = new ArrayList<>();23 if (username != null) {24 predicates.add(cb.like(user.get("username"), "%" + username + "%"));25 }26 if (email != null) {27 predicates.add(cb.equal(user.get("email"), email));28 }29 30 query.where(predicates.toArray(new Predicate[0]));31 return entityManager.createQuery(query).getResultList();32}2.2 主流ORM框架对比
框架特性对比
| 特性 | Hibernate | MyBatis | JPA | Spring Data JPA | Spring Data MongoDB |
|---|---|---|---|---|---|
| 类型 | 全功能ORM | 半自动ORM | 规范 | 数据访问抽象 | MongoDB数据访问 |
| SQL控制 | 自动生成 | 手动编写 | 自动生成 | 自动生成 | MongoDB查询 |
| 学习成本 | 高 | 低 | 中 | 低 | 低 |
| 性能 | 中等 | 高 | 中等 | 中等 | 高 |
| 灵活性 | 中等 | 高 | 中等 | 中等 | 高 |
| 适用场景 | 复杂业务 | 复杂查询 | 标准CRUD | 快速开发 | 文档存储 |
| 数据库支持 | 多种关系型 | 多种关系型 | 多种关系型 | 多种关系型 | MongoDB |
| 缓存机制 | 多级缓存 | 一级缓存 | 依赖实现 | 依赖实现 | 无内置缓存 |
| 关系映射 | 强大完整 | 需手动配置 | 完整标准 | 继承JPA | 文档引用 |
| 代码生成 | 不擅长 | 擅长 | 不擅长 | 不擅长 | 不擅长 |
Hibernate特性
1// 1. 自动SQL生成2@Entity3public class User {4 @Id5 @GeneratedValue(strategy = GenerationType.IDENTITY)6 private Long id;7 8 private String name;9 private String email;10 11 // Hibernate自动生成SQL12 // INSERT INTO users (name, email) VALUES (?, ?)13}1415// 2. 缓存机制16@Entity17@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)18public class User {19 // 二级缓存配置20}2122// 3. 延迟加载23@Entity24public class User {25 @OneToMany(fetch = FetchType.LAZY)26 private List<Order> orders; // 延迟加载27}2829// 4. 批量操作30Session session = sessionFactory.openSession();31session.beginTransaction();3233for (int i = 0; i < 1000; i++) {34 User user = new User("User" + i, "user" + i + "@example.com");35 session.save(user);36 37 if (i % 50 == 0) {38 session.flush();39 session.clear();40 }41}4243session.getTransaction().commit();44session.close();4546// 5. 对象生命周期管理47// 瞬时态:新创建的对象,未持久化48User newUser = new User("张三");4950// 持久态:通过Session保存或查询获取的对象51User persistedUser = session.get(User.class, 1L);52persistedUser.setName("李四"); // 自动检测和同步到数据库5354// 游离态:Session关闭后的对象55session.close();56// persistedUser现在是游离态5758// 6. 继承映射策略59@Entity60@Inheritance(strategy = InheritanceType.JOINED)61public abstract class Payment {62 @Id63 private Long id;64 private BigDecimal amount;65}6667@Entity68public class CreditCardPayment extends Payment {69 private String cardNumber;70}MyBatis特性
1<!-- 1. 动态SQL -->2<select id="findByCondition" parameterType="UserQuery" resultType="User">3 SELECT * FROM users4 <where>5 <if test="username != null and username != ''">6 AND username LIKE CONCAT('%', #{username}, '%')7 </if>8 <if test="email != null and email != ''">9 AND email = #{email}10 </if>11 <if test="status != null">12 AND status = #{status}13 </if>14 </where>15 ORDER BY created_at DESC16</select>1718<!-- 2. 结果映射 -->19<resultMap id="UserResultMap" type="User">20 <id column="id" property="id"/>21 <result column="username" property="username"/>22 <result column="email" property="email"/>23 <association property="profile" javaType="UserProfile">24 <id column="profile_id" property="id"/>25 <result column="real_name" property="realName"/>26 </association>27 <collection property="orders" ofType="Order">28 <id column="order_id" property="id"/>29 <result column="order_amount" property="amount"/>30 </collection>31</resultMap>3233<!-- 3. 插件机制 -->34<plugins>35 <plugin interceptor="com.example.interceptor.PerformanceInterceptor"/>36 <plugin interceptor="com.github.pagehelper.PageInterceptor">37 <property name="helperDialect" value="mysql"/>38 </plugin>39</plugins>JPA规范与实现
1// 1. 标准实体定义2@Entity3@Table(name = "products")4public class Product {5 @Id6 @GeneratedValue(strategy = GenerationType.IDENTITY)7 private Long id;8 9 @Column(nullable = false)10 private String name;11 12 @Column(precision = 10, scale = 2)13 private BigDecimal price;14 15 @Enumerated(EnumType.STRING)16 private ProductStatus status;17 18 @Temporal(TemporalType.TIMESTAMP)19 private Date createdAt;20 21 // 关系映射22 @ManyToOne23 @JoinColumn(name = "category_id")24 private Category category;25 26 // JPA生命周期回调27 @PrePersist28 protected void onCreate() {29 createdAt = new Date();30 }31}3233// 2. JPQL查询34EntityManager em = entityManagerFactory.createEntityManager();3536// 基本查询37TypedQuery<Product> query = em.createQuery(38 "SELECT p FROM Product p WHERE p.price > :minPrice", Product.class);39query.setParameter("minPrice", new BigDecimal("100.00"));40List<Product> products = query.getResultList();4142// 使用Criteria API构建动态查询43CriteriaBuilder cb = em.getCriteriaBuilder();44CriteriaQuery<Product> cq = cb.createQuery(Product.class);45Root<Product> root = cq.from(Product.class);46cq.select(root).where(cb.equal(root.get("status"), ProductStatus.ACTIVE));47List<Product> activeProducts = em.createQuery(cq).getResultList();4849// 3. Spring Data JPA50public interface ProductRepository extends JpaRepository<Product, Long> {51 // 方法名自动生成查询52 List<Product> findByPriceGreaterThanAndStatusEquals(53 BigDecimal minPrice, ProductStatus status);54 55 // 使用@Query定义JPQL56 @Query("SELECT p FROM Product p WHERE p.category.name = :categoryName")57 List<Product> findByCategoryName(@Param("categoryName") String categoryName);58 59 // 分页查询60 Page<Product> findAll(Pageable pageable);61}MongoDB数据访问
1// 1. 文档映射2@Document(collection = "users")3public class User {4 @Id5 private String id;6 7 private String name;8 private String email;9 10 @Field("created_date")11 private Date createdAt;12 13 @DBRef14 private List<Order> orders;15 16 // 嵌入式文档17 private Address address;18}1920// 2. MongoDB仓库21public interface UserRepository extends MongoRepository<User, String> {22 // 自动实现基本CRUD23 24 // 基于方法名的查询定义25 List<User> findByNameLikeAndEmailLike(String name, String email);26 27 // 基于@Query注解的查询28 @Query("{ 'address.city': ?0, 'age': { $gte: ?1 } }")29 List<User> findByCityAndAgeGreaterThan(String city, int age);30 31 // 聚合查询32 @Aggregation("{ $match: { 'status': ?0 } }, { $group: { _id: '$department', count: { $sum: 1 } } }")33 List<CountResult> countByStatus(String status);34}3536// 3. MongoTemplate使用37@Autowired38private MongoTemplate mongoTemplate;3940public List<User> findActiveUsersByCity(String city) {41 Query query = new Query();42 query.addCriteria(Criteria.where("address.city").is(city)43 .and("status").is("active"));44 return mongoTemplate.find(query, User.class);45}2.3 ORM框架选型对比
在实际项目中,ORM框架的选择应考虑以下因素:
-
业务复杂度:
- 简单CRUD操作:Spring Data JPA
- 复杂业务逻辑:Hibernate
- 复杂查询和性能要求:MyBatis
-
团队技术栈:
- Java传统企业应用:JPA/Hibernate
- 互联网公司:MyBatis
- 微服务架构:Spring Data JPA
-
灵活性与控制力:
- 高度控制SQL:MyBatis
- 快速开发:Spring Data JPA
- 复杂对象图:Hibernate
-
性能要求:
- 极致性能:JDBC/MyBatis
- 均衡性能:JPA/Hibernate + 缓存
- 读写分离:自定义数据源 + ORM
3. 数据库性能优化
3.1 索引优化
索引策略
1-- 1. 索引选择策略2-- 高选择性列优先3CREATE INDEX idx_users_email ON users(email); -- 高选择性4CREATE INDEX idx_users_gender ON users(gender); -- 低选择性56-- 查询频率高的列7CREATE INDEX idx_users_status ON users(status); -- 经常按状态查询89-- 外键列10CREATE INDEX idx_orders_user_id ON orders(user_id); -- 加速JOIN1112-- 排序和分组列13CREATE INDEX idx_users_created_at ON users(created_at); -- 加速ORDER BY1415-- 2. 复合索引设计16CREATE INDEX idx_users_department_status_created ON users(department_id, status, created_at);1718-- 可以使用索引的查询19SELECT * FROM users WHERE department_id = 1;20SELECT * FROM users WHERE department_id = 1 AND status = 'active';21SELECT * FROM users WHERE department_id = 1 AND status = 'active' AND created_at > '2023-01-01';2223-- 不能使用索引的查询24SELECT * FROM users WHERE status = 'active'; -- 缺少department_id25SELECT * FROM users WHERE department_id = 1 AND created_at > '2023-01-01'; -- 缺少status2627-- 3. 索引优化技巧28-- 避免在索引列上使用函数29SELECT * FROM users WHERE YEAR(created_at) = 2023; -- 无法使用索引30SELECT * FROM users WHERE created_at >= '2023-01-01' AND created_at < '2024-01-01'; -- 可以使用索引3132-- 避免使用!=或<>操作符33SELECT * FROM users WHERE status != 'inactive'; -- 无法使用索引34SELECT * FROM users WHERE status IN ('active', 'pending', 'suspended'); -- 可以使用索引3.2 SQL优化
查询优化技巧
1-- 1. 避免SELECT *2SELECT id, name, email FROM users WHERE department_id = 1; -- 推荐3SELECT * FROM users WHERE department_id = 1; -- 不推荐45-- 2. 使用LIMIT限制结果集6SELECT * FROM orders ORDER BY created_at DESC LIMIT 10;78-- 3. 优化JOIN查询9-- 使用INNER JOIN而不是逗号连接10SELECT u.name, o.order_number 11FROM users u INNER JOIN orders o ON u.id = o.user_id 12WHERE u.status = 'active'; -- 推荐1314SELECT u.name, o.order_number 15FROM users u, orders o 16WHERE u.id = o.user_id AND u.status = 'active'; -- 不推荐1718-- 4. 子查询优化19-- 将子查询转换为JOIN20SELECT u.* FROM users u21JOIN departments d ON u.department_id = d.id22WHERE d.name = 'IT'; -- 推荐2324SELECT * FROM users 25WHERE department_id IN (SELECT id FROM departments WHERE name = 'IT'); -- 不推荐2627-- 5. 使用EXISTS代替IN(大数据量时)28SELECT * FROM customers c29WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id); -- 推荐3031SELECT * FROM customers 32WHERE id IN (SELECT DISTINCT customer_id FROM orders); -- 不推荐3.3 缓存策略
缓存模式
1// 1. Cache-Aside模式2public User getUser(Long userId) {3 // 先从缓存获取4 String key = "user:" + userId;5 User user = redisTemplate.opsForValue().get(key);6 7 if (user != null) {8 return user;9 }10 11 // 缓存未命中,从数据库获取12 user = userRepository.findById(userId);13 14 if (user != null) {15 // 写入缓存16 redisTemplate.opsForValue().set(key, user, 1, TimeUnit.HOURS);17 }18 19 return user;20}2122// 2. Write-Through模式23public void updateUser(User user) {24 // 先更新数据库25 userRepository.save(user);26 27 // 再更新缓存28 String key = "user:" + user.getId();29 redisTemplate.opsForValue().set(key, user, 1, TimeUnit.HOURS);30}3132// 3. Write-Behind模式33public void updateUserAsync(User user) {34 // 先更新缓存35 String key = "user:" + user.getId();36 redisTemplate.opsForValue().set(key, user, 1, TimeUnit.HOURS);37 38 // 异步更新数据库39 CompletableFuture.runAsync(() -> {40 userRepository.save(user);41 });42}4. 事务管理
4.1 ACID特性
事务特性详解
1// 1. 原子性(Atomicity)2@Transactional3public void transferMoney(Long fromAccountId, Long toAccountId, BigDecimal amount) {4 // 所有操作要么全部成功,要么全部失败5 Account fromAccount = accountRepository.findById(fromAccountId);6 Account toAccount = accountRepository.findById(toAccountId);7 8 fromAccount.setBalance(fromAccount.getBalance().subtract(amount));9 toAccount.setBalance(toAccount.getBalance().add(amount));10 11 accountRepository.save(fromAccount);12 accountRepository.save(toAccount);13 14 // 如果任何一步失败,整个事务回滚15}1617// 2. 一致性(Consistency)18// 事务执行前后数据库状态一致19// 转账前后总金额保持不变2021// 3. 隔离性(Isolation)22@Transactional(isolation = Isolation.READ_COMMITTED)23public User getUser(Long userId) {24 // 事务之间相互隔离25 return userRepository.findById(userId);26}2728// 4. 持久性(Durability)29// 事务提交后永久保存30// 即使系统崩溃,已提交的事务不会丢失4.2 事务隔离级别
隔离级别详解
1// 1. READ UNCOMMITTED(读未提交)2@Transactional(isolation = Isolation.READ_UNCOMMITTED)3public User getUser(Long userId) {4 // 可能读取到未提交的数据(脏读)5 return userRepository.findById(userId);6}78// 2. READ COMMITTED(读已提交)9@Transactional(isolation = Isolation.READ_COMMITTED)10public User getUser(Long userId) {11 // 避免脏读,可能出现不可重复读12 return userRepository.findById(userId);13}1415// 3. REPEATABLE READ(可重复读)16@Transactional(isolation = Isolation.REPEATABLE_READ)17public User getUser(Long userId) {18 // 避免不可重复读,可能出现幻读19 return userRepository.findById(userId);20}2122// 4. SERIALIZABLE(串行化)23@Transactional(isolation = Isolation.SERIALIZABLE)24public User getUser(Long userId) {25 // 完全避免并发问题,性能最低26 return userRepository.findById(userId);27}5. 面试题精选
5.1 数据库设计题
Q: 数据库设计的三个范式是什么?如何应用?
A: 三个范式分别是:
- 第一范式(1NF):每个字段都是不可分割的原子值
- 第二范式(2NF):在1NF基础上,非主键字段完全依赖于主键
- 第三范式(3NF):在2NF基础上,非主键字段不依赖于其他非主键字段
应用原则:根据业务需求适度规范化,避免过度规范化影响性能。
Q: 如何设计数据库索引?有哪些注意事项?
A: 索引设计原则:
- 高选择性列优先:不同值数量多,重复值少
- 查询频率高的列:为频繁查询的列创建索引
- 外键列:外键列通常需要索引以加速JOIN操作
- 排序和分组列:为ORDER BY和GROUP BY的列创建索引
- 避免过多索引:索引会占用存储空间,影响写入性能
5.2 ORM框架题
Q: Hibernate和MyBatis的区别是什么?如何选择?
A: 主要区别:
- Hibernate:全功能ORM,自动生成SQL,学习成本高,适合复杂业务
- MyBatis:半自动ORM,手动编写SQL,学习成本低,适合复杂查询
选择原则:根据项目需求选择,复杂查询选择MyBatis,标准CRUD选择Hibernate。
Q: JPA的实体关系映射有哪些?如何使用?
A: JPA关系映射包括:
- @OneToOne:一对一关系
- @OneToMany:一对多关系
- @ManyToOne:多对一关系
- @ManyToMany:多对多关系
使用要点:合理设置级联操作、延迟加载和关联查询策略。
5.3 性能优化题
Q: 如何优化数据库查询性能?
A: 优化策略包括:
- 索引优化:创建合适的索引,避免在索引列上使用函数
- SQL优化:避免SELECT *,使用LIMIT,合理使用JOIN
- 查询重写:将子查询转换为JOIN,使用EXISTS代替IN
- 缓存策略:合理使用缓存,避免缓存穿透、击穿、雪崩
- 分页查询:避免大结果集查询,使用分页
Q: 什么是缓存穿透、击穿、雪崩?如何解决?
A: 缓存问题及解决方案:
- 缓存穿透:查询不存在的数据,解决方案:布隆过滤器、缓存空值
- 缓存击穿:热点数据过期,解决方案:互斥锁、热点数据永不过期
- 缓存雪崩:大量缓存同时过期,解决方案:随机过期时间、多级缓存
5.4 事务管理题
Q: 数据库事务的ACID特性是什么?
A: ACID特性:
- 原子性(Atomicity):事务是不可分割的工作单位
- 一致性(Consistency):事务执行前后数据库状态一致
- 隔离性(Isolation):事务之间相互隔离
- 持久性(Durability):事务提交后永久保存
Q: 事务隔离级别有哪些?各有什么特点?
A: 事务隔离级别:
- READ UNCOMMITTED:读未提交,可能出现脏读
- READ COMMITTED:读已提交,可能出现不可重复读
- REPEATABLE READ:可重复读,可能出现幻读
- SERIALIZABLE:串行化,完全避免并发问题
5.5 高可用题
Q: 如何实现数据库的高可用?
A: 高可用方案包括:
- 主从复制:一主多从,读写分离
- 集群模式:数据分片,自动故障转移
- 负载均衡:使用代理或中间件分发请求
- 数据备份:定期备份,支持快速恢复
- 监控告警:实时监控,及时发现问题
Q: 分库分表的策略有哪些?
A: 分库分表策略:
- 水平分表:按行分割,如按时间、ID范围分表
- 垂直分表:按列分割,将大表拆分为多个小表
- 水平分库:按数据库分割
- 垂直分库:按业务模块分割
- 分片策略:取模、范围、哈希等
5.6 PostgreSQL与MongoDB面试题
Q: PostgreSQL和MySQL有哪些主要区别?如何选择?
A: PostgreSQL与MySQL的主要区别:
- 功能丰富度:PostgreSQL提供更丰富的数据类型、更强大的索引类型和更多的SQL扩展
- 扩展性:PostgreSQL有完善的扩展机制,如PostGIS地理信息扩展
- 事务处理:PostgreSQL提供更完善的事务隔离和MVCC支持
- 复杂查询:PostgreSQL更适合处理复杂查询和数据分析
- 合规性:PostgreSQL对SQL标准的遵循度更高
选择标准:
- 简单应用且易于管理优先考虑MySQL
- 复杂应用、空间数据、分析型应用优先考虑PostgreSQL
Q: MongoDB适合什么场景?不适合什么场景?
A: MongoDB适合场景:
- 非结构化或半结构化数据:灵活的文档模型适合变化的数据结构
- 高写入量:高性能写入和水平扩展能力
- 大数据存储:通过分片可扩展到TB级数据
- 敏捷开发:模式灵活支持快速迭代
- 内容管理:适合博客、文章等内容存储
不适合场景:
- 复杂事务:多文档事务支持仍有限制
- 复杂关联查询:JOIN操作不如关系型数据库高效
- 强一致性要求:最终一致性可能不适合某些业务场景
- 固定结构数据:高度规范化的数据可能更适合关系型数据库
Q: 如何优化PostgreSQL和MongoDB性能?
A: PostgreSQL性能优化:
- 索引优化:合理使用B-tree、GIN、BRIN等不同类型索引
- 查询优化:EXPLAIN分析、查询重写和物化视图
- 配置调整:shared_buffers、work_mem、effective_cache_size等参数调优
- 表分区:大表使用表分区提高性能
- 并行查询:利用多CPU并行处理大型查询
MongoDB性能优化:
- 索引策略:创建正确的索引支持常用查询
- 文档结构:设计合理的文档结构避免深层嵌套
- 分片策略:选择良好的分片键实现数据均衡分布
- 读写分离:利用读取偏好从次要节点读取
- 批量操作:使用批量插入/更新而非单条操作
5.7 Hibernate与JPA面试题
Q: Hibernate的Session和JPA的EntityManager有什么异同?
A: Hibernate Session与JPA EntityManager的比较:
- 关系:EntityManager是JPA规范的接口,Session是Hibernate的接口,EntityManager在Hibernate中的实现内部使用了Session
- 功能相似性:两者都提供实体的基本CRUD操作、查询功能和事务支持
- API差异:
- Session提供save()、update()、saveOrUpdate()等方法
- EntityManager提供persist()、merge()、remove()等方法
- 缓存操作:Session提供更多的缓存控制API,如evict()、clear()
- 查询API:Session有更丰富的查询API,如createCriteria()
Q: Hibernate的对象状态转换是怎样的?
A: Hibernate对象状态:
- 瞬时态(Transient):新创建的对象,没有持久化标识符,未与Session关联
- 持久态(Persistent):与当前Session关联的对象,有持久化标识符,修改会自动同步到数据库
- 游离态(Detached):曾经持久化,但当前未与任何Session关联的对象
- 删除态(Removed):被标记为删除的对象
状态转换:
- 瞬时态→持久态:
session.save(),session.persist() - 持久态→游离态:
session.evict(),session.clear(),session.close() - 游离态→持久态:
session.update(),session.merge(),session.saveOrUpdate() - 持久态→删除态:
session.delete()
Q: Hibernate的一级缓存和二级缓存有什么区别?如何优化?
A: Hibernate缓存比较:
-
一级缓存(Session缓存):
- 作用域:单个Session
- 生命周期:Session开启到关闭
- 特点:强制启用,不能禁用
- 数据一致性:自动管理,与事务绑定
-
二级缓存(SessionFactory缓存):
- 作用域:应用级别,所有Session共享
- 生命周期:应用程序生命周期
- 特点:需要显式配置,可选择缓存提供者(EhCache, Infinispan等)
- 数据一致性:需要配置合适的缓存并发策略
优化策略:
- 合理设置缓存对象,只缓存不经常变化的数据
- 为频繁查询的实体启用二级缓存
- 为只读数据配置READ_ONLY策略,提高性能
- 合理设置缓存过期时间和大小
- 监控缓存命中率,调整缓存策略
Q: JPA与Spring Data JPA的区别是什么?
A: JPA与Spring Data JPA的区别:
-
定义:
- JPA(Java Persistence API)是Java持久化的标准规范
- Spring Data JPA是在JPA基础上的更高抽象,简化数据访问层开发
-
功能对比:
- JPA提供基本的对象关系映射和实体管理
- Spring Data JPA提供方法名生成查询、分页排序、自动实现Repository等功能
-
使用方式:
- JPA需要编写实现类和查询代码
- Spring Data JPA只需定义接口,无需编写实现类
-
扩展性:
- Spring Data JPA支持多种数据存储(MongoDB, Redis等)
- JPA主要针对关系型数据库
Q: 如何处理Hibernate的N+1查询问题?
A: N+1查询问题解决方案:
-
使用JOIN FETCH:
java1// 解决N+1问题2String hql = "SELECT o FROM Order o JOIN FETCH o.items WHERE o.status = :status"; -
使用EntityGraph:
java1@EntityGraph(attributePaths = {"items", "customer"})2Order findById(Long id); -
使用批量获取:
java1// 在实体类上配置2@BatchSize(size = 20)3@OneToMany(mappedBy = "order")4private Set<OrderItem> items;56// 或全局配置7<property name="hibernate.default_batch_fetch_size" value="20" /> -
在查询中预先指定加载关系:
java1Criteria criteria = session.createCriteria(Order.class);2criteria.setFetchMode("items", FetchMode.JOIN);
- 掌握数据库设计原则:理解规范化理论,合理设计表结构
- 熟悉ORM框架特性:掌握JPA、Hibernate、MyBatis、Spring Data的使用
- 学会性能优化技巧:掌握索引优化、SQL优化、缓存策略
- 理解事务管理机制:掌握ACID特性、隔离级别、锁机制
- 了解高可用架构:掌握主从复制、集群模式、分库分表
- 掌握NoSQL特性:理解文档数据库和键值数据库的场景与用法
- 深入ORM原理:理解对象状态、缓存机制、延迟加载
通过本章的学习,你应该已经全面掌握了数据库设计和ORM框架的核心概念、最佳实践和面试重点。数据库是后端系统的核心,合理的设计和优化能够显著提升系统性能和用户体验。在实际项目中,需要根据业务需求选择合适的数据库类型和ORM框架,并持续优化数据库性能。
评论