mysql数据库相关知识

mysql数据库相关知识

1 jdbc操作数据库步骤以及代码
第一步:Class.forName() 加载数据库连接驱动;
第二步:DriverManager.getConnection()获取数据连接对象;
第三步:根据SQL 获取 sql 会话对象,有 2 种方式 Statement、PreparedStatement ;
第四步:执行SQL 处理结果集,执行 SQL 前如果有参数值就设置参数值 setXXX();
第五步:关闭结果集、关闭会话、关闭连接。

注意: 如果是mysql8.0版本的话,驱动应该是com.mysql.cj.jdbc.Driver,而mysql5.x版本就是com.mysql.jdbc.Driver

public class JDBCDemo {
    public static void main(String[] args) {
        // JDBC连接信息
        String url = "jdbc:mysql://localhost:3306/mydatabase";  // 数据库名 mydatabase
        String username = "root";  
        String password = "password";  

        // 加载数据库驱动程序
        try {
            Class.forName("com.mysql.jdbc.Driver");  // 连接驱动;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            return;
        }

        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;

        try {
            // 建立数据库连接
            connection = DriverManager.getConnection(url, username, password);

            // 创建Statement对象
            statement = connection.createStatement();

            // 执行SQL语句
            String sql = "SELECT * FROM customers";  //  customers是数据库的表;
            resultSet = statement.executeQuery(sql);

            // 处理结果集
            while (resultSet.next()) {
                int id = resultSet.getInt("id");
                String name = resultSet.getString("name");
                System.out.println("ID: " + id + ", Name: " + name);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            try {
                if (resultSet != null) {
                    resultSet.close();   // 关闭结果集
                }
                if (statement != null) {
                    statement.close();  //  关闭会话
                }
                if (connection != null) {
                    connection.close();  // 关闭连接;
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

额外的一点:
PreparedStatement比Statement更适合执行多次相同或类似的SQL语句,特别是需要传递参数的情况。它提供了更好的性能、更高的安全性,并且代码更清晰易读。然而,如果只需要执行一次简单的SQL语句,或者没有参数化查询的需求,Statement对象也是一个可行的选择。

2 MYSQL事务以及隔离级别
事务的基本要素(ACID):
原子性(Atomicity)事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。
一致性(Consistency)事务开始前和结束后,数据库的完整性约束没有被破坏 。比如甲向乙转账,不可能甲扣了钱,乙却没收到。
隔离性(Isolation)同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。
持久性(Durability)事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。

事务带来的问题
脏读 不可重复读 幻读
(1)脏读: 在一个事务中读取到了另一个未提交事务所修改的数据。脏读可能导致数据的不一致性和错误的结果。
举例:
假设有两个事务:事务A和事务B,它们同时访问同一个数据库表中的数据。
事务A执行如下操作:

BEGIN TRANSACTION;
SELECT balance FROM accounts WHERE id = 123; -- 读取账户余额
-- 在此期间,事务B执行了一次更新操作
UPDATE accounts SET balance = balance - 100 WHERE id = 123; -- 减少账户余额
-- 在此期间,事务B未提交事务
COMMIT;

事务B执行如下操作:

BEGIN TRANSACTION;
UPDATE accounts SET balance = balance + 500 WHERE id = 123; -- 增加账户余额
COMMIT;

如果事务A在读取账户余额的时候发生了脏读,它将读取到事务B未提交的更新结果,导致事务A获取到的账户余额是不准确的。

(2) 不可重复读
指在同一个事务中,对于相同的查询条件,多次读取同一行数据时,可能会得到不同的结果。这是由于其他事务在该事务读取期间对数据进行了修改或删除。
举例:
假设有两个事务:事务A和事务B,它们同时访问同一个数据库表中的数据。
事务A执行如下操作:

BEGIN TRANSACTION;
SELECT balance FROM accounts WHERE id = 123; -- 读取账户余额
-- 在此期间,事务B执行了一次更新操作
UPDATE accounts SET balance = balance - 100 WHERE id = 123; -- 减少账户余额
COMMIT;

事务B执行如下操作

BEGIN TRANSACTION;
UPDATE accounts SET balance = balance + 500 WHERE id = 123; -- 增加账户余额
COMMIT;

如果事务A在读取账户余额后再次读取,可能会得到不同的结果。因为事务B在事务A的读取期间对数据进行了修改,导致事务A的两次读取结果不一致。

(3)幻读
同一个事务中,多次执行相同的查询条件时,可能会返回不同的行数或数据集合。不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。

举例:
假设有两个事务:事务A和事务B,它们同时访问同一个数据库表中的数据。
事务A执行如下操作:

BEGIN TRANSACTION;
SELECT * FROM products WHERE category = 'Electronics'; -- 查询电子产品
-- 在此期间,事务B执行了一次插入操作
INSERT INTO products (category, name) VALUES ('Electronics', 'New Product'); -- 插入新的电子产品
COMMIT;

事务B执行如下操作:

BEGIN TRANSACTION;
INSERT INTO products (category, name) VALUES ('Electronics', 'Another Product'); -- 插入另一个电子产品
COMMIT;

如果事务A在第一次查询电子产品之后再次查询,可能会返回不同的行数或数据集合。因为事务B在事务A的查询期间插入了新的电子产品,导致事务A的两次查询结果不一致,产生了幻读现象。

那如何解决脏读,不可重复读 幻读呢,接下来讲解事务的隔离级别。

MYSQL事务隔离级别有四种。分别是
读未提交(Read Uncommitted)最低的隔离级别可能会出现脏读、不可重复读和幻读的问题。
读已提交(Read Committed):事务只能读取已经提交的数据可以避免脏读问题,但仍可能出现不可重复读和幻读的问题。
可重复读(Repeatable Read):默认的隔离级别,保证在同一事务内多次读取同样的数据时,结果保持一致。可以避免脏读和不可重复读问题,但仍可能出现幻读的问题。
串行化(Serializable):最高的隔离级别,事务按照顺序一个接一个地执行,保证数据的完全一致性。可以避免脏读、不可重复读和幻读问题。

可以使用以下语句设置事务的隔离级别:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
需要根据应用需求和具体情况选择适当的隔离级别。较低的隔离级别可以提高并发性能,但可能引发数据不一致的问题,而较高的隔离级别可以保证数据的一致性,但并发性能较低。

3 执行引擎InnoDB和MyISAM
数据库执行引擎是数据库管理系统(DBMS)中负责执行和处理数据库操作的核心组件。不同的数据库管理系统可以使用不同的执行引擎,每个执行引擎具有其自己的特性和优化策略。接下来对比InnoDB和MyISAM的区别。

事务支持InnoDB是一个支持事务的执行引擎而MyISAM不支持事务,只支持表级锁定
并发性能InnoDB支持行级锁定,可以实现更高的并发性能多个事务可以并发地访问和修改不同的行,减少了锁冲突而MyISAM只支持表级锁定,即一个事务对于整个表进行锁定,当有并发操作时可能会导致性能下降
完整性约束InnoDB支持外键约束MyISAM不支持外键约束。
崩溃恢复InnoDB具有崩溃恢复的能力,可以通过事务日志(redo log)来恢复未完成的事务而MyISAM不支持崩溃恢复,它在崩溃后可能导致数据的不一致性。
全文搜索MyISAM支持全文搜索功能,可以进行高效的全文搜索操作。而InnoDB在MySQL 5.6版本之后也开始支持全文搜索。

4 索引
(1) 为什么需要使用索引:
快速数据访问:索引允许数据库引擎快速定位和访问数据,而不必扫描整个数据表。通过使用适当的索引,可以大大减少查询所需的时间,特别是对于大型数据表和复杂查询。
提高查询性能:索引可以加速数据的查找和匹配过程。当执行查询语句时,数据库引擎可以使用索引直接定位到匹配的数据行,而不需要逐行扫描整个表。这可以显著减少查询的执行时间。
优化排序和分组:索引可以帮助优化排序和分组操作。当查询需要对结果进行排序或分组时,数据库引擎可以使用索引的有序性,避免对整个数据集进行排序操作,提高排序和分组的效率。
提高连接操作的效率:当进行连接(Join)操作时,索引可以加快连接的速度。连接操作通常需要在多个表之间查找匹配的数据行,使用适当的索引可以大大减少连接操作所需的时间。
约束数据完整性:索引可以用于实施唯一性约束和主键约束,确保数据表中的数据的唯一性和完整性。

(2) 为什么使用数据索引能提高效率
索引就是通过事先排好序,从而在查找时可以应用二分查找等高效率的算法。
在一个或者一些字段需要频繁用作查询条件,并且表数据较多的时候,创建索引会明显提高查询速度,因为可由全表扫描改成索引扫描
无索引,直接去读表数据存放的磁盘块,读到数据缓冲区中再查找需要的数据。
有索引,先读入索引表,通过索引表直接找到所需数据的物理地址,并把数据读入数据缓冲区中。

(3)索引的结构
这里我们需要讲解索引的底层结构。MYSQL索引一般有B树索引和哈希索引,这里我们一一讲解。

首先来认识B树和B+树
B树: B树是一种多路自平衡搜索树,它类似普通的二叉树,但是B树允许每个节点有更多的子节点。
特点:
所有键值分布在整个树中
任何关键字出现且只出现在一个节点中
搜索有可能在非叶子节点结束
在关键字全集内做一次查找,性能逼近二分查找算法。

B+树:
相比于B树,B+树的不同之处在于
非叶子节点只存储键值信息
所有叶子节点之间都有一个链指针
数据记录都存放在叶子节点中

由此,B树不利于范围查询,B+树适合范围查询。

那何为哈希索引,为什么在MySQL中,使用B+树索引的情况更为常见和普遍,而哈希索引的应用相对较少?
从底层来分析:
hash索引底层: hash表,进行查找时,调用一次hash函数就可以获取到相应的键值,之后进行回表查询获得实际数据。
B+树底层实现是多路平衡查找树.对于每一次的查询都是从根节点出发,查找到叶子节点方可以获得所查键值,然后根据查询判断是否需要回表查询数据。

那么可以看出他们有以下的不同:
hash索引: 等值查询更快(一般情况下),**但是却无法进行范围查询.**因为在hash索引中经过hash函数建立索引之后,索引的顺序与原顺序无法保持一致,不能支持范围查询。
B+树的的所有节点皆遵循(左节点小于父节点,右节点大于父节点,多叉树也类似),数据结构支持范围查询。
hash索引不支持使用索引进行排序, 原因也是如此。
hash索引不支持模糊查询以及多列索引的最左前缀匹配。因为hash函数的不可预测。MMM和MMMN的索引没有相关性。
hash索引任何时候都避免不了回表查询数据,而B+树在符合某些条件(聚簇索引,覆盖索引等)的时候可以只通过索引完成查询。
hash索引虽然在等值查询上较快,当某个键值存在大量重复的时候,发生hash碰撞,此时效率可能极差。
而B+树的查询效率比较稳定,对于所有的查询都是从根节点到叶子节点,且树的高度较低。
因此,在大多数情况下,直接选择B+树索引可以获得稳定且较好的查询速度,而不需要使用hash索引

(4)索引失效的情况并举例
MySQL索引可能会失效的一些情况包括:
<1> 列数据类型不匹配:当查询条件中的列数据类型与索引列的数据类型不匹配时,索引将无法使用。例如,索引列为整数类型,但查询条件中使用了字符串类型。

<2> 列使用函数或表达式:如果查询条件中的列使用了函数或表达式进行运算或转换,索引可能无法使用。例如,查询条件中使用了字符串函数、日期函数或数学运算。

<3> 列使用了类型转换:当查询条件中的列进行了类型转换时,索引可能无法使用。例如,索引列为整数类型,但查询条件中使用了隐式或显式的类型转换。

<4> 列使用了LIKE操作符:如果查询条件中的列使用了LIKE操作符,并且匹配模式以通配符开头(如’%abc’),索引可能无法使用。

<5> 列使用了OR条件:当查询条件中的列使用了OR条件时,如果OR条件中的每个子条件都无法使用索引,整个查询可能无法使用索引。

<6> 列使用了NOT操作符:当查询条件中的列使用了NOT操作符时,索引可能无法使用。NOT操作符对索引的匹配条件进行了反转,导致无法有效利用索引。

<7> 数据量过小:当数据表中的数据量非常小(如几行或几十行),MySQL优化器可能认为使用全表扫描比使用索引更快,从而导致索引失效。

举例:
假设有一个表格名为"users",有以下结构和索引:

CREATE TABLE users (
    id INT PRIMARY KEY,  // id 主键;
    name VARCHAR(50),  // 姓名使用字符串类型:
    age INT,         // 年龄整型;
    created_at DATETIME 
);

CREATE INDEX idx_name ON users (name);
CREATE INDEX idx_age ON users (age);

索引失效的情况:
SELECT * FROM users WHERE UPPER(name) = ‘JOHN’; // 查询条件中使用了函数失效;
SELECT * FROM users WHERE age = ‘25’; // 查询条件中使用了类型转换
SELECT * FROM users WHERE name LIKE ‘%abc’; // 查询条件中使用了LIKE操作符:注意,这里是以%开头的失效,如果查询条件是 like ‘15%abc’,这里的索引不失效。 这也就是为什么在微服务开发时,使用Elasticsearch原因之一。
SELECT * FROM users WHERE age = 25 OR name = ‘John’; // 查询条件中使用了OR条件
SELECT * FROM users WHERE NOT age = 25; // 查询条件中使用了NOT操作符

当索引没有遵循最左匹配条件时,可能会导致索引失效。
案例如下:
假设有一个表格名为 “users”,有以下结构和索引:

CREATE TABLE users (
    id INT PRIMARY KEY,
    first_name VARCHAR(50),
    last_name VARCHAR(50),
    age INT,
    created_at DATETIME
);

CREATE INDEX idx_name ON users (first_name, last_name);

索引失效的情况:
查询条件不遵循最左匹配:SELECT * FROM users WHERE last_name = ‘Smith’;
查询条件包含了索引的部分列:SELECT * FROM users WHERE first_name = ‘John’ AND last_name = ‘Smith’;

查询条件中使用了索引列,并且索引列的顺序不符合最左匹配:SELECT * FROM users WHERE last_name = ‘Smith’ AND first_name = ‘John’;
在上述情况中,索引失效是因为查询条件不符合最左匹配原则。最左匹配指的是在联合索引中,如果要使用索引,查询条件必须从索引的最左边开始,并且连续地使用索引的列。当查询条件中的列不符合最左匹配原则时,MySQL无法有效使用索引,可能会导致索引失效,从而影响查询性能。

对于上述示例,如果查询条件中只使用了 last_name 列,而没有使用 first_name 列,或者 first_name 列在查询条件中位于 last_name 列之后,那么索引 idx_name 将无法使用,MySQL可能会选择执行全表扫描,导致查询性能下降。
要避免索引失效,可以确保查询条件遵循最左匹配原则,按照索引定义的顺序使用索引列,并且连续使用索引的列。如果需要针对不同的查询条件进行优化,可以考虑创建多个索引或使用覆盖索引来提高查询性能。

当然,如果不确定实际SQL语句走没走索引,可以使用 explain SQL语句,来查看。
可以通过其中和索引有关的信息来分析是否命中了索引,例如possilbe_key,key,key_len等字段,分别说明了此语句可能会使用的索引,实际使用的索引以及使用的索引长度。

(5) 那么什么情况下应不建或少建索引
表记录太少
如果一个表只有5条记录,采用索引去访问记录的话,那首先需访问索引表,再通过索引表访问数据表,一般索引表与数据表不在同一个数据块,这种情况下ORACLE至少要往返读取数据块两次。而不用索引的情况下ORACLE会将所有的数据一次读出,处理速度显然会比用索引快。
经常插入、删除、修改的表
对一些经常处理的业务表应在查询允许的情况下尽量减少索引。
数据重复且分布平均的表字段
假如一个表有15万行记录,有一个字段A只有T和F两种值,且每个值的分布概率大约为50%,那么对这种表A字段建索引一般不会提高数据库的查询速度。
经常和主字段一块查询但主字段索引值比较多的表字段

在开发中,需要合理使用索引,来提高系统的性能。避免使用索引不当。今天先到这里吧,下期再见。

你可能感兴趣的:(java,数据库,数据库,mysql,jdbc)