MySQL的索引事务&&JDBC编程

目录

1.索引

2.事务

3.JDBC编程


1.索引

1.1 何为索引?

索引其实就对应生活中的目录,目录!存在的意义就是加快查找的速度。

索引本质上是通过一些更复杂的数据结构,把所有待查询的记录给组织起来了,从而就能够加快查询的速度!!

1.2 索引的应用场景

美中不足:

1.消耗额外的空间!!(目录总得占用书的纸张吧)

2.虽然有了索引,加快了查询速度,但是拖慢了增删改的速度!!(增删改,我们书的目录是不是都得变化,非常麻烦)

适合的场景:

1.对于空间不紧张,对于时间更敏感(空间换时间)

2.查询频繁,增删改不频繁 -- 少写多读的场景

(其实大多数情况都是符合第二点的,例如学校发电子版的作业给学生做,大部分都是往年师兄师姐做过的,除非题目有问题,跟老师反应,才会涉及到增删改)

1.3 索引的一些操作

  • 查看索引
show index from 表名;
show index from student;
  • 创建索引
create index 索引名 on 表名(列名);
create index id_index on student(id);
  • 删除索引
drop index 索引名 on 表名;
drop index id_index on student;

注意:

1.当我们没有给表手动创建索引时,表的主键,unique,以及外键约束的列,都会自动带上一个索引。

2.像主键这种,为了保证记录不重复,每次插入新纪录,都需要查询一下旧纪录,看新纪录是否已经存在!判定是否重复!

3.创建索引和删除索引和增删查改一样,也是危险操作!!尤其时是针对一个已经包含很大数据量的表进行操作的时候,创建索引,就会导致大规模磁盘IO,直接把主机的磁盘IO吃满!!,主机可能就卡了,无法对线上服务进行响应......

1.4 索引的数据结构

我们回顾前面学过的数据结构,用来查找的数据结构最可观的只有二叉搜索树O(N)和哈希表O(1)

二叉搜索树:高度可能比较高,树高度每增加一层,就要多出磁盘IO操作!!显然不适合索引。

哈希表:查找是快,但是不能支持范围查找和模糊匹配,固然也不适合索引。

既然二叉搜索树和哈希表都不适合索引的数据结构,聪明的程序猿就发明了 N 叉搜索树,就是 B树,它和二叉搜索树类似,只不过就是结点的度多了。

   像这样的树,整体的高度,就大大缩短了!!l(logn^N < log2^N) 

虽然这个 B树相比于二叉搜索树,哈希表是好多了,但是在实现数据库的时候,一般也不会直接使用 B树,而是使用 B树的改进版本,B+树!

B+树可以说是为了实现数据库索引,量身定做的数据结构!

特点:

  • 查询速度快,类似于二叉搜索树!
  • 单个结点存储更多的数据,树的高度比较低,查询的时候,比较的次数就相对较少! 
  • 叶子节点包含了整个数据库的数据全集!
  • 所有的叶子节点,使用链表首尾相连,非常便于范围查找!(例如我要找 id > 9 的,搜索树的方式定位到9,然后遍历 9 之后的所有数据)
  • 每一个数据行,只需要保存在叶子节点上就够了,非叶子结点不必存储真实的数据行,只要存储用来做索引的 id 即可!!此时非叶子结点占用的空间小,就有了在内存中缓存非叶子结点的可能性,大大降低了磁盘IO!!

【面试问题】

谈谈对于数据库索引的理解?(高频问题)

【三个方面】

1.理解数据库的索引为啥使用B+树,而不是二叉搜索树 / 哈希表

2.理解 B+ 树的结构特点

3.理解 B+ 树的优势


2.事务

2.1 何为事务?

事务就是把一件事情的多个步骤,多个操作,打包成一个步骤,一个操作。其中任意一个步骤执行失败,都会进行回退,使影响降到最低!

2.2 事务的特性 

2.2.1.原子性(最核心的特性)

