java面试

Redis

简介*

Redis是一个开源的、高效的、分布式Key-Value数据库。Redis目前支持存储5种数据类型,**字符串(String)、字典/哈希表(Hash)、有序列表(List)、无序列表(Set)、有序Set(zSet)**。并且拥有**内存回收策略和数据持久策略**来保证Redis的高效和安全,我们在项目中主要使用Redis作为缓存,存储一些频繁访问但是又不经常改变的数据比如说字典数据、权限数据、城市数据等。

常用数据类型
字符串(String):简单的key-value存储,string类型是二进制安全的。意思是redis的string可以包含任何数据,比如jpg图片或者序列化的对象,一个键最大能存储521MB.
有序列表(list):有序可重复、有序的数据(按照插入顺序排序),基于双向链表的数据类型,可以分别在list两端进行操作最大存储2^32-1个元素。
无序列表(set):不允许重复,不能通过下边获取元素。基于hash表实现,可以获取集合中的交集,并集,差集,最大存储2^32-1个元素。
字典/哈希表(hash):氏一族key-value结构,特别适合存储对象。当hash表中k-v较少时采用紧凑型(zipmap)数据结构,当k-v较多时变为真正的hashmap(ht).一个hash中可以存储2^32-1键值对。

有序set(zSet):相当于set的升级,保留了set的无序特性,按照指定字段值进行排序,最大存储2^32-1个元素。
注:注:默认16个库
各类数据类型使用场景

Redis是单线程还是多线程?

1,无论什么版本,工作线程就是一个
2,6.x高版本出现了IO多线程
3,使用上来说,没有变化
------
3,[去学一下系统IO课],你要真正的理解面向IO模型编程的时候,有内核的事,从内核把数据搬运到程序里这是第一步,
然后,搬运回来的数据做的计算式第二步,netty
4,单线程,满足redis的串行原子,只不过IO多线程后,把输入/输出放到更多的线程里去并行,好处如下:1,执行时间缩短,更快;2,更好的压榨系统及硬件的资源(网卡能够高效的使用);
*,客户端被读取的顺序不能被保障
那个顺序时可以被保障的:在一个连接里,socket里

Redis是否存在线程安全的问题?

重复2中的单线程串行
redis可以保障内部串行
Redis 无论什么版本,工作线程只有一个。6.x 以上版本 采用了IO多线程
外界使用的时候要保障,业务上要自行保障顺序~!

常见问题:Redis穿透 击穿 雪崩

Redis穿透
一般情况在查询缓存时,缓存没有,这时代码逻辑一般就是直接去 DB查询。
如果查询的数据在缓存中一定不存在的,并且对该数据并发请求量很大,就会对DB造成很大的压力。这就叫做缓存穿透。

解决办法:
1:数据预热: 简单粗暴的是我们会把所有DB需要缓存的数据全部放进去,当缓存中的数据不存在,直接返回,不去DB查询

2: 布隆过滤器 对一定不存在的key进行过滤可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该bitmap过滤。【用的不多】

Redis击穿

是指在使用缓存查询时,查询一个非常热门的数据,当这个数据失效的瞬间,同时有大量的请求进来,导致缓存被击穿,请求直接落到数据库上,形成数据库的压力。

解决:
a. 加互斥锁,当缓存失效后,只允许一个线程访问数据库,其他线程等待
b. 设置热点数据不过期

Redis雪崩
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。
缓存设计不合理或者配置不当引起的

解决办法:
1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
2:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
3. 数据分布:将数据均匀分布到多台服务器上,避免某一台服务器宕机导致大量请求落到其他服务器上。

Redis的过期Key是如何被Redis删除掉的(缓存如何回收的?)

我了解到的Redis删除过期Key是通过2中方式进行删除的,主动删除和惰性删除的方式,
主动删除就是Redis会定时检查具有过期时间的Key是否过期,过期进行删除。
惰性删除就是当进行get具有过期的Key是会检查一下该Key是否过期,过期删除并返回null,没过期就返回对应数据。

缓存是如何淘汰的

0.内存空间不足情况下
1.淘汰机制里有不允许淘汰
3.lru /lfu/random/ttl
在这里插入图片描述
4.全空间
5.设置过期key的集合中

