小总结:Redis缓存和数据库以及Elasticsearch索引库,JDBC

讲讲Redis

什么是Redis?

redis是内存中的数据结构存储系统,一个key-value类型的非关系型数据库,可持久化的数据库,相对于关系型数据库(数据主要存在硬盘中),性能高,因此我们一般用redis来做缓存使用;并且redis支持丰富的数据类型,比较容易解决各种问题,因此redis可以用来作为注册中心,​数据库、缓存和消息中间件。Redis的Value支持5种数据类型,string、hash、list、set、zset(sorted set);

String类型:一个key对应一个value

Hash类型:它的key是string类型,value又是一个map(key-value),适合存储对象。

List类型:按照插入顺序的字符串链表(双向链表),主要命令是LPUSH和RPUSH,能够支持反向查找和遍历

Set类型:用哈希表类型的字符串序列,没有顺序,集合成员是唯一的,没有重复数据,底层主要是由一个value永远为null的hashmap来实现的。

zset类型:和set类型基本一致,不过它会给每个元素关联一个double类型的分数(score),这样就可以为成员排序,并且插入是有序的。

你还用过其他的缓存吗?这些缓存有什么区别?都在什么场景下去用?

对于缓存了解过redis和memcache

Memcache和redis的区别:

数据支持的类型:redis不仅仅支持简单的k/v类型的数据,同时还支持list、set、zset、hash等数据结构的存储;memcache只支持简单的k/v类型的数据,key和value都是string类型

可靠性:memcache不支持数据持久化,断电或重启后数据消失,但其稳定性是有保证的;redis支持数据持久化和数据恢复,允许单点故障,但是同时也会付出性能的代价

性能上:对于存储大数据,memcache的性能要高于redis

应用场景:

Memcache:适合多读少写,大数据量的情况(一些官网的文章信息等)

Redis:适用于对读写效率要求高、数据处理业务复杂、安全性要求较高的系统

案例:分布式系统,存在session之间的共享问题,因此在做单点登录的时候,我们利用redis来模拟了session的共享,来存储用户的信息,实现不同系统的session共享;

对redis的持久化了解不?

redis的持久化方式有两种:

RDB(半持久化方式):按照配置不定期的通过异步的方式、快照的形式直接把内存中的数据持久化到磁盘的一个dump.rdb文件(二进制的临时文件)中,redis默认的持久化方式,它在配置文件(redis.conf)中。

优点:只包含一个文件,将一个单独的文件转移到其他存储媒介上,对于文件备份、灾难恢复而言,比较实用。

缺点:系统一旦在持久化策略之前出现宕机现象,此前没有来得及持久化的数据将会产生丢失

RDB持久化配置:

Redis会将数据集的快照dump到dump.rdb文件中。此外,我们也可以通过配置文件来修改Redis服务器dump快照的频率,在打开6379.conf文件之后,我们搜索save,可以看到下面的配置信息:

save 900 1              #在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照。

save 300 10            #在300秒(5分钟)之后,如果至少有10个key发生变化,则dump内存快照。

save 60 10000        #在60秒(1分钟)之后,如果至少有10000个key发生变化,则dump内存快照。

AOF(全持久化的方式):把每一次数据变化都通过write()函数将你所执行的命令追加到一个appendonly.aof文件里面,Redis默认是不支持这种全持久化方式的,需要在配置文件(redis.conf)中将appendonly no改成appendonly yes

优点:数据安全性高,对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机问题,也不会破坏日志文件中已经存在的内容;

缺点:对于数量相同的数据集来说,aof文件通常要比rdb文件大,因此rdb在恢复大数据集时的速度大于AOF;

AOF持久化配置:

在Redis的配置文件中存在三种同步方式,它们分别是:

appendfsync always     #每次有数据修改发生时都会都调用fsync刷新到aof文件,非常慢,但是安全;

appendfsync everysec  #每秒钟都调用fsync刷新到aof文件中,很快,但是可能丢失一秒内的数据,推荐使用,兼顾了速度和安全;

appendfsync no          #不会自动同步到磁盘上,需要依靠OS(操作系统)进行刷新,效率快,但是安全性就比较差;

二种持久化方式区别:

AOF在运行效率上往往慢于RDB,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效;

如果缓存数据安全性要求比较高的话,用aof这种持久化方式(比如项目中的购物车);

如果对于大数据集要求效率高的话,就可以使用默认的。而且这两种持久化方式可以同时使用。  

