文章目录
写数据库
查询成绩/平均成绩
http://blog.sina.com.cn/s/blog_8ea826d10102vm1h.html
https://blog.csdn.net/qq_34226628/article/details/82862041
给定学生和成绩的数据库表,写一个sql语句求出每门课程的平均分。
A.SELECT SUM(score) FROM course GROUP BY student_id;
B.SELECT score FROM course GROUP BY course_id;
C.SELECT AVG(score) FROM course GROUP BY course_id;
D.SELECT AVG(score) FROM course ;
A,求学生每门课程总成绩
B,按照课程吧学生成绩列出来
C,按照不同课程求该课程的平均成绩
D,求所有课程的综合平均成绩
group by 和order by的区别
https://blog.csdn.net/sinat_40692412/article/details/81200133
https://www.cnblogs.com/zhuzhubaoya/p/10429273.html
https://blog.csdn.net/mkmkmkhh/article/details/81808319
group by和count
事务和锁机制是什么关系
事务与锁是不同的。事务具有ACID(
原子性、一致性、隔离性和持久性),锁是用于解决隔离性的一种机制。事务的隔离级别通过锁的机制来实现。另外锁有不同的粒度,同时事务也是有不同的隔离级别的(一般有四种:读未提交Read uncommitted,
读已提交Read committed,
可重复读Repeatable read,
可串行化Serializable)。
在具体的程序设计中,开启事务其实是要数据库支持才行的,如果数据库本身不支持事务,那么仍然无法确保你在程序中使用的事务是有效的。
锁可以分为乐观锁和悲观锁:
悲观锁:认为在修改数据库数据的这段时间里存在着也想修改此数据的事务;
乐观锁:认为在短暂的时间里不会有事务来修改此数据库的数据;
我们一般意义上讲的锁其实是指悲观锁,在数据处理过程中,将数据置于锁定状态(由数据库实现)。
回到你的问题,如果开启了事务,在事务没提交之前,别人是无法修改该数据的;如果rollback,你在本次事务中的修改将撤消(不是别人修改的会没有,因为别人此时无法修改)。当然,前提是你使用的数据库支持事务。还有一个要注意的是,部分数据库支持自定义SQL锁覆盖事务隔离级别默认的锁机制,如果使用了自定义的锁,那就另当别论。
重点:一般事务使用的是悲观锁(具有排他性)。
转
四种隔离七种传播
事务的特性,隔离及传播
mysql重复读,其他读已提交
索引
组合索引索引
为什么要用b+树
索引的数据结构
聚簇非聚集
聚簇索引的索引顺序和表中记录的物理顺序是一致的,而非聚簇索引的索引顺序和记录的物理顺序是不一致的,一个表中只能有一个聚簇索引,通常为设为主键的列,非聚簇索引可以创建多个
数据库优化
数据库优化的思路
这个我借鉴了慕课上关于数据库优化的课程。
1.SQL语句优化
1)应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
2)应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from t where num=0
3)很多时候用 exists 代替 in 是一个好的选择
4)用Where子句替换HAVING 子句 因为HAVING 只会在检索出所有记录之后才对结果集进行过滤
2.索引优化
看上文索引
3.数据库结构优化
1)范式优化: 比如消除冗余(节省空间。。) 2)反范式优化:比如适当加冗余等(减少join) 3)拆分表: 分区将数据在物理上分隔开,不同分区的数据可以制定保存在处于不同磁盘上的数据文件里。这样,当对这个表进行查询时,只需要在表分区中进行扫描,而不必进行全表扫描,明显缩短了查询时间,另外处于不同磁盘的分区也将对这个表的数据传输分散在不同的磁盘I/O,一个精心设置的分区可以将数据传输对磁盘I/O竞争均匀地分散开。对数据量大的时时表可采取此方法。可按月自动建表分区。
4)拆分其实又分垂直拆分和水平拆分: 案例: 简单购物系统暂设涉及如下表: 1.产品表(数据量10w,稳定) 2.订单表(数据量200w,且有增长趋势) 3.用户表 (数据量100w,且有增长趋势) 以mysql为例讲述下水平拆分和垂直拆分,mysql能容忍的数量级在百万静态数据可以到千万 垂直拆分: 解决问题:表与表之间的io竞争 不解决问题:单表中数据量增长出现的压力 方案: 把产品表和用户表放到一个server上 订单表单独放到一个server上 水平拆分: 解决问题:单表中数据量增长出现的压力 不解决问题:表与表之间的io争夺
方案: 用户表通过性别拆分为男用户表和女用户表 订单表通过已完成和完成中拆分为已完成订单和未完成订单 产品表 未完成订单放一个server上 已完成订单表盒男用户表放一个server上 女用户表放一个server上(女的爱购物 哈哈)
4.服务器硬件优化
面试如何回答优化数据库
(1)、根据服务层面:配置mysql性能优化参数;
(2)、从系统层面增强mysql的性能:优化数据表结构、字段类型、字段索引、分表,分库、读写分离等等。
(3)、从数据库层面增强性能:优化SQL语句,合理使用字段索引。
(4)、从代码层面增强性能:使用缓存和NoSQL数据库方式存储,如MongoDB/Memcached/Redis来缓解高并发下数据库查询的压力。
(5)、减少数据库操作次数,尽量使用数据库访问驱动的批处理方法。
(6)、不常使用的数据迁移备份,避免每次都在海量数据中去检索。
(7)、提升数据库服务器硬件配置,或者搭建数据库集群。
(8)、编程手段防止SQL注入:使用JDBC PreparedStatement按位插入或查询;正则表达式过滤(非法字符串过滤)
提高sql语句的性能或者查询效率
要提高SQL查询效率where语句条件的先后次序应如何写
mysql中Mysql模糊查询like效率,以及更高效的写法和sql优化方法
JDBC连接数据库的基本步骤
1.在项目中导入java.sql包
2.加载数据库驱动程序
Class.forName(“com.mysql.cj.jdbc.Driver”);
3.定义数据库的链接地址
String url=“jdbc:mysql://localhost/studentserverTimezone=GMT%2B8&useSSL=false”;
String databasename=“root”;
String pass=“123456”;
4.得到与数据库的连接对象
con = DriverManager.getConnection(url,databasename,pass);
5.声明sql语句
select * from rj1602 where Sno=‘201616040212’;
6.得到语句对象
Statement sql
7.执行sql语句
sql = con.CreateStatement();
sql.executeQuery(“select * from rj1602 where Sno=‘201616040212’”)
8.处理sql语句的返回结果
9.关闭对象
数据库连接池的代码
/**
* 自定义连接池, 管理连接
* 代码实现:
1. MyPool.java 连接池类,
2. 指定全局参数: 初始化数目、最大连接数、当前连接、 连接池集合
3. 构造函数:循环创建3个连接
4. 写一个创建连接的方法
5. 获取连接
------> 判断: 池中有连接, 直接拿
------> 池中没有连接,
------> 判断,是否达到最大连接数; 达到,抛出异常;没有达到最大连接数,
创建新的连接
6. 释放连接
-------> 连接放回集合中(..)
*
*/
public class MyPool {
private int init_count = 3; // 初始化连接数目
private int max_count = 6; // 最大连接数
private int current_count = 0; // 记录当前使用连接数
// 连接池 (存放所有的初始化连接)
private LinkedList<Connection> pool = new LinkedList<Connection>();
//1. 构造函数中,初始化连接放入连接池
public MyPool() {
// 初始化连接
for (int i=0; i<init_count; i++){
// 记录当前连接数目
current_count++;
// 创建原始的连接对象
Connection con = createConnection();
// 把连接加入连接池
pool.addLast(con);
}
}
//2. 创建一个新的连接的方法
private Connection createConnection(){
try {
Class.forName("com.mysql.jdbc.Driver");
// 原始的目标对象
final Connection con = DriverManager.getConnection("jdbc:mysql:///jdbc_demo", "root", "root");
/**********对con对象代理**************/
// 对con创建其代理对象
Connection proxy = (Connection) Proxy.newProxyInstance(
con.getClass().getClassLoader(), // 类加载器
//con.getClass().getInterfaces(), // 当目标对象是一个具体的类的时候,但是这里con是一个接口
new Class[]{Connection.class}, // 目标对象实现的接口
new InvocationHandler() { // 当调用con对象方法的时候, 自动触发事务处理器
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 方法返回值
Object result = null;
// 当前执行的方法的方法名
String methodName = method.getName();
// 判断当执行了close方法的时候,把连接放入连接池
if ("close".equals(methodName)) {
System.out.println("begin:当前执行close方法开始!");
// 连接放入连接池
pool.addLast(con);
System.out.println("end: 当前连接已经放入连接池了!");
} else {
// 调用目标对象方法
result = method.invoke(con, args);
}
return result;
}
}
);
return proxy;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//3. 获取连接
public Connection getConnection(){
// 3.1 判断连接池中是否有连接, 如果有连接,就直接从连接池取出
if (pool.size() > 0){
return pool.removeFirst();
}
// 3.2 连接池中没有连接: 判断,如果没有达到最大连接数,创建;
if (current_count < max_count) {
// 记录当前使用的连接数
current_count++;
// 创建连接
return createConnection();
}
// 3.3 如果当前已经达到最大连接数,抛出异常
throw new RuntimeException("当前连接已经达到最大连接数目 !");
}
//4. 释放连接
public void realeaseConnection(Connection con) {
// 4.1 判断: 池的数目如果小于初始化连接,就放入池中
if (pool.size() < init_count){
pool.addLast(con);
} else {
try {
// 4.2 关闭
current_count--;
con.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) throws SQLException {
MyPool pool = new MyPool();
System.out.println("当前连接: " + pool.current_count); // 3
// 使用连接
pool.getConnection();
pool.getConnection();
Connection con4 = pool.getConnection();
Connection con3 = pool.getConnection();
Connection con2 = pool.getConnection();
Connection con1 = pool.getConnection();
// 释放连接, 连接放回连接池
// pool.realeaseConnection(con1);
/*
* 希望:当关闭连接的时候,要把连接放入连接池!【当调用Connection接口的close方法时候,希望触发pool.addLast(con);操作】
* 把连接放入连接池
* 解决1:实现Connection接口,重写close方法
* 解决2:动态代理
*/
con1.close();
// 再获取
pool.getConnection();
System.out.println("连接池:" + pool.pool.size()); // 0
System.out.println("当前连接: " + pool.current_count); // 3
}
}
等待超时模式获得数据库连接池
端口
JAVA各种框架插件常用端口:redis、MySQL、rabbitmq、elasticsearch、tomcat等等
MongoDB和MySQL的区别
死锁相关
实现一个死锁(Java版),产生死锁的四大必要条件,如何避免死锁,如何解决死锁
statement, preparedStatement,和callableStatement
Statement接口提供了执行语句和获取结果的基本方法;
PreparedStatement接口添加了处理输入参数的方法;
CallableStatement接口添加了调用存储过程核函数以及处理输出参数的方法。
Statement
场景:普通的不带参的查询SQL
PreparedStatement
场景:支持可变参数的SQL
CallableStatement
场景:支持调用存储过程,提供了对输出和输入/输出参数(INOUT)的支持;
SQL的执行需要编译和解析
Statement每次的执行都需要编译SQL
PreparedStatement会预编译,会被缓冲,在缓存区中可以发现预编译的命令,虽然会被再次解析,但不会被再次编译,能够有效提高系统性能
使用PreparedStatement的优点:
1.效率更高
2.代码可读性和维护性更好
3.使用PreparedStatement能够预防SQL注入攻击
假如登录SQL为select * from user where name=‘姓名’ and password=‘密码’ ,如果在登录框密码处输入 “密码 or 1=1”,那么SQL就成为了
select * from user where name=‘姓名’ and password=‘密码’ or 1=1 ,这就是SQL注入
所谓SQL注入就是将SQL语句片段插入到被执行的语句中,把SQL命令插入到Web表单提交或者输入域名或者页面请求的查询字符串,最终达到欺骗服务器,达到执行恶意SQL命令的目的。
PreparedStatement通过预编译,原有的SQL语句中的参数转换为占位符? 的形式,相当于变成了填空题,不管你输入的内容是什么,都是作为参数,而不可能作为SQL的一部分
(要注意 #与$的区别)
你把密码输入为‘密码 or 1=1’然后提交,他会转换为 and password=‘密码’ or 1=1’ 输入内容都转换为纯粹参数
小结:
静态SQL可以用Statement和PreparedStatement,带参数的用PreparedStatement,存储过程用CallableStatement
但是基本上没有道理非要使用Statement,而且很少情况不需要参数,所以能使用PreparedStatement的情况下就不要使用Statement了
Statement、PreparedStatement和CallableStatement三种执行对象,为执行SQL而生,所以他们的重中之重全都是执行SQL