如何进行缓存预热

  1,提前把数据塞入redis,(你知道那些是热数据吗?肯定不知道,会造成上线很多数据没有缓存命中)
  2,开发逻辑上也要规避差集(你没缓存的),会造成击穿,穿透,雪崩,实施456中的锁方案
  3,一劳永逸,未来也不怕了
  *,结合穿透  击穿   雪崩  去看,看图理解


## 11. 数据库与缓存不一致如何解决?
    在单机并发小的情况下,可以直接访问数据库,单机mysql 每秒1万左右查询请求,3000左右的新增更改请求。但是高并发场景下,将会对数据库造成压力

  1,恶心点的,我们可以使用**分布式事务来解决**,(意义不大),顶多读多,写稀有情况下
  结合图去思考
  1,redis是缓存,更倾向于稍微的有时差
  
  2,还是减少DB的操作
      搭建高可用的分布式缓存,采用redis sentinel哨兵模式  + redis  cluter集群模式
      先写数据库,再写缓存,如果缓存中没有,读写数据库后,将数据存入缓存中

  3,真的要落地,咱就canal吧

## Redis有哪些持久化方式?
  1,RDB,AOF;主从同步也算持久化;
  2,高版本:开启AOF,**AOF是可以通过执行日志得到全部内存数据的方式**,但是追求性能:
  2.1,体积变大,重复无效指令  重写,后台用线程把内存的kv生成指令写个新的aof
  2.2,4.x 新增更有性能模式:把重写方式换成直接RDB放到aof文件的头部,比2.1的方法快了,再追加日志
  




Redis集群相关(T)
分片
简单说就是将数据根据一定算法(有唯一且规则的算法[hash])存储到不同位置。
集群策略
据我所知目前Redis的集群策略比较流行的是主从复制+sentinel哨兵、redis cluster(redis官方提供的基于ruby的集群方案)、代理分片、codeis(豌豆荚的集群解决方案)
  你们用的那种?
  	目前我做到xxx项目只使用到了主从复制(一主两从+3哨兵),主只做写的操作,从做读的操作,因为该项目还属于业务的前期用户量和数据类还达不到Redis单实例的瓶颈,没必要去搭集群。
  为啥是3哨兵?
  	1个哨兵容易出现失误(由于网络的原因导致哨兵认为主挂掉了),
2个哨兵容易在相互投票选举时出现脑死情况导致无法切换主。
3个哨兵投票数为2的可以避免失误和哨兵在选举时的问题。
你们Redis存了什么数据(T)?
  (字典数据、权限数据、城市数据、session等)
Redis内存回收机制你了解过吗?/你怎么理解的(T)。
  Redis的内存回收会主动删除过期的key或不常用的key,通过设置maxmemory的大小来开启key的删除,通过指定maxmemory-policy的策略来指定Redis删除key的方式。
  1、noeviction:当内存使用达到阈值的时候,所有引起申请内存的命令会报错。
  2、allkeys-lru:在主键空间中,优先移除最近未使用的key。
  3、volatile-lru:在设置了过期时间的键空间中,优先移除最近未使用的key。
  4、allkeys-random:在主键空间中,随机移除某个key。
  5、volatile-random:在设置了过期时间的键空间中,随机移除某个key。
  6、volatile-ttl:在设置了过期时间的键空间中,具有更早过期时间的key优先移除。
  (删除的是部分满足算法选中key,而不是满足算法的全部key)
  怎么选择?
  根据系统的实际要求进行选择,防止过期key的脏读概率使用volatile-ttl,为了给更多频繁使用的key提高更多的空间可以使用volatile-lru
  lru算法你有了解过吗?(T)
  这个lru算法我只有一点简单的了解,LRU就算最近最少使用的数据,redis主要用它来进行获取要删除的key。
Redis持久策略相关
  Redis的持久化策略分为两种AOF和RDB,它俩的区别是存储数据的方式不一样,RDB是定时将全部内存中的数据进行存储。AOF是以日志Append(追加)的方式进行部分数据的持久化。AOF和RDB各有利弊,AOF的主要优势在于损失部分性能来保证数据的安全以及更高的一致性,相比AOF,RDB的优势在于其的高性能。
为什么选用Redis(F)。
  高效、数据类型丰富可以应对不同的业务场景,有良好的持久策略和内存回收策略。并且Redis可以集群来对redis服务质量的提高。