做过redis的集群吗?你们做集群的时候搭建了几台,都是怎么搭建的?

Redis的数据是存放在内存中的,不适合存储大数据,大数据存储一般公司常用hadoop中的Hbase或者MogoDB。redis主要用来处理高并发的,用我们的项目来说,电商项目如果并发大的话,一台单独的redis是不能足够支持我们的并发,这就需要我们扩展多台设备协同合作,即用到集群。

Redis搭建集群的方式有多种,例如:客户端分片、Twemproxy、Codis等,但是redis3.0之后就支持redis-cluster集群,这种方式采用的是无中心结构,每个节点保存数据和整个集群的状态,每个节点都和其他所有节点连接。如果使用的话就用redis-cluster集群。集群这块是公司运维搭建的,具体怎么搭建不是太了解。

我们项目中redis集群主要搭建了6台,3主(为了保证redis的投票机制)3从(高可用),每个主服务器都有一个从服务器,作为备份机。所有的节点都通过PING-PONG机制彼此互相连接;客户端与redis集群连接,只需要连接集群中的任何一个节点即可;Redis-cluster中内置了16384个哈希槽,Redis-cluster把所有的物理节点映射到【0-16383】slot上,负责维护。

redis有事务吗?

Redis是有事务的,redis中的事务是一组命令的集合,这组命令要么都执行,要不都不执行,保证一个事务中的命令依次执行而不被其他命令插入。redis的事务是不支持回滚操作的。redis事务的实现,需要用到MULTI(事务的开始)和EXEC(事务的结束)命令 ;

缓存穿透

缓存查询一般都是通过key去查找value,如果不存在对应的value,就要去数据库中查找。如果这个key对应的value在数据库中也不存在,并且对该key并发请求很大,就会对数据库产生很大的压力,这就叫缓存穿透。

解决方案:

  • 对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃。
  • 将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
  • 如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

缓存雪崩

当缓存服务器重启或者大量缓存集中在一段时间内失效,发生大量的缓存穿透,这样在失效的瞬间对数据库的访问压力就比较大,所有的查询都落在数据库上,造成了缓存雪崩。 这个没有完美解决办法,但可以分析用户行为,尽量让失效时间点均匀分布。大多数系统设计者考虑用加锁或者队列的方式保证缓存的单线程(进程)写,从而避免失效时大量的并发请求落到底层存储系统上。

解决方案:

  • 在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
  • 可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存
  • 不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀
  • 做二级缓存,或者双缓存策略。A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期。

redis的安全机制(你们公司redis的安全这方面怎么考虑的?)

漏洞介绍:redis默认情况下,会绑定在bind 0.0.0.0:6379,这样就会将redis的服务暴露到公网上,如果在没有开启认证的情况下,可以导致任意用户在访问目标服务器的情况下,未授权就可访问redis以及读取redis的数据,攻击者就可以在未授权访问redis的情况下可以利用redis的相关方法,成功在redis服务器上写入公钥,进而可以直接使用私钥进行直接登录目标主机;

解决方案:

  1. 禁止一些高危命令。修改redis.conf文件,用来禁止远程修改DB文件地址,比如 rename-command FLUSHALL "" 、rename-command CONFIG"" 、rename-command EVAL “”等;
  2. 以低权限运行redis服务。为redis服务创建单独的用户和根目录,并且配置禁止登录;
  3. 为redis添加密码验证。修改redis.conf文件,添加requirepass mypassword;
  4. 禁止外网访问redis。修改redis.conf文件,添加或修改 bind 127.0.0.1,使得redis服务只在当前主机使用;
  5. 做log监控,及时发现攻击;

redis的哨兵机制(redis2.6以后出现的):

  • 监控:监控主数据库和从数据库是否正常运行;
  • 提醒:当被监控的某个redis出现问题的时候,哨兵可以通过API向管理员或者其他应用程序发送通知;
  • 自动故障迁移:主数据库出现故障时,可以自动将从数据库转化为主数据库,实现自动切换;

具体的配置步骤参考的网上的文档。要注意的是,如果master主服务器设置了密码,记得在哨兵的配置文件(sentinel.conf)里面配置访问密码

redis中对于生存时间的应用

  Redis中可以使用expire命令设置一个键的生存时间,到时间后redis会自动删除;

  应用场景:

  1. 设置限制的优惠活动的信息;
  2. 一些及时需要更新的数据,积分排行榜;
  3. 手机验证码的时间;
  4. 限制网站访客访问频率;

 

