本文是实习过程中数据库 常问问题的总结。
数据库
网站的高并发大流量如何解决:1.HTML页面静态化2.图片服务器与应用服务器相分离3.数据库优化4.缓存5.镜像(副本)6.负载均衡(将并发访问请求分发到多台服务器上做处理)7.并发控制(加锁,乐观悲观)8.消息队列。12306订票系统用数据库层面的加乐观锁
并发包含有的类:ConcurrentHashMap与CopyOnWriteArrayList、阻塞队列(ArrayBlockingQueue)、同步辅助类(CountdownLatch控制某个资源可被同时访问的次数)、线程池相关的类、Lock接口、原子类。 并发编程:高并发:高并发是请求,指的是多个客户端同一时刻向服务端发送请求, 它是一种现象。比如,电商网站在双11凌晨12:00分 同时有2000个下单请求。多线程:多线程是处理,指的是同一时刻多个执行者处理同一类的任务, 它有具体的实现。比如电商网站在双11凌晨12:00分同时有100个线程处理2000个下单请求。
并行:多核cpu的情况, 多个任务执行者并行处理任务。并发:单个cpu的情况下,cpu间断性的执行多个任务。编写代码,有效地利用多个处理器是很好的,每个程序都分为串行与并行部分,降低串行的比重,可提高程序的效率。并发主要是要解决资源争夺,并发一般发生在数据聚合的地方,只要有聚合,就有争夺发生,传统解决争夺的方式采取线程锁机制,这是强行对CPU管理线程人为干预,线程唤醒成本高,新的无锁并发策略来源于Java的NIO或Node.js,通过队列+单线程操作资源的方式巧妙避免了多线程,由于只有一个线程,在多核情况下增加了并行计算的机会。注意问题:共享数据的安全性问题:堆内存和方法区内存可以共享。 因此成员变量和静态变量存在数据安全性问题。锁竟争带来的程序效率问题:多个线程访问共享资源时,只有获取到锁的线程才允许访问共享资源,未获得到锁的线程只能在临界区进行排队等待,试想如果有1000个线程同时访问共享资源,那么最后一个线程必须要等前面999个线程执行完后才能够进入监界区操作共享资源。
为什么使用线程池:1.创建/销毁线程伴随着系统开销,过于频繁的创建/销毁线程,会很大程度上影响处理效率2. 线程并发数量过多,抢占系统资源从而导致阻塞3. 运用线程池对线程管理:延时执行、定时循环执行 Executors的四种线程池:Executor这个接口,包括类:Executor,Executors,ExecutorService,ThreadPoolExecutor ,Callable和Future、FutureTas。具体实现为ThreadPoolExecutor类。①newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。②newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。③newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。④newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
ThreadPoolExecutor 参数:corePoolSize:线程池的核心线程数,线程池中运行的线程数也永远不会超过 corePoolSize 个,默认情况下可以一直存活。可以通过设置allowCoreThreadTimeOut为True,此时 核心线程数就是0,此时keepAliveTime控制所有线程的超时时间。maximumPoolSize:线程池允许的最大线程数;keepAliveTime: 指的是空闲线程结束的超时时间;unit :是一个枚举,表示 keepAliveTime 的单位;workQueue:表示存放任务的BlockingQueue<Runnable队列。BlockingQueue:阻塞队列(BlockingQueue)是java.util.concurrent下的主要用来控制线程同步的工具。 线程池策略:线程数量未达到corePoolSize,则新建一个线程(核心线程)执行任务。线程数量达到了corePools,则将任务移入队列等待 队列已满,新建线程(非核心线程)执行任务。队列已满,总线程数又达到了maximumPoolSize,就会由上面那位星期天RejectedExecutionHandler)抛出异常。
进程与线程:进程作为分配资源的基本单位,具有一定独立功能的程序关于某个数据集合上的一次运行活动。线程作为独立运行和独立调度的基本单位,线程是进程的一个实体,线程共享整个进程的资源(寄存器、堆栈、上下文),一个进行至少包括一个线程。1.进程结束后它拥有的所有线程都将销毁,而线程的结束不会影响同个进程中的其他线程的结束,2.线程是轻两级的进程,它的创建和销毁所需要的时间比进程小很多,所有操作系统中的执行功能都是创建线程去完成的。3.线程中执行时一般都要进行同步和互斥,因为他们共享同一进程的所有资源4.线程有自己的私有属性TCB,线程id,寄存器、硬件上下文,而进程也有自己的私有属性进程控制块PCB,这些私有属性是不被共享的,用来标示一个进程或一个线程的标志。
进程间通讯:1. 消息传递(管道,FIFO,posix和system v消息队列) 2. 同步(互斥锁,条件变量,读写锁,文件和记录锁,Posix和System V信号灯) 3. 共享内存区(匿名共享内存区,有名Posix共享内存区,有名System V共享内存区) 4. 过程调用(Solaris门,Sun RPC)
线程间通讯:1. 使用全局变量(volatile修饰的成员变量在每次被线程访问时,都强迫从主内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到主内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。)2. 使用消息队列实现通信3. 使用事件Event实现线程间通信
线程间同步:1.临界区2.互斥量3. 信号量4. 事件【多线程同步,线程间共享问题Java中1Synchronized中的内容不能同时被多个线程访问2.Lock(ReentrantLock类是可重入、互斥、实现了Lock接口的锁)3.wait()/notify()/notifyAll()4.CAS(非阻塞算法,乐观锁)5. 使用ThreadLocal线程本地变量:管理变量,则每一个使用该变量的线程都获得该变量的副本, 在线程内部任何地方都可以使用,线程之间互不影响,这样一来就不存在线程安全问题,也不会严重影响程序执行性能,常用场景为:数据库连接、Session管理】
在Java中实现线程:继承java.lang.Thread 类或者直接调用Runnable接口来重写run()方法实现线程,也可以线程池创建线程(创建Executor执行器)、Callable和future接口对象,实现call方法,有返回值,可抛出异常,futureTask对象封装Callable对象的call方法的返回值。Java不支持类的多重继承,但允许你调用多个接口。所以如果你要继承其他类,当然是调用Runnable接口。Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果等操作。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。 一个类可以同时继承Thread类并实现Runnable接口。
Thread 类中的start() 和 run() 方法:start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的 start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。run方法只是thread的一个普通方法调用,还是在主线程里执行。
Runnable和Callable都代表那些要在不同的线程中执行的任务。Runnable从JDK1.0开始就有了,Callable是在JDK1.5增加的。它们的主要区别是Callable的 call() 方法可以返回值和抛出异常,而Runnable的run()方法没有这些功能。Callable可以返回装载有计算结果的Future对象。 线程安全:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
交集(inner join):Select A.* from A inner join B using(name,addr,age);差集(left join\right join);并集(union) 内连接:使用比较运算符根据每个表共有的列的值匹配两个表中的行;LEFT JOIN:结果集包括 LEFT OUTER子句中指定的左表的所有行,从右表中将符合ON条件的结果查询出来,合并到左表中,再作为一个结果集输出;RIGHT JOIN:将返回右表的所有行。如果右表的某行在左表中没有匹配行,则将为左表返回空值。交叉联接(笛卡儿积):表示左表中的每一行与右表中的每一行的组合。
Truncate\drop\delete区别:drop:删除内容定义,释放空间;truncate:删除内容,释放空间,不删除定义,一次性删除全部数据,日志志记录页释放、不能回滚、执行速度快、不可恢复数据;delete:删除内容、不删除定义、不释放空间,逐条删,
Sql语句:Grant 权限 on 数据库表 to 用户;revoke 权限 on 数据库表 from 用户;insert into 表 values();alter table 表名 add/drop 字段名;count(*) 和count(1)返回记录总行数,count(字段名)返回不为null的记录行数;
存储过程:编译好的sql语句,实现某些功能,可根据过程名调用。优点:1.预编译,速度快2.服务器段运行,减少客户端压力3.模块化程序设计,一次创建多次调用4.减少网络流量5.增强使用安全性 缺点:调试麻烦、可移植性差
数据库优化:【Mysql】1. 优化表的数据类型select * from 表procedure analyse()选取最适用的字段属性(字段宽度设置小、not null、省份性别设为enum)2.使用事物查询保证安全性5.锁定表避免事物的独占性6使用外键,保证数据的关联性7.使用索引,建立使用JOIN,WHERE,ORDERBY排序的字段上。不要对含有大量重复的值的字段建立索引。8优化的查询语句(a. 在相同类型的字段间进行比较b. 建索引的字段上不要使用函数操作c.不要使用like关键词通配符d.通过explain查看执行效果、帮助选择更好的索引和优化查询语句e.使用limit减少放回行数,减少数据传输时间f.使用连接(JOIN)来代替子查询g.使用联合(UNION)来代替手动创建的临时表)9.对表进行拆分:垂直拆分(主要按功能分,主键与一些列、主键与另一些列),水平拆分(根据一列或多列数据值将数据行独立的表中)10.使用中间表提高查询速度10硬件优化 11.MySql自身优化(配置文件指定查询缓冲区的大小,最大连接进程数)11.应用优化(使用数据库连接池、对更新不频繁的表使用查询缓存)
有特别大的访问量到数据库时,如何做优化:1.优化查询2.主从复制、读写分离、负载均衡:写操作发向master,读操作发向slave.(包括同步复制、异步半同步复制)3. 数据库分表、分区、分库:分表(垂直水平);分区:把一张表的数据分成多个区块,在逻辑上看最终只是一张表,但底层是由N个物理区块组成的;分库:根据业务不同将相关的表分入不同的数据库中,如web、bbs
MySQL主从复制原理:1.master将数据变更记录到二进制日志中,2.slave将二进制日志拷贝到中继日志中3.slave重做中继日志中的事件,将master上的改变反应到自己的数据库
MySql锁的粒度(级别):1.表级锁(MyISAM):直接锁定整张表,开销小,加锁块,粒度最大,冲突概率高,并发度最低,两种模式(表共享读锁、表独占写锁)、写锁优先2.行级锁(InnoDB):对指定记录加锁,开销大,加锁慢,会出现死锁、发生锁冲突的概率最低、并发度最高3.页级锁(一次锁定相邻的一组记录)数据页:介于两者之间
锁有两种机制:乐观锁(Optimistic Lock):就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读,发生冲突的概率低的应用类型,可以提高吞吐量。悲观锁(Pessimistic Lock):就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。悲观锁适用于可靠的持续性连接,诸如C/S应用(行锁、表锁、读锁、写锁)。并发性不好
数据库索引:1. 普通索引:(最经常出现的查询条件或排序条件中的列就该创建索引)2. 唯一索引(数据列将只包含彼此各不相同的值unique)3. 主键索引(唯一性索引,但是不能为空,primary key)4. 组合索引(使用到表中的多个数据列,查询条件应包含索引的首列字段,B+树多列索引保存的顺序按照索引创建的顺序,检索时也按照次顺序,范围查询会导致组合索引半失效)
何字段上建索引:主键及外键通常都要有索引①字段出现在查询条件中,并且查询条件可以使用索引②执行频率高。不适合建索引的字段:描述字段、大字段
索引:用于提高数据访问速度的数据库对象。查找问题,是数据库管理系统中一个排序的数据结构,使用B树及其变种B+树。 优点:1)索引可以避免全表扫描;2)对于非聚集索引,有些查询甚至可以不访问数据项;3)聚集索引可以避免数据插入操作集中于表的最后一个数据页;4)一些情况下,索引还可以避免排序。缺点:1)导致数据库更新数据的性能下降,因为大部分数据更新时需要同时更新索引。2)占用额外的磁盘空间
聚集索引:数据按索引顺序存储,叶子节点存储真实的数据行,不再有另外单独的数据页。
非聚集索引:1)叶子节点并非数据节点2)为每一个真正的数据行存储一个“键-指针”对 3)还存储了一个指针偏移量,根据页指针及指针偏移可以定位到具体的数据行。4)在除叶节点外的其他索引节点,存储的是类似内容,只不过是指向下一级索引页。
B树、B+树、B*树:
- B树就是B-树,是一种多路索引树(并不是二叉的)1)任意非叶子节点最多只有m个儿子,且m>2 。2)根节点的儿子数最多为[2,m]。3)除了跟节点以外的非叶子节点的儿子数为[m/2,m]。4)每个节点至少存放m/2-1和最多m-1个关键字。5)非叶子节点的关键字个数=儿子数-1。6)非叶子节点的关键字,k[1], k[2] ,k[3]…. k[m-1],且k[i]< k[i+1].7)非叶子节点的指针p[1], p[2], p[3]…..p[m],其中p[i]指向关键字< k[i]的子树,p[m]指向关键字> p[m-1]的子树,其他p[i]指向关键字属于(k[i-1],k[i])的子树。8)所有叶子节点位于同一层。B-树可能在非叶子节点中命中.
- B+树:适合文件系统索引,1)其基本定义和B-树相同。2)非叶子节点的儿子数=关键字个数3)非叶子节点的子树指针p[i]指向关键字属于[k[i], k[i+1])的子树(B-树为开区间)。4)all叶子节点增加一个键指针。5)all关键字都在叶子节点出现,且叶子节点本身依赖关键字的大小顺序排序。B+树只有到达叶子节点才命中。(B+树最低使用率为1/2)
- B* 树是B+树的变体,B树中非跟和非叶子节点增加指向兄弟的指针。B树中非叶子节点关键字个数>=2m/3,即块的最低使用率为2/3 B+树比b-树根适合文件索引和数据库索引。B-树:有序数组+平衡多叉树。B+树:有序数组链表+平衡多叉树。B*树:一个丰满的B+树。
NoSQL 数据库分类:列存储(hbase)、文档存储(mangoDB,类json)、key-value存储(redis)\图存储、对象存储、xml数据库 关系型数据库与非关系型数据库的区别:非优势:1.性能高,基于键值对;2.可扩展,数据耦合小。关优势:1.支持复杂查询2.事物支持 在线事务处理系统(OLTP)和在线分析系统(OLAP):OLTP是传统的关系型数据库的主要应用,主要是基本的、日常的事务处理,例如银行交易。OLAP是数据仓库系统的主要应用,支持复杂的分析操作,侧重决策支持,并且提供直观易懂的查询结果。
ETL:是数据抽取(Extract)、转换(Transform)、加载(Load ):将OLTP系统中的数据抽取出来,并将不同数据源的数据进行转换和整合,得出一致性的数据,然后加载到数据仓库中。
三范式:第一范式:关系模式中,每个属性不可再分。属性原子性 第二范式:非主属性完全依赖于主属性,即消除非主属性对主属性的部分函数依赖关系。 第三范式:非主属性对主属性不存在传递函数依赖关系。 BNCF范式:在第三范式的基础上,消除主属性之间的部分函数依赖
并发事物带来的问题:更新丢失、脏读:所谓脏读就是对脏数据的读取,而脏数据所指的就是未提交的数据。2)不可重复读:一个事务先后读取同一条记录,但两次读取的数据不同(修改,两次值不同)3)幻读:一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据(新增或删除,两次记录数不同)
ACID: 原子性是指事务是一个不可再分割的工作单位,事务中的操作要么都发生,要么都不发生. 一致性是指在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏. 隔离性是个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果。持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。
事物的四个隔离级别:(事物是一组sql语句组成的逻辑处理单元)事务的4种隔离级别:未提交读(一个事务可以读取另一个未提交事务的数据,隔离级别最低)、提交读(一个事务要等另一个事务提交后才能读取数据,解决了脏读,但未解决不可重复读) 、可重复读(开始读取数据(事务开启)时,不再允许修改操作,MySQL默认隔离级别) 、串行读(事务串行化顺序执行,读取使用表级共享锁,读写相互都会阻塞。隔离级别最高。 共享锁(S)排他锁(X)
饥饿与死锁:饥饿是指系统不能保证某个进程的等待时间上界,从而使该进程长时间等待,当等待时间给进程推进和响应带来明显影响时,称发生了进程饥饿。当饥饿到一定程度的进程所赋予的任务即使完成也不再具有实际意义时称该进程被饿死。死锁是指在多道程序系统中,一组进程中的每一个进程都无限期等待被该组进程中的另一个进程所占有且永远不会释放的资源。由于竞争资源。不同点:从进程状态考虑,死锁进程都处于等待状态,忙等待(处于运行或就绪状态)的进程并非处于等待状态,但却可能被饿死;死锁进程等待永远不会被释放的资源,饿死进程等待会被释放但却不会分配给自己的资源,表现为等待时限没有上界(排队等待或忙式等待);死锁一定发生了循环等待,而饿死则不然。这也表明通过资源分配图可以检测死锁存在与否,但却不能检测是否有进程饿死;
条件:互斥条件:一个资源每次只能被一个进程使用。请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。