Redis为什么高效?(F)
  因为Redis的所有操作都是基于内存的所以相比硬盘,省去了频繁的IO操作,所以高效。
你们项目中用什么连接Redis/你们是怎么使用Redis的?(F)
  Redis的java连接常用的是jedis,我们在项目中使用的是Spring-data-redis,用的是Jedis的连接池。
你对Redis的看法(F)
  我觉得Redis目前还不太适合直接代替关系型数据库进行数据的持久化,首先是应为它是基于内存的带给我们高效的同时必然不能相硬盘一样存储海量数据而且Redis的设计之初也不是为了代替关系型数据库。虽然说现在Redis的集群分片+主从复制可以进行类数据库的持久,但是Redis始终存在会丢失数据可能,存在一定的风险,对于公司和用户来说数据的丢失是不可取的行为。

Redis与Memcached
我们在项目中使用的是redis, Memcached没用过但简单了解过,redis和memcached都是key-value存储的,但redis还能存储list,set,zset,hash等数据结构的数据,简单的就了解这么多

## RabbitMQ

我们使用MQ主要把某些非必须串行处理的业务改为了异步通知处理,能够及时的响应用户,这样就能节省服务器的请求响应时间,提高系统的吞吐量。选用RabbitMQ我们也是经过比较的从综合(性能、并发、是否支持集群、消息是否可以持久、上手难易程度等方面)考虑RabbitMQ都是不错的,RabbitMQ配置相对简单,而且管理界面也比较清晰,也可以和Spring无缝结合。我在XX项目中使用MQ就是将发送短信验证码做了异步通知,当时我们项目刚上线用户注册的量比较大,但是第三方短信服务和我们的服务器之间的网络通信有阻碍,导致调用短信接口的时候特别慢,这时候我们就把发送短信加入到了MQ中,用户点击发送短信将用户的手机号包装成消息,加入到队列中,消费者接受到消息后生成验证码,然后进行第三方服务的调用,发送成功后,把验证码存入Redis设置好超时时间。当用户收到短信后在进行有效性的验证。

队列是先进先出

RabbitMQ的运行原理:生产者产生消息(Message)后发送到RabbitMQ,MQ接受到消息后会把消息放到指定的路由(Exchange)中,路由会根据路由的转发规则和routingkey(点对点[单播]、广播、主题[模糊匹配])找到绑定(binding)的队列。消费者监听到队列中有消息后进行消费。

交换机、队列、消息的持久化设置durabl=true,持久化主要是为了防止MQ宕机后造成数据丢失的问题。

路由转发规则
  1、Fanout 广播,不需要处理RouteKey 。只需要简单的将队列绑定到exchange 上。这样发送到exchange的消息都会被转发到与该交换机绑定的所有队列上。类似子网广播,每台子网内的主机都获得了一份复制的消息。所以,Fanout Exchange 转发消息是最快的。
  2、Direct 点对点/单播,消息被转发到RouteKey中指定的Queue。Direct模式,可以使用rabbitMQ自带的Exchange:default Exchange 。所以不需要将Exchange进行任何绑定(binding)操作。消息传递时,RouteKey必须完全匹配,才会被队列接收,否则该消息会被抛弃。
  3、Topic 模糊匹配所有发送到Topic Exchange的消息被转发到所有关心RouteKey中指定Topic的Queue上,Exchange 将RouteKey 和某Topic 进行模糊匹配。此时队列需要绑定一个Topic。可以使用通配符进行模糊匹配,符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词。因此“log.#”能够匹配到“log.info.oa”,但是“log.*” 只会匹配到“log.error”。所以,Topic Exchange 使用非常灵活。

MQ还可以在哪些业务场景使用:
  例如订单系统和库存系统,没有使用队列,用户下单成功后,需要等待库存系统的响应耗费时间。如果使用队列改为异步通知后,可以减少用户的等待时间。并且库存系统出现异常将订单状态改为出库失败即可。
  出库失败,短信通知用户,并通知管理员。