能讲下redis的具体使用场景吗?使用redis存储长期不改变的数据完全可以使用也看静态化,那么你们当时是为什么会使用redis?

redis在项目中应用:

  • 主要应用在门户网站首页广告信息的缓存。因为门户网站访问量较大,将广告缓存到redis中,可以降低数据库访问压力,提高查询性能。
  • 应用在用户注册验证码缓存。利用redis设置过期时间,当超过指定时间后,redis清理验证码,使过期的验证码无效。
  • 用在购物车模块,用户登陆系统后,添加的购物车数据需要保存到redis缓存中。

使用redis主要是减少系统数据库访问压力。从缓存中查询数据,也提高了查询性能,挺高用户体验度。
 

Redis分布式锁理解

获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。

  • 获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。
  • 释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。
  • SETEX:如果 key 已经存在, SETEX 命令将覆写旧值。
  • SETNX:若给定的 key 已经存在,则 SETNX 不做任何动作。

 

Redis怎么设置过期的?

设置过期:this.redisTemplate.expire("max",tempTime,TimeUnit.SECONDS);

 

讲到redis缓存的时候说不清楚

redis中项目中的使用场景:

  • 主要应用在门户网站首页广告信息的缓存。因为门户网站访问量较大,将广告缓存到redis中,可以降低数据库访问压力,提高查询性能。
  • 应用在用户注册验证码缓存。利用redis设置过期时间,当超过指定时间后,redis清理验证码,使过期的验证码无效。
  • 用在购物车模块,用户登陆系统后,添加的购物车数据需要保存到redis缓存中。

技术角度分析:

  • Redis如何实现负载的?采用Hash槽来运算存储值,使用CRC16算法取模运算,来保证负载问题。
  • Redis缓存穿透问题?将数据查询出来如果没有强制设置空值,并且设置过期时间,减少频繁查询数据库。
  • 使用redis主要是减少系统数据库访问压力。从缓存中查询数据,也提高了查询性能,挺高用户体验度。

 

redis中对一个key进行自增或者自减操作,它是原子性的吗?

是原子性的。对于Redis而言,命令的原子性指的是:一个操作的不可以再分,操作要么执行,要么不执行。Redis的操作之所以是原子性的,是因为Redis是单线程的。对Redis来说,执行get、set以及eval等API,都是一个一个的任务,这些任务都会由Redis的线程去负责执行,任务要么执行成功,要么执行失败,这就是Redis的命令是原子性的原因。Redis本身提供的所有API都是原子操作,Redis中的事务其实是要保证批量操作的原子性。

 

项目添加Redis缓存后,持久化具体怎么实现的。

  • RDB:保存存储文件到磁盘;同步时间为15分钟,5分钟,1分钟一次,可能存在数据丢失问题。
  • AOF:保存命令文件到磁盘;安全性高,修改后立即同步或每秒同步一次。

上述两种方式在我们的项目中都有使用到,在广告轮播的功能中使用了redis缓存,先从redis中获取数据,无数据后从数据库中查询后保存到redis中。采用默认的RDB方式。

 

怎么提高redis缓存利用率

  • 从业务场景分析,预计会高频率用到的数据预先存放到redis中,
  • 可以定时扫描命中率低的数据,可以直接从redis中清除。

 

Redis宕机之后,购物车中的数据如何处理?如何缓解mysql压力?

用redis保存的*.rdb文件恢复即可。另外redis还有AOF功能,启动时可以自动恢复到前一条查询。这样做在一定程度上减少数据丢失。但重启redis会需要从关系型数据库中读取数据,增大mysql的压力。依据实际情况,如果redis之前有主从复制,则可在其他节点redis上拿到数据。如果公司没钱,则只能暂时限制客户端访问量,优先恢复redis数据。

 

Redis和mysql数据同步是先删除redis还是先删除mysql?

不管是先写库,再删除缓存;还是先删缓存,再写库,都有可能出现数据不一致的情况。因为写和读是并发的,没法保证顺序,如果删了缓存,还没有来得及写库,另一个线程就来读取,发现缓存为空,则去数据库中读取数据写入缓存,此时缓存中为脏数据。如果先写了库,再删除缓存前,写库的线程宕机了,没有删除掉缓存,则也会出现数据不一致情况。 如果是redis集群,或者主从模式,写主读从,由于redis复制存在一定的时间延迟,也有可能导致数据不一致。这时候,考虑先删除数据库内容,再删redis。因为在库存等实时数据都是直接在数据库中读取,从业务逻辑上来说,我们允许查询时的数据缓存误差,但是不允许结算时的数据存在误差。

 