一切事务要么全部执行完,要么一个都不执行。此处的"一个都不执行"并不是真的不执行,而是通过恢复的方式,,把之前操作造成的影响给还原了,我们把这个过程叫做"回滚"(rollback)。例如我们生活中转账,张三给李四转了500,在程序执行的过程出现了问题,导致张三的钱包已经少了500,而李四的钱包并没有增加500,这就会造成纠纷,所以需要回滚来解决这种情况!

2.2.2.一致性

数据一定是对的,没有纰漏,不能出现张三给李四转账500,张三 -500 ,李四 +300这样的情况。

2.2.3.持久性

事务进行的操作都会存储在磁盘上,只要事务执行成功,就是持久化的保存了,哪怕重启主机这样的改变,也任然存在!

2.2.4.隔离性(重点)

描述多个事务并发执行的时候,所产生的影响。

【什么是并发执行?】

并发,简单粗暴理解成,两个事务在同时执行!就像一个数据库可以给多个客户端提供服务,这个时候就可能会涉及到多个客户端同时尝试操作同一个表,就可能产生这种并发事务的情况。

为啥要并发执行?目的是为了提高执行效率!!

为啥要有隔离性?使事务之间互不干扰,数据更精准了,但是效率低了!!

为了解决并发执行事务带来的问题,MySQL等数据库引入了"隔离级别",可以让用户自行选择一个适合自己当前业务场景的级别!!

先研究一下,并发执行事务的时候会出现哪些问题??

【脏读问题】

一个事务A,在执行的过程中,对数据进行了一系列修改,在提交到数据库之前(完成事务之前),另一个事务B,读取了对应的数据,此时这个B读到的数据都是一些临时的结果,后续可能马上就被A给改了,此时B的读取行为就是"脏读"!

如何解决??

给读操作加锁,等到A事务提交事务后再进行读操作。

-- 隔离性最低,并发能力最强

【不可重复度】

事务A提交了事务之后,事务B才开始读(读的时候加了锁),然后B在执行的过程中,A又开启了一次,修改了数据,此时B执行中,就导致两次读取操作结果可能就不一致!

如何解决??

在A提交事务之前,给B的读加锁。A提交事务之后,B在读的时候,规定A不能修改B正在读的数据。

-- 隔离性提高了,并发性降低了

幻读

事务B读取过程中,事务A进行了修改,没有直接修改B读取的数据(修改了其他数据),但是影响到了B读取的结果集,事务B两次读取到的结果集不一样,这个就是幻读!幻读相当于不可重复的特殊情况。

如何解决??

B事务在读的时候,严格规定A不能修改,保证读和写操作都是严格串行化执行的。(一个执行完,才能执行另一个)

-- 隔离性最高,并发程度最低;数据的准确性最好,同时速度最慢!

对于这四种情况,MySQL里给我们提供了四个隔离级别,供我们自由选择,我们可以根据实际需求,在配置文件中,修改数据库的隔离级别。

【重点】

1.read uncommitted    并发能力最强,隔离性最弱。

2.read committed    只能读取提交之后的数据,解决了脏读问题,并发能力下降一些,隔离性增加了一些。

3.repeatable read   针对读和写都进行了限制,解决了不可重复读问题。并发能力又进一步下降,隔离性进一步增加。

4.serializable   严格的串行执行,解决了幻读问题,并发能力最低,隔离性最高。


3.JDBC编程

在idea上编写代码时,我们要做一些准备工作:

1.打开MySQL服务;2.建立一个 directory 目录,然后将我们的连接器导入,并add一下;


 先演示一下插入操作:

Insert--插入

    public static void main(String[] args) throws SQLException {
        // 1.使用 DataSource 描述 MySQL 服务器的位置
        DataSource dataSource = new MysqlDataSource();
        ((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/xyz?characterEncoding=utf8&useSSL=false");
        //Url--网址   jdbc:mysql://协议名 ,127.0.0.1 ip地址 ,3306 端口号 ,xyz 数据库,useSSL=false 是否启用加密
        // characterEncoding=utf8 设置客户端连接服务器使用的字符集
        ((MysqlDataSource)dataSource).setUser("root");
        ((MysqlDataSource)dataSource).setPassword("123456");

        //2.和数据库服务器建立连接
        Connection connection = dataSource.getConnection();

        //输入
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入你要插入信息的id: ");
        int id = scanner.nextInt();
        System.out.println("请输入你要插入信息的name: ");
        String name = scanner.next();
        String sql = "insert into student values(?,?)";

        // 3.构造 SQL 语句,JDBC 操作数据,本质仍然是通过 SQL 来描述数据库操作
        PreparedStatement statement = connection.prepareStatement(sql);
        statement.setInt(1,id);
        statement.setString(2,name);

        // 4.执行SQL语句,insert,update,delete 都是通过 executeUpdate() 来执行的
        int count = statement.executeUpdate();
        System.out.println("成功向student表中插入了"+count+"条记录!");
        //关闭,释放资源(先打开,后关闭;后打开,先关闭)
        statement.close();
        connection.close();
    }

【注意事项】 

1.我们注意到第一步使用 DataSource 描述 MySQL 服务器位置的时候,先向下转型,再向下转型:

((MysqlDataSource)dataSource).setUrl(),
为啥前面要写成 DataSource dataSource = new MysqlDataSource(); 
为什么不直接实例化MysqlDataSource的对象呢??

虽然两种写法没啥区别,但实际开发中,可能还是更多的看到前面这种写法。前面这种写法里,得到的数据源是 DataSource 类型,后续写其他代码,方法/类,如果使用到数据源,持有的类型也是 DataSource 类型。

DataSource 是通用的类型,可以指代任何数据库。未来一旦需要更换数据库,代码改动非常小的,只需要将实例化这一小块代码更改即可,其他代码都不需要变。而后面这种写法就需要将所有地方出现的 MysqlDataSource 类型的都更改。所以第一种写法,代码的可维护性,可扩展性就相比于第二种好很多!!

2.dataSource.getConnection() 和 DriverManager.getConnection() 有啥区别??

DriverManager 每次 getConnection() 都会重新和数据库服务器建立连接。

而 DataSource 则可能会复用之前的连接,就不必多次建立连接,毕竟建立连接的过程还挺麻烦,比较低效。(DataSource 内置了数据库连接池) 

再演示一个查找操作,插入,删除,修改执行后返回值都是一个整数,所以代码差别不大,只需要将sql 语句做简单修改即可。

select--查找

    public static void main(String[] args) throws SQLException {
        DataSource dataSource = new MysqlDataSource();
        // 1.使用 DataSource 描述 MySQL 服务器的位置
        ((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/xyz?characterEncoding=utf8&useSSL=false");
        ((MysqlDataSource)dataSource).setUser("root");
        ((MysqlDataSource)dataSource).setPassword("123456");

        //2.和数据库服务器建立连接
        Connection connection = dataSource.getConnection();

        String sql = "select * from student";
        PreparedStatement statement = connection.prepareStatement(sql);
        //查询操作,返回的是一个结果集,类似于我们的迭代器,resultSet指针最初指向第一行数据的前头,所以需要先调用 next() 往后走!
        ResultSet resultSet = statement.executeQuery();
        while(resultSet.next()) {
            //根据列标签获取当前列的值
            int id = resultSet.getInt("id");
            String name = resultSet.getString("name");
            System.out.println(id+"\t"+name);
        }
        resultSet.close();
        statement.close();
        connection.close();
    }

以上操作中重要的知识点还有:1.Statement 对象,2. SQL的两种执行方法。

 SQL的两种执行方法:

  • executeQuery() 方法执行后返回单个结果集的(类似于迭代器),通常用于 select 语句。
  • executeUpdate() 方法返回值是一个整数,指示受影响的行数,通常用于  update insert delete 语句。
    本篇博客就到这里了,谢谢观看!!
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Master_hl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值