了解过但是还没用到:
问题:消息队列怎么防止消息阻塞?
分析:
  1、什么是消息阻塞?当消费者处理某条消息时应为业务处理失败了没有返回ack。消息会被mq不断重复发生,每次发送该消息都无法处理,导致其他消息无法及时处理,队列堆积越来越长就出现了消息阻塞。
  2、怎么解决?利用mq的死信队列,当发生消息处理失败的情况下,把该消息加入到死信队列中,不影响后面的消息处理,进入死信队列中的消息在进行其它处理。
  3、怎么配置,配置一个死信路由和死信队列(其实很正常的是一样,只是用于存放处理失败的消息),并让正常消息指向死信队列。当消息被拒绝、消息到了过期时间、或队列达到最大长度都可以加入死信队列。

为什么选用RabbitMQ其他MQ(ZeroMQ、ActiveMQ等等)
因为RabbitMQ与其它常见的MQ框架相比,虽然在性能上不是最突出的,但是Rabbit的综合能力相对来说是最好的。RabbitMQ支持消息持久化,有更高的可靠性和灵活的路由,也支持事务和集群,有简单清晰的可视化管理工具,社区(相关技术讨论的博客或群就是社区)也比较活跃。最主要的是上手也比较简单,可以和Spring无缝融合。
(了解即可)
MQ框架比较流行的有RabbitMq、ActiveMq、ZeroMq。
性能方面:ZeroMq>RabbitMq>ActiveMq
消息是否支持持久:RabbitMq,ActiveMq都支持,zeroMq不支持
技术点:可靠性、灵活的路由、集群、事务、高可用的队列、消息排序、问题追踪、可视化管理工具、插件系统、社区
    RabbitMq>ActiveMq>ZeroMq最差。当然ZeroMq也可以做到,不过自己必须手动写代码实现,代码量不小。尤其是可靠性中的:持久性、投递确认、发布者证实和高可用性。
    所以在可靠性和可用性上,RabbitMQ是首选,虽然ActiveMQ也具备,但是它性能不及RabbitMQ。
5、高并发
从实现语言来看,RabbitMQ最高,原因是它的实现语言是天生具备高并发高可用的erlang语言。

## Spring boot & Spring Cloud

Springboot和springcloud都是微服务框架,Spring Boot的核心思想就是约定大于配置,Spring Boot应用省去了原本spring大量的xml配置文件,采用properties或者yml属性配置文件即可完成原本很复杂的配置,采用 Spring Boot 可以大大的简化你的开发模式,所有你想集成的常用框架,它都有对应的组件支持。默认支持模板的是html,数据需要通过thymeleaf来渲染。
springboot在和mybatis整合时,sql语句有三种写法,常见的接口和xml,注解式(不建议使用),注解+类,这三种方式,我们在项目中使用的一个mybatis-plus这个插件,这个插件没有任何侵入,只需要在对应的层继承相关的东西就能完成单表的CRUD。
Spring Cloud是完全基于Spring Boot而开发,springcloud主要是实现分布式服务的架构,我们在项目中主要使用的模块分为eureka(注册中心)、熔断器(hystrix)、配置中心(config)、路由(zuul),每个模块都需要依赖注册中心,服务模块之间的调用都是通过feign实现的。
在使用SpringCloud时,操作时,需要注意使用fegin调用服务时请求方式默认都是post,简单数据类型需要加@RequertParm注解才能接收到,fegin使用@RepsonseBady默认返回的数据格式为xml,在RequestMapping里添加属性设置返回格式为json
模块:
注册中心(eureka)没啥说的,模块都需要依赖他,模块启动时必须先启动它
配置中心(config)配置中心就是配置一些公共的配置。其实除了配置中心以及注册中心的配置在本地,其他几乎都在配置中心也就是gitlab上,但是要注意,如果使用配置中心,我们需要把配置文件的名称改为boostrap.yml,让项目启动时先去加载。如果项目使用了配置中心,启动完注册中心,则第二个必须启动配置中心
路由(zuul)也叫做网关,就是同一个链接可以调用不同模块的提供的服务。如果没有,调用时就需要记录每个服务的请求地址,很麻烦,这时路由的好处就出现了,在路由上配置不同服务的地址,这时只需要访问路由,由路由去请求不同的服务
熔断器(hystrix),其实就是在Feign中来配置的,主要是解决分布式系统交互时超时处理和容错的类库,使用熔断器预防服务雪崩,我在项目配置的熔断器是20个请求,错误百分比50%以上,3秒内如果还没有返回则进入到熔断器处理,在熔断器中记录日志
常用注解:
@springbootapplication、@EnableEurekaServer、@EnableEurekaClient、@EnableFeignClients、@EnableZuulProxy、@FeignClient