Redis中watch机制和原理

我们常用redis的watch和multi来处理一些涉及并发的操作,redis的watch+multi实际是一种乐观锁

watch命令描述:WATCH命令可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行。监控一直持续到EXEC命令(事务中的命令是在EXEC之后才执行的,所以在MULTI命令后可以修改WATCH监控的键值)

 

讲讲缓存的设计和优化,缓存和数据库一致性同步解决方案

  • 降低后端负载:对于高消耗的SQL:join结果集、分组统计结果;对这些结果进行缓存。
  • 加速请求响应
  • 大量写合并为批量写:如计数器先redis累加再批量写入DB
  • 超时剔除:例如expire
  • 主动更新:开发控制生命周期(最终一致性,时间间隔比较短)
  • 缓存空对象
  • 布隆过滤器拦截
  • 命令本身的效率:例如sql优化,命令优化
  • 网络次数:减少通信次数
  • 降低接入成本:长连/连接池,NIO等。
  • IO访问合并

目的:要减少缓存重建次数、数据尽可能一致、减少潜在危险。
解决方案:
互斥锁setex,setnx:

  • 如果 set(nx 和 ex) 结果为 true,说明此时没有其他线程重建缓存,那么当前线程执行缓存构建逻辑。
  • 如果 setnx(nx 和 ex) 结果为 false,说明此时已经有其他线程正在执行构建缓存的工作,那么当前线程将休息指定时间 ( 例如这里是 50 毫秒,取决于构建缓存的速度 ) 后,重新执行函数,直到获取到数据。

永远不过期:
热点key,无非是并发特别大一级重建缓存时间比较长,如果直接设置过期时间,那么时间到的时候,巨大的访问量会压迫到数据库上,所以要给热点key的val增加一个逻辑过期时间字段,并发访问的时候,判断这个逻辑字段的时间值是否大于当前时间,大于了说明要对缓存进行更新了,那么这个时候,依然让所有线程访问老的缓存,因为缓存并没有设置过期,但是另开一个线程对缓存进行重构。等重构成功,即执行了redis set操作之后,所有的线程就可以访问到重构后的缓存中的新的内容了

  • 从缓存层面来看,确实没有设置过期时间,所以不会出现热点 key 过期后产生的问题,也就是“物理”不过期。
  • 从功能层面来看,为每个 value 设置一个逻辑过期时间,当发现超过逻辑过期时间后,会使用单独的线程去构建缓存。

一致性问题:

  • 先删除缓存,然后在更新数据库,如果删除缓存失败,那就不要更新数据库,如果说删除缓存成功,而更新数据库失败,那查询的时候只是从数据库里查了旧的数据而已,这样就能保持数据库与缓存的一致性。
  • 先去缓存里看下有没有数据,如果没有,可以先去队列里看是否有相同数据在做更新,发现队列里有一个请求了,那么就不要放新的操作进去了,用一个while(true)循环去查询缓存,循环个200MS左右再次发送到队列里去,然后同步等待缓存更新完成。
     

为什么InnoDB支持事务而myisam不支持

MyISAM:这个是默认类型,它是基于传统的ISAM类型,ISAM是Indexed Sequential Access Method (有索引的顺序访问方法) 的缩写,它是存储记录和文件的标准方法.与其他存储引擎比较,MyISAM具有检查和修复表格的大多数工具. MyISAM表格可以被压缩,而且它们支持全文搜索.它们不是事务安全的,而且也不支持外键。如果事物回滚将造成不完全回滚,不具有原子性。如果执行大量的SELECT,MyISAM是更好的选择。

InnoDB:这种类型是事务安全的.它与BDB类型具有相同的特性,它们还支持外键.InnoDB表格速度很快.具有比BDB还丰富的特性,因此如果需要一个事务安全的存储引擎,建议使用它.如果你的数据执行大量的INSERT或UPDATE,出于性能方面的考虑,应该使用InnoDB表
 

 

SQL语句中关于查询语句的优化你们是怎么做的?

  1. 应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
  2. 对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
  3. 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描
  4. 尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描
  5. in 和 not in 也要慎用,否则会导致全表扫描
  6. 应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。
  7. 应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描
  8. 不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
  9. 在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使 用,并且应尽可能的让字段顺序与索引顺序相一致。
  10. 索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。
  11. 尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
  12. 任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。
  13. 使用连接(JOIN)来代替子查询(Sub-Queries)
  14. 使用联合(UNION)来代替手动创建的临时表

 

