数据库是软件开发中必不可少的组件,无论是关系型数据库MySQL、Oracle,还是NoSQL数据库Redis、MongoDB,都针对不同的应用场景解决不同的问题。
# 存储引擎
数据库的存储引擎是数据库的底层软件组织,数据库管理系统(DBMS)使用存储引擎创建、查询、更新和删除数据。
常用的存储引擎主要有MyISAM、InnoDB、Memory。
MyIASM
MyIASM是MySQL默认的存储引擎,不支持数据库事务、行级锁和外键,因此在INSERT(插入)或UPDATE(更新)数据即写操作时需要锁定整个表,效率较低。
MyIASM的特点是执行读取操作的速度快,且占用的内存和存储资源较少。
总体来说,MyIASM的缺点是更新数据慢且不支持事务处理,优点是查询速度快。
InnoDB
InnoDB 为 MySQL 提 供 了 事 务 ( Transaction ) 支 持 、 回 滚(Rollback)、崩溃修复能力(Crash Recovery Capabilities)、事务安全 (Transaction-safe)的操作。InnoDB的底层存储结构为B+树,B+树的每个节点都对应InnoDB的一个Page,Page大小是固定的,一般被设为16KB。其中,非叶子节点只有键值,叶子节点包含完整的数据,
Memory
Memory表使用内存空间创建。每个Memory表实际上都对应一个磁盘文件用于持久化。Memory表因为数据是存放在内存中的,因此访问速度非常快,通常使用Hash索引来实现数据索引。Memory表的缺点是一旦服务关闭,表中的数据就会丢失。
# 创建索引的原则
# 数据库三范式
# 数据库事务
# 存储过程
存储过程指一组用于完成特定功能的SQL语句集,它被存储在数据 库中,经过第一次编译后再次调用时不需要被再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行 它。
存储过程是数据库中的一个重要对象,我们可以基于存储过程快 速完成复杂的计算操作。
# 应对并发的策略
数据库的并发控制一般采用三种方法实现,分别是乐观锁、悲观锁及时间戳。
- 乐观锁 乐观锁在读数据时,认为别人不会去写其所读的数据;悲观锁就 刚好相反,觉得自己读数据时,别人可能刚好在写自己刚读的数据, 态度比较保守;时间戳在操作数据时不加锁,而是通过时间戳来控制并发出现的问题。
- 悲观锁 悲观锁指在其修改某条数据时,不允许别人读取该数据,直到自 己的整个事务都提交并释放锁,其他用户才能访问该数据。悲观锁又可分为排它锁(写锁)和共享锁(读锁)。
- 时间戳 时间戳指在数据库表中额外加一个时间戳列TimeStamp。每次读数 据时,都把时间戳也读出来,在更新数据时把时间戳加 1,在提交之 前跟数据库的该字段比较一次,如果比数据库的值大,就允许保存, 否则不允许保存。这种处理方法虽然不使用数据库系统提供的锁机 制,但是可以大大提高数据库处理的并发量。
# 数据库锁
- 行级锁
行级锁指对某行数据加锁,是一种排他锁,防止其他事务修改此行。在执行以下数据库操作时,数据库会自动应用行级锁。
◎ INSERT 、 UPDATE 、 DELETE 、 SELECT … FOR UPDATE [OF columns] [WAIT n|NOWAIT]。
◎ SELECT … FOR UPDATE语句允许用户一次针对多条记录执行更新。
◎ 使用COMMIT或ROLLBACK语句释放锁。
- 表级锁
表级锁指对当前操作的整张表加锁,它的实现简单,资源消耗较 少,被大部分存储引擎支持。最常使用的MyISAM与InnoDB都支持表级 锁定。表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)。
- 页级锁
页级锁的锁定粒度介于行级锁和表级锁之间。表级锁的加锁速度 快,但冲突多,行级冲突少,但加锁速度慢。页级锁在二者之间做了平衡,一次锁定相邻的一组记录。
4.基于Redis的分布式锁(锁超时的时间不容易确定)
数据库锁是基于单个数据库实现的,在我们的业务跨多个数据库 时,就要使用分布式锁来保证数据的一致性。
下面介绍使用Redis实现 一个分布式锁的流程。 Redis实现的分布式锁以Redis setnx命令为中心实现 , setnx 是 Redis 的 写 入 操 作 命 令 , 具 体 语 法 为 setnx(key val)。 在且仅在key不存在时,则插入一个key为val的字符串,返回1;若key存在,则什么都不做,返回0。通过setnx实现分布式锁的思路如下。
◎ 获取锁:在获取锁时调用setnx,如果返回 0,则该锁正在被别人使用;如果返回1,则成功获取锁。
◎ 释放锁:在释放锁时,判断锁是否存在,如果存在,则执行Redis的delete操作释放锁。
简单的Redis实现分布式锁的代码如下,注意,如果锁并发比较 大,则可以设置一个锁的超时时间,在超时时间到后,Redis会自动释放锁给其他线程使用:
# 数据库分表
# Druid
Druid(发音:丢的)是阿里巴巴开源的一款性能优秀的数据库连接池,同时扩展了SQL监控的相关功能。
# 依赖
<dependencies>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<!-- 或者druid与springboot整合-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
</dependencies>
# 配置
# jdbc.properties
# JDBC
# MySQL 8.x com.mysql.cj.jdbc.Driver
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.connectionURL=jdbc:mysql://xxxx
jdbc.username=root
jdbc.password=123456
# JDBC Pool
jdbc.pool.init=1
jdbc.pool.minIdle=3
jdbc.pool.maxActive=20
# JDBC Test
jdbc.testSql=SELECT 'x' FROM DUAL
# .yml
spring:
cloud:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
url: jdbc:mysql://aaa.bbb.ccc.ddd:3306/qcsd?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
username: root
password: xxx
# 初始化大小
initial-size: 3
# pool 最大值
max-active: 10
# pool 最小值
min-idle: 3
# 配置获取连接等待超时的时间
max-wait: 60000
# 打开PSCache,并且指定每个连接上PSCache的大小
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
time-between-eviction-runs-millis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
min-evictable-idle-time-millis: 300000
#validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
stat-view-servlet:
enabled: true
url-pattern: /druid/*
login-username: xxx
login-password: yyy
filter:
stat:
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: false
wall:
config:
multi-statement-allow: true
# DataSource注入
如果使用SpringBoot的AutoConfigure的话,这一步就可以省了。
否则需要给SpringBoot容器中注入DataSource
# 监控中心
Druid提供了大量的监控数据,只需要在web.xml
中配置一个Servlet
就可以方便地查看这些信息。
修改web.xml
配置文件,增加Druid提供的Servlet
。
<servlet>
<servlet-name>DruidStatView</servlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DruidStatView</servlet-name>
<url-pattern>/druid/*</url-pattern>
</servlet-mapping>
打开浏览器,输入: http://192.168.3.110:30999/base/druid/login.html
浏览器显示效果如下:
# 进入mysql
mysql -hlocalhsot -uroot -p
# 查看数据库 以分号结尾
show databases;
# 最大连接数
# 修改最大连接数
whereis my.cnf
vim /etc/my.cnf
# 常见问题
# navicat 报错:1030 get error 28 from storage engine
解决方案:磁盘不够,使用docker system prune
清理镜像即可。
# 1040 is blocked because of many connection errors
连接数过多,解决方案:增加连接数/重启mysql
# "unknown variable 'max_connections=75'" when trying to start mysql
whereis my.cnf
vim /etc/my.cnf
Please edit [mysql] to [mysqld] and start mysql
# com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60000, active 20, maxActive 20
← 算法 ORM·MyBatis →