SpringCloud和dubbo对比
SpringCloud是spring家族的产品,企业级开发离不开spring,SpringCloud有很多现成的组件使用,dubbo阿里系,开源后不维护了,现在已经贡献给apache了,使用dubbo如果想实现cloud一些功能时,一些组件时则需要自己集成,很麻烦,显得很重,SpringCloud可以看做一个很成型的微服务架构,而dubbo我觉得只是一个服务治理的架构,两者没有多么大的可比性,这是我的理解
Eureka和zookeeper
在分布式系统中有个著名的CAP定理,C-数据一致性;A-服务可用性;P-服务故障的容错性,这三个特性在任何分布式系统中不能同时满足,最多同时满足两个,
ZooKeeper基于CP,不保证高可用,如果Zookeeper集群中半数以上机器不可用,那么将无法获得数据。
Eureka基于AP,能保证高可用,即使所有机器都挂了,也能拿到本地缓存的数据。
作为注册中心,其实配置是不经常变动的,只有发版和机器出故障时会变。对于不经常变动的配置来说,CP是不合适的,而AP在遇到问题时可以用牺牲一致性来保证可用性,既返回旧数据,缓存数据。
所以理论上Eureka是更适合作注册中心。而现实环境中大部分项目可能会使用ZooKeeper,那是因为集群不够大,并且基本不会遇到用做注册中心的机器一半以上都挂了的情况。所以实际上也没什么大问题。

## 利用自定义注解+AOP切面

  1、定义日志注解
  