MySQL索引使用限制

不要在列上进行运算

select * from users where YEAR(adddate)<2007; 
将在每个行上进行运算,这将导致索引失效而进行全表扫描,
因此我们可以改成select * from users where adddate<‘2007-01-01’;

like查询是以%开头不使用索引

    如果使用like。like “%aaa%” 不会使用索引而like “aaa%”可以使用索引。
    select * from users where name like '%aaa%'不会使用索引
    select * from users where name like 'aaa%'可以使用索引

使用短索引

例如,如果有一个CHAR(255)的列,如果在前10个或20个字符内,多数值是惟一的,
那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。

索引不会包含NULL列,IS NULL /IS NOT NULL不使用索引

复合索引中如果有一列含有NULL值那么这个组合索引都将失效,一般需要给默认值0或者 ' '字符串

 最左匹配,任何一个索引的最左前缀可以通过使用优化器来查找行

不按索引最左列开始查询(多列索引) 例如:
index(‘c1’, ‘c2’, ‘c3’) ,
where ‘c2’ = ‘aaa’ 不使用索引,
where ‘c2’ = ‘aaa’ and ‘c3’ = ‘sss’ 不能使用索引。
where ‘c1’ = ‘aaa’ and ‘c2’ = ‘bbb’ 可以使用索引。
index('c1')靠最左可以使用索引。

多列索引,不是使用的第一部分,则不会使用索引

查询中某个列有范围查询,则其右边的所有列都无法使用查询(多列查询)。
where c1= ‘xxx’ and c2 like = ‘aa%’ and c3=’sss’ 
该查询只会使用索引中的前两列,c3将不能使用到索引,因为like是范围查询。

检索排序

一个查询语句中,既有检索又有排序并且是不同的字段,且这两个列上都有单列索引(独立索引),
那么只有其中一个列用到索引,因为查询优化器在做检索和排序中不能同时使用两个不同的索引。

索引散列度

通过索引扫描的记录超过了表总行数的30%(估计值),则查询优化器认为全表扫描的效率更高,所以会变成全表扫描查询。

隐式转换:如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引

隐式转换导致的索引失效。比如,表的字段tu_mdn定义为varchar(20),
但在查询时把该字段作为number类型当做where条件,这样会导致索引失效. 
错误的例子:select * from test where tu_mdn=13333333333; 
正确的例子:select * from test where tu_mdn='13333333333’;

条件中有or

即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因)
注意:要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引

使用全表扫描要比使用索引快,则不使用索引,数据唯一性差(一个字段的取值只有几种时)的字段不要使用索引

比如性别,只有两种可能数据。意味着索引的二叉树级别少,多是平级。这样的二叉树查找无异于全表扫描

频繁更新的字段不要使用索引

比如logincount登录次数,频繁变化导致索引也频繁变化,增大数据库工作量,降低效率

where 子句里对索引列使用不等于(<>),使用索引效果一般,不使用索引

 

 

数据库创建表的时候会有哪些考虑呢?

项目中使用的是MySQL数据库,数据库创建表时要考虑:

  • 大数据字段最好剥离出单独的表,以便影响性能
  • 使用varchar,代替char,这是因为varchar会动态分配长度,char指定为20,即时你存储字符“1”,它依然是20的长度
  • 给表建立主键,看到好多表没主键,这在查询和索引定义上将有一定的影响
  • 避免表字段运行为null,如果不知道添加什么值,建议设置默认值,特别int类型,比如默认值为0,在索引查询上,效率立显。
  • 建立索引,聚集索引则意味着数据的物理存储顺序,最好在唯一的,非空的字段上建立,其它索引也不是越多越好,索引在查询上优势显著,在频繁更新数据的字段上建立聚集索引,后果很严重,插入更新相当忙。
  • 组合索引和单索引的建立,要考虑查询实际和具体模式

 

有了解过大数据层面的分库分表吗?以及mysql的执行计划吗?

  • 分库:通过Mycat结点来管理不同服务器上的数据库,每个表最多存500万条记录
  • 分表:重直切割,水平切割

MySql提供了EXPLAIN语法用来进行查询分析,在SQL语句前加一个"EXPLAIN"即可。mysql中的explain语法可以帮助我们改写查询,优化表的结构和索引的设置,从而最大地提高查询效率。

 

