数据库是软件开发中必不可少的组件,无论是关系型数据库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语句集,它被存储在数据 库中,经过第一次编译后再次调用时不需要被再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行 它。

存储过程是数据库中的一个重要对象,我们可以基于存储过程快 速完成复杂的计算操作。

# 应对并发的策略

数据库的并发控制一般采用三种方法实现,分别是乐观锁、悲观锁及时间戳。

  1. 乐观锁 乐观锁在读数据时,认为别人不会去写其所读的数据;悲观锁就 刚好相反,觉得自己读数据时,别人可能刚好在写自己刚读的数据, 态度比较保守;时间戳在操作数据时不加锁,而是通过时间戳来控制并发出现的问题。
  2. 悲观锁 悲观锁指在其修改某条数据时,不允许别人读取该数据,直到自 己的整个事务都提交并释放锁,其他用户才能访问该数据。悲观锁又可分为排它锁(写锁)和共享锁(读锁)。
  3. 时间戳 时间戳指在数据库表中额外加一个时间戳列TimeStamp。每次读数 据时,都把时间戳也读出来,在更新数据时把时间戳加 1,在提交之 前跟数据库的该字段比较一次,如果比数据库的值大,就允许保存, 否则不允许保存。这种处理方法虽然不使用数据库系统提供的锁机 制,但是可以大大提高数据库处理的并发量。

# 数据库锁

  1. 行级锁

行级锁指对某行数据加锁,是一种排他锁,防止其他事务修改此行。在执行以下数据库操作时,数据库会自动应用行级锁。

◎ INSERT 、 UPDATE 、 DELETE 、 SELECT … FOR UPDATE [OF columns] [WAIT n|NOWAIT]。

◎ SELECT … FOR UPDATE语句允许用户一次针对多条记录执行更新。

◎ 使用COMMIT或ROLLBACK语句释放锁。

  1. 表级锁

表级锁指对当前操作的整张表加锁,它的实现简单,资源消耗较 少,被大部分存储引擎支持。最常使用的MyISAM与InnoDB都支持表级 锁定。表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)。

  1. 页级锁

页级锁的锁定粒度介于行级锁和表级锁之间。表级锁的加锁速度 快,但冲突多,行级冲突少,但加锁速度慢。页级锁在二者之间做了平衡,一次锁定相邻的一组记录。

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

# 常见问题

解决方案:磁盘不够,使用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

修改druid的最大连接数,并进行超时回收 (opens new window)

Last Updated: 6 days ago