![在这里插入图片描述](https://img-blog.csdnimg.cn/89d10d2517d04994bccf54f85ca1c897.png)




  2、定义注解切面处理类(XML或注解方式都可以)
![在这里插入图片描述](https://img-blog.csdnimg.cn/736c9b1463d54cdb91bb159a0f25a0e2.png)

定义一个注解,加入@Document[表示该注解可以被javadoc(java生成API文档工具)识别,javadoc默认是不识别注解],@Retention(RetentionType.RUNTIME)[表示该自定义注解在运行时生效],@Target(ElementType.METHOD)[表示该注解只能在方法上生效]。定义好日志注解后在需要加入日志的方法上加入该注解。定义AOP切面类,将切面表达式变为切入该注解@After/@before/@Around(value="annotation(log)")。
![在这里插入图片描述](https://img-blog.csdnimg.cn/e491b2c8aa3d4529ba742e63224e0954.png)


## 多线程相关

**多线程写的多吗?有哪些实现方式**
多线程写的不太多,不过也有一点简单的了解,Java实现多线程可以通过继承Thread类或实现Runnable接口,并重写run方法。还可以实现Callable接口重写call()方法创建一个带有返回值的线程类。
(启动线程调用的是start()不是run(),run是线程具体做的事情)
**同步锁有哪些?**
我了解都的同步锁有Synchronized和ReentrantLock。在资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized是很合适的。原因在于,编译程序通常会尽可能的进行优化synchronize,另外可读性非常好。 ReentrantLock提供了多样化的同步(lock()),比如有时间限制的同步(tryLock(long timeout,TimeUnit unit)),可以被中断(lockInterruptibly())的同步(synchronized的同步是不能被中断的)等。在资源竞争不激烈的情形下,性能稍微比synchronized差点点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock确还能维持常态。
所以,我们写同步的时候,优先考虑synchronized,如果有特殊需要,再进一步优化。ReentrantLock如果用的不好,不仅不能提高性能,还可能带来灾难。因为ReentrantLock是编程式同步锁,需要我们自己进行解锁(unlock),所以我们一般都把解锁操作放到finally{}中。
线程有哪些状态?线程的生命周期,流程图能画一下吗?
![在这里插入图片描述](https://img-blog.csdnimg.cn/766d04d2f4c8432fa5f5ab09d5b80d10.png)

线程从创建、运行到结束总是处于下面五个状态之一:新建状态、就绪状态、运行状态、阻塞状态及死亡状态。

两个线程操作不同的变量会有问题吗?(脏读)

如果这两个变量之间没有任何联系,那么将无影响。如果它们之间有一些关联那么可能会造成脏读写问题,当一个线程在修改数据,另一个线程在查询数据,这样就出现脏读的问题。一般情况下我们是把出现脏读写的地方或有可能出现脏读写问题的地方加入同步锁,让线程依次执行就可以解决。

**线程的死锁是怎么回事?**

这种情况可能发生在当两个线程尝试获取其他资源的锁,而每个线程又陷入无线等待其他资源锁的释放,除非一个用户的进程被终止。线程死锁可能发生在以下的情况:
当两个线程相互调用Thread.join();
当两个线程使用嵌套的同步块时,一个线程占用了另一个线程的必需的锁,互相等待时被阻塞,就有可能出现死锁。
线程安全怎么理解的?为什么要加锁?当一个线程执行完另一个再去执行,为什么会有这种问题?
线程安全就是在多线程的情况下,不会出现脏读写的问题。为了避免脏读写的发生,我们可以使用同步锁,来保证线程的依次执行。

**多线程中的sleep()和wait()的区别**

1、	sleep()是Thread类中的方法,wait()是Object类中的方法。
2、	当前线程调用sleep()方法后会让当前线程处于休眠状态,让出CPU的执行权,当休眠结束后会自动恢复到运行状态。sleep()执行中不会释放当前线程中的锁(如果当前线程有锁的操作的话)。
3、	当前线程调用wait()方法后会让当前线程释放当前线程中的锁(如果当前线程有锁操作的话),当前线程进入等待阻塞状态。只有针对当前线程进行notify()或notifyAll()后才会进入就绪状态。

**在项目里有没有线程的问题?比如线程泄露?**

线程泄露指的就是线程使用完成之后没有销毁或没有放回到线程池中(线程池和数据库连接池原理一样)导致程序中的线程不断增加,最后导致无法创建新线程。
  目前我在项目里碰到线程相关的操作比较少,我在写多线程相关的内容是都会很小心去避免发送死锁问题以及把资源及时释放。


**线程池的原理,为什么要创建线程池?创建线程池的方式**
     降低资源消耗
    提高响应速度
    提高线程的可管理性
    提供更多更强大的功能

架构图
![在这里插入图片描述](https://img-blog.csdnimg.cn/c57f31f414c24c5593e2a69fc44bb6e9.png)



## 数据库

数据库引擎
Mysql默认使用的是Innodb,( show engines;可以通过这个语句来查看)

MyISAM和InnoDB的区别:
MyISAM用的是**表锁**,InnoDB用的是**行锁**。 		    
  MyISAM不支持事务处理,不支持外键而InnoDB支持。

表锁   锁定的是整张表(整张表不得做其他操作),并发低

行锁   只是锁定指定行(执行增删改时锁定该行,不得操作)。并发高。又分为共享锁和排它锁


在mysql 的 InnoDB引擎支持行锁,与Oracle不同,mysql的行锁是通过索引加载的,即是行锁是加在索引响应的行上的,要*是对应的SQL语句没有走索引,则会全表扫描,*
行锁则无法实现,取而代之的是表锁。




(了解)
ISAM:ISAM执行读取操作的速度很快,而且不占用大量的内存和存储资源。ISAM的两个主要不足之处在于,它不支持事务处理,也不能够容错:如果你的硬盘崩溃了,那么数据文件就无法恢复了。
MyISAM:可以看做是ISAM的升级版引擎,ISAM有的它都有, 它也同样不支持事务处理,也不能够容错 MyISAM还使用一种表格锁定的机制,来优化多个并发的读写操作,MYISAM强调了快速读取操作,MyISAM格式的一个重要缺陷就是不能在表损坏后恢复数据。
HEAP: HEAP允许只驻留在内存里的临时表格。驻留在内存里让HEAP要比ISAM和MYISAM都快,但是它所管理的数据是不稳定的,而且如果在关机之前没有进行保存,那么所有的数据都会丢失。
InnoDB: 引擎提供了对数据库ACID事务的支持,在使用MYSQL的时候,你所面对的每一个挑战几乎都源于ISAM和MyISAM数据库引擎不支持事务处理也不支持外键。尽管要比ISAM和 MyISAM引擎慢很多,但是InnoDB包括了对事务处理和外来键的支持,这两点都是前两个引擎所没有的
事务的四大特性(ACID)
事务是指一个工作单元,它包含了一组数据操作命令,并且所有的命令作为一个整体一起向系统提交或撤消请求操作,即这组命令要么都执行,要么都不执行。
⑴ 原子性(Atomicity)
  原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚
因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
⑵ 一致性(Consistency)
  一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
(了解)拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。

⑶ 隔离性(Isolation)
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。

⑷ 持久性(Durability)
  持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
数据库隔离级别
数据库事务的隔离级别有4种,由低到高分别为Read uncommitted(读未提交) 、Read committed(读提交) 、Repeatable read(重复读) 、Serializable(序列化) 。而且,在事务的并发操作中可能会出现脏读,不可重复读,幻读。

大多数数据库默认的事务隔离级别是Read committed(读提交),比如Sql Server , Oracle。
Mysql的默认隔离级别是Repeatable read(重复读)。

1: 读未提交,顾名思义,就是一个事务可以读取另一个未提交事务的数据。

事例:老板要给程序员发工资,程序员的工资是3.6万/月。但是发工资时老板不小心按错了数字,按成3.9万/月,该钱已经打到程序员的户口,但是事务还没有提交,就在这时,程序员去查看自己这个月的工资,发现比往常多了3千元,以为涨工资了非常高兴。但是老板及时发现了不对,马上回滚差点就提交了的事务,将数字改成3.6万再提交。

分析:实际程序员这个月的工资还是3.6万,但是程序员看到的是3.9万。他看到的是老板还没提交事务时的数据。这就是脏读。
那怎么解决脏读呢?Read committed!读提交,能解决脏读问题。

2读提交,顾名思义,就是一个事务要等另一个事务提交后才能读取数据。

事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候!!程序员的妻子要把钱全部转出充当家用,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了(第二次检测金额当然要等待妻子转出金额事务提交完)。程序员就会很郁闷,明明卡里是有钱的…
分析:这就是读提交,若有事务对数据进行更新(UPDATE)操作时,读操作事务要等待这个更新操作事务提交后才能读取数据,可以解决脏读问题。但在这个事例中,出现了一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读。
那怎么解决可能的不可重复读问题?使用重复读  Repeatable read ! 

3 重复读,就是在开始读取数据(事务开启)时,不再允许修改操作

事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(事务开启,不允许其他事务的UPDATE修改操作),收费系统事先检测到他的卡里有3.6万。这个时候他的妻子不能转出金额了。接下来收费系统就可以扣款了。
分析:重复读可以解决不可重复读问题。写到这里,应该明白的一点就是,不可重复读对应的是修改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不是UPDATE操作。

什么时候会出现幻读?
事例:程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,似乎出现了幻觉,这就是幻读。

那怎么解决幻读问题?Serializable!

Serializable 序列化
Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
SQL聚合函数
COUNT:统计行数量			SUM:获取单个列的合计值			AVG:计算某个列的平均值
MAX:计算列的最大值		MIN:计算列的最小值
DDL、DML、DCL、DQL
DDL(Data Definition Language):数据定义语言,用来定义数据库对象:库、表、列等;
创建数据库:CREATE DATABASE mydb1;	 删除数据库:DROP DATABASE  mydb1;
 修改数据库编码:ALTER DATABASE mydb1 CHARACTER SET utf8
DML(Data Manipulation Language):数据操作语言,用来定义数据库记录(数据);
 插入数据:INSERT语句		修改数据:UPDATE 语句		   删除数据:DELETE 语句
DCL(Data Control Language):数据控制语言,用来定义访问权限和安全级别;
 创建用户:CREATE USER 用户名@地址 IDENTIFIED BY '密码';
 授权用户:GRANT 权限1, … , 权限n ON 数据库.* TO 用户名
 撤销授权:REVOKE权限1, … , 权限n ON 数据库.* FORM 用户名
 查看权限:SHOW GRANTS FOR 用户名	 删除用户:DROP USER 用户名	
 修改密码:USE mysql;
          UPDATE USER SET PASSWORD=PASSWORD(‘密码’) WHERE User=’用户名’ and Host=’IP’;
          FLUSH PRIVILEGES;

DQL(Data Query Language):数据查询语言,用来查询记录(数据)。
SELECT selection_list /*要查询的列名称*/
 FROM table_list /*要查询的表名称*/
 WHERE condition /*行条件*/
 GROUP BY grouping_columns /*对结果分组*/
 HAVING condition /*分组后的行条件*/
 ORDER BY sorting_columns /*对结果分组*/
 LIMIT offset_start, row_count /*结果限定*/
MySQL和oracle的分页
oracle使用rownum加上嵌套子查询完成分页功能
SELECT * FROM
(
SELECT A.*, ROWNUM RN
FROM (SELECT * FROM TABLE_NAME) A
WHERE ROWNUM <= 40
)
WHERE RN >= 21
MySQL使用的是limit函数
SELECT * FROM table LIMIT 5,10;
数据库的悲观锁与乐观锁
悲观锁(Pessimistic Lock), 每次去查询数据的时候都认为别人会修改,所以每次在查询数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了这种锁机制,比如通过select ....for update进行数据锁定(行锁定)。

乐观锁(Optimistic Lock), 每次去查询数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号,时间戳等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。
Mysql优化
1.通过开启慢日志查询定位执行速度慢的sql语句,进行分析
  		在my.ini中: 
  			#开启慢日志
  			slow_query_log=1
  			#指明慢日志的地址
  			slow-query-log-file=d:\mysqlslow.log
  			#记录没有使用索引的sql语句
  			#log_queries_not_using_indexes=1 
  			#将查询时间大于1秒的sql语句进行记录
  			long_query_time=1 
  			把超过1秒的记录在慢查询日志中 
  			可以用mysqlreport来分析。
2:sql优化
2.1	外键必须加索引。
2.2	SELECT语句中避免使用 *,尽量应该根据业务需求按字段进行查询
2.3	在 where 及 order by 涉及的列上建立索引,要尽量避免全表扫描。
2.4	在设计表时要避免表中字段出现null的情况,通常要为其设置默认值,避免在查找时放弃使用索引而进行全表扫描(null的话如果有索引不生效)。
2.5 用>=替换>
  如一个表有100万记录,一个数值型字段A,
    A=0时,有30万条;
    A=1时,有30万条;
    A=2时,有39万条;
    A=3时,有1万记录。
    那么执行 A>2 与 A>=3 的效果就有很大的区别了,因为 A>2 时,
    ORACLE会先找出为2的记录索引再进行比较,
    而A>=3时ORACLE则直接找到=3的记录索引。
2.6 用NOT EXISTS/ EXISTS 替换 NOT IN / IN
NOT IN / IN不能应用表的索引
2.7 LIKE操作符(大数据的全文检索使用luncene)(solr)
  因为使用like不当,会导致性能问题,原因是like在左右两边都有
  %的时候,不会使用索引。
      
  如LIKE '%5400%' 这种查询不会引用索引而是全表扫描,
  而LIKE 'X5400%' 则会引用范围索引。
2.8 避免在索引列上使用计算和函数,这样索引就不能使用   
举例:  sal为索引列
  低效: 
  SELECT … FROM  DEPT  WHERE SAL * 12 > 25000; 
  高效: 
  SELECT … FROM DEPT WHERE SAL > 25000/12;
  2.6 用UNION-ALL 替换UNION,
  因为UNION-ALL不会过滤重复数据而且不会自动排序,所执行效率要快于UNION。

  2.9 减少访问数据库的次数,可以使用缓存,分段提交等处理
举例:如果批量删除多条数据,可以用  delete  from tableName where id in (1,2,3), 而不要用多条delete语句进行删除
索引
Mysql索引
MySQL索引类型有普通索引、唯一索引、主键索引、组合索引、全文索引。
MySQL索引的数据格式有:BTree和Hash。
索引的优缺点
索引会加快查询效率,但是在进行insert/update/delete时需要维护索引。
索引会占用磁盘空间,所以要避免过分创建索引。
什么情况需要加索引
1:主键默认会有主键索引。2:外键列必须创建索引。
给经常查询的列建立索引,字段多的情况下可以考虑使用组合索引,但是要考虑组合索引的使用问题(最左原则)
innerdb索引和锁的关系
Innerdb这个存储引擎比较特殊,他的锁是拿索引做的,意味的就是说你正要锁的这一行的这个字段没有索引的话那么他就会把表给锁了,比如你在做查询的时候,where条件后的字段没有索引的话那么就会把你的表给锁了
mysql单表最多支持多少数据
Mysql单表官方的说法貌似是2个G左右,一般来说,大家公认的是单表不超过2000万条数据



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值