有了解过数据库中的表级锁和行级锁吗?乐观锁和悲观锁你有哪些了解?

MySQL的锁机制比较简单,其最显著的特点是不同的存储引擎支持不同的锁机制。比如,MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking);InnoDB存储引擎既支持行级锁( row-level locking),也支持表级锁,但默认情况下是采用行级锁。

MySQL主要锁的特性可大致归纳如下:

  • 表级锁: 开销小,加锁快;不会出现死锁(因为MyISAM会一次性获得SQL所需的全部锁);锁定粒度大,发生锁冲突的概率最高,并发度最低。
  • 行级锁: 开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
  • 乐观锁:通过version版本字段来实现
  • 悲观锁:通过for update来实现

Sql层面:

一、悲观锁

  •     排它锁,当事务在操作数据时把这部分数据进行锁定,直到操作完毕后再解锁,其他事务操作才可操作该部分数据。这将防止其他进程读取或修改表中的数据。
  •     实现:大多数情况下依靠数据库的锁机制实现

     一般使用 select ...for update 对所选择的数据进行加锁处理,例如select * from account where name=”Max” for update, 这条sql 语句锁定了account 表中所有符合检索条件(name=”Max”)的记录。本次事务提交之前(事务提交时会释放事务过程中的锁),外界无法修改这些记录。

二、乐观锁

  •     如果有人在你之前更新了,你的更新应当是被拒绝的,可以让用户重新操作。
  •     实现:大多数基于数据版本(Version)记录机制实现

     具体可通过给表加一个版本号或时间戳字段实现,当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断当前版本信息与第一次取出来的版本值大小,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据,拒绝更新,让用户重新操作。

代码层面:

  • 悲观锁:一段执行逻辑加上悲观锁,不同线程同时执行时,只能有一个线程执行,其他的线程在入口处等待,直到锁被释放.
  • 乐观锁:一段执行逻辑加上乐观锁,不同线程同时执行时,可以同时进入执行,在最后更新数据的时候要检查这些数据是否被其他线程修改了(版本和执行初是否相同),没有修改则进行更新,否则放弃本次操作。
     

Mysql优化有没有工具

三个MySQL性能测试工具:The MySQL Benchmark Suite、MySQL super-smack、MyBench。除了第一个为MySQL性能测试工具,其他两个都为压力测试工具。

 

你有了解mysql的隔离级别吗?mysql默认的隔离级别是什么?

数据库事务的隔离级别有四种,隔离级别高的数据库的可靠性高,但并发量低,而隔离级别低的数据库可靠性低,但并发量高,系统开销小。

  1. READ UNCIMMITTED(未提交读)
  2. READ COMMITTED(提交读)
  3. REPEATABLE READ(可重复读)
  4. SERIALIZABLE(可串行化)

mysql默认的事务处理级别是'REPEATABLE-READ',也就是可重复读。

 

怎样进行数据库性能调优

一:应用程序优化

 (1)把数据库当作奢侈的资源看待,在确保功能的同时,尽可能少地动用数据库资源。

 (2)不要直接执行完整的SQL 语法,尽量通过存储过程实现数据库操作。

 (3)客户与服务器连接时,建立连接池,让连接尽量得以重用,以避免时间与资源的损耗。

 (4)非到不得已,不要使用游标结构,确实使用时,注意各种游标的特性。

二:基本表设计优化

  (1)表设计遵循第三范式。在基于表驱动的信息管理系统中,基本表的设计规范是第三范式。

  (2)分割表。分割表可分为水平分割表和垂直分割表两种:水平分割是按照行将一个表分割为多个表。

 (3)引入中间表。

: 数据库索引优化

索引是建立在表上的一种数据组织,它能提高访问表中一条或多条记录的特定查询效率。 

聚集索引

  一种索引,该索引中键值的逻辑顺序决定了表中相应行的物理顺序。 

  聚集索引确定表中数据的物理顺序。

非聚集索引

  一种索引,该索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同.

 

MySQL存储过程

SQL语句需要先编译然后执行,而存储过程(Stored Procedure)是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给定参数(如果该存储过程带有参数)来调用执行它。

存储过程是可编程的函数,在数据库中创建并保存,可以由SQL语句和控制结构组成。当想要在不同的应用程序或平台上执行相同的函数,或者封装特定功能时,存储过程是非常有用的。数据库中的存储过程可以看做是对编程中面向对象方法的模拟,它允许控制数据的访问方式。

存储过程的优点:

  1. 增强SQL语言的功能和灵活性:存储过程可以用控制语句编写,有很强的灵活性,可以完成复杂的判断和较复杂的运算。
  2. 标准组件式编程:存储过程被创建后,可以在程序中被多次调用,而不必重新编写该存储过程的SQL语句。而且数据库专业人员可以随时对存储过程进行修改,对应用程序源代码毫无影响。
  3. 较快的执行速度:如果某一操作包含大量的Transaction-SQL代码或分别被多次执行,那么存储过程要比批处理的执行速度快很多。因为存储过程是预编译的。在首次运行一个存储过程时查询,优化器对其进行分析优化,并且给出最终被存储在系统表中的执行计划。而批处理的Transaction-SQL语句在每次运行时都要进行编译和优化,速度相对要慢一些。
  4. 减少网络流量:针对同一个数据库对象的操作(如查询、修改),如果这一操作所涉及的Transaction-SQL语句被组织进存储过程,那么当在客户计算机上调用该存储过程时,网络中传送的只是该调用语句,从而大大减少网络流量并降低了网络负载。
  5. 作为一种安全机制来充分利用:通过对执行某一存储过程的权限进行限制,能够实现对相应的数据的访问权限的限制,避免了非授权用户对数据的访问,保证了数据的安全。

MySQL存储过程的创建

语法:

CREATE PROCEDURE  过程名([[IN|OUT|INOUT] 参数名 数据类型[,[IN|OUT|INOUT] 参数名 数据类型…]]) [特性 ...] 过程体

DELIMITER //
  CREATE PROCEDURE myproc(OUT s int)
    BEGIN
      SELECT COUNT(*) INTO s FROM students;
    END
    //
DELIMITER ;

分隔符:MySQL默认以";"为分隔符,如果没有声明分割符,则编译器会把存储过程当成SQL语句进行处理,因此编译过程会报错,所以要事先用“DELIMITER //”声明当前段分隔符,让编译器把两个"//"之间的内容当做存储过程的代码,不会执行这些码;“DELIMITER ;”的意为把分隔符还原。

参数:存储过程根据需要可能会有输入、输出、输入输出参数,如果有多个参数用","分割开。MySQL存储过程的参数用在存储过程的定义,共有三种参数类型,IN,OUT,INOUT:

  • IN参数的值必须在调用存储过程时指定,在存储过程中修改该参数的值不能被返回,为默认值
  • OUT:该值可在存储过程内部被改变,并可返回
  •  INOUT:调用时指定,并且可被改变和返回

过程体:过程体的开始与结束使用BEGIN与END进行标识。
 

 

怎么实现数据量大、 并发量高的搜索

创建Elasticsearch/solr索引库,数据量特别大时采用Elasticsearch/solr分布式集群。

 

ES的用途

ES在系统中主要完成商品搜索功能,提高搜索性能。

 

讲讲Elasticsearch全文搜索:

简单介绍一个Elasticsearch?

ElasticSearch是一个基于Lucene的搜索服务器。通过HTTP使用JSON进行数据索引,用于分布式全文检索,解决人们对于搜索的众多要求。

lucene与elasticsearch(solr)有什么区别?

lucene只是一个提供全文搜索功能类库的核心工具包,而真正使用它还需要一个完善的服务框架搭建起来的应用。好比lucene是类似于jdk,而搜索引擎软件就是tomcat 的。elasticsearch和solr,这两款都是基于lucene的搭建的,可以独立部署启动的搜索引擎服务软件。

基本概念:

cluster集群

整个elasticsearch 默认就是集群状态,整个集群是一份完整、互备的数据。

node节点

集群中的一个节点,一般只一个进程就是一个node

shard分片

分片,即使是一个节点中的数据也会通过hash算法,分成多个片存放,默认是5片。

index逻辑数据库

相当于rdbms的database, 对于用户来说是一个逻辑数据库,虽然物理上会被分多个shard存放,也可能存放在多个node中。

type

类似于rdbms的table,但是与其说像table,其实更像面向对象中的class , 同一Json的格式的数据集合。

document

类似于rdbms的 row、面向对象里的object

field

相当于字段、属性

与MySQL对比

利用kibana学习 elasticsearch restful api (DSL)

Kibana 是一个开源分析和可视化平台,可视化操作 Elasticsearch 。Kibana可以用来搜索,查看和与存储在 Elasticsearch 索引中的数据进行交互。可以轻松地进行高级数据分析,并可在各种图表,表格和地图中显示数据。

ES提供了基于JSON的query DSL查询语言

es中保存的数据结构

public class  Movie {

 String id;

     String name;

     Double doubanScore;

     List<Actor> actorList;

}

 

public class Actor{

String id;

String name;

}

这两个对象如果放在关系型数据库保存,会被拆成2张表,但是elasticsearch是用一个json来表示一个document。

所以它保存到es中应该是:

{

  “id”:”1”,

  “name”:”operation red sea”,

  “doubanScore”:”8.5”,

  “actorList”:[  

{“id”:”1”,”name”:”zhangyi”},

{“id”:”2”,”name”:”haiqing”},

{“id”:”3”,”name”:”zhanghanyu”}

]

}

es 的java 客户端的选择

目前市面上有两类客户端

  • 一种是TransportClient 为代表的ES原生客户端,不能执行原生dsl语句必须使用它的Java api方法。
  • 一种是以Rest Api为主的missing client,最典型的就是jest。 这种客户端可以直接使用dsl语句拼成的字符串,直接传给服务端,然后返回json字符串再解析。

两种方式各有优劣,但是最近elasticsearch官网,宣布计划在7.0以后的版本中废除TransportClient。以RestClient为主。在官方的RestClient 基础上,进行了简单包装的Jest客户端,就成了首选,而且该客户端也与springboot完美集成。

中文分词

elasticsearch本身自带的中文分词,就是单纯把中文一个字一个字的分开,根本没有词汇的概念。

问题:

  • es大量的写操作会影响es 性能,因为es需要更新索引,而且es不是内存数据库,会做相应的io操作。
  • 而且修改某一个值,在高并发情况下会有冲突,造成更新丢失,需要加锁,而es的乐观锁会恶化性能问题。

解决思路:

用redis做精确计数器,redis是内存数据库读写性能都非常快,利用redis的原子性的自增可以解决并发写操作。redis每计100次数(可以被100整除)我们就更新一次es ,这样写操作就被稀释了100倍,这个倍数可以根据业务情况灵活设定。

增量同步索引库

推荐使用MQ(RabbitMQ)

原理:使用MQ做增量同步,即当修改数据之后就将此数据发送至MQ,由MQ将此数据同步到ES上

 

ES索引中使用了IK分词器,你们项目中使用到了分词器的哪种工作模式?

IK分词器,基本可分为两种模式,一种为smart模式,一种为非smart模式。

例如:张三说的确实在理
smart模式的下分词结果为:
张三 | 说的 | 确实 | 在理
而非smart模式下的分词结果为:
张三 | 三 | 说的 | 的确 | 的 | 确实 | 实在 | 在理
可见非smart模式所做的就是将能够分出来的词全部输出;smart模式下,IK分词器则会根据内在方法输出一个认为最合理的分词结果,这就涉及到了歧义判断。

项目中采用的是smart模块分词的。

 

怎么分词

使用第三方的分词器IKAnalyzer,会按照中国人用此习惯自动分词。

 

 ES高亮不能显示的问题

前台使用angularJS加载搜索结果,但是发现高亮不能展示。

问题原因:

angularJS底层使用ajax,异步加载高亮信息返回给页面后,页面没有刷新,就直接显示返回的数据。此时会把所有的数据作为普通的文本数据进行加载。因此就没有高亮的效果。

解决方案:

使用angularJS过滤器过滤文本数据,此时angularJS过滤器把html文本数据解析为浏览器能识别的html标签。高亮就能展示了。

 

简单介绍一下Es全文检索在整个系统中的应用,在更新索引库的同时会产生索引碎片,这个碎片是如何处理的?

根据商品的名称,分类,品牌等属性来创建索引进行商品搜索。

更新索引库时会先删除索引,然后再重建。而对于删除聚集索引,则会导致对应的非聚集索引重建两次(删除时重建,建立时再重建).直接删除碎片。

 

JDBC的理解

JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序

有了JDBC,向各种关系数据发送SQL语句就是一件很容易的事。换言之,有了JDBC API,就不必为访问Sybase数据库专门写一个程序,为访问Oracle数据库又专门写一个程序,或为访问Informix数据库又编写另一个程序等等,程序员只需用JDBC API写一个程序就够了,它可向相应数据库发送SQL调用。
 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章