面试经历
Posted linyukun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试经历相关的知识,希望对你有一定的参考价值。
今天算是第一次技术面,真是一塌糊涂,各种问题答不上来,没有条理,我都感觉在浪费人家面试官的时间,然而面试官真是非常nice啊,还给了我一些建议。记录下今天面试碰到的一些问题,面试过不过是一方面,主要是不能接受自己这么菜啊。
面试的第一个问题是说说java.util下用过的类或者包,面试完后我想当时应该说的是集合相关的,然而当时不知道怎么说了concurrent这个并发包,关键平时没怎么用过对它也并不了解啊,唉!!也好,系统地了解一下java.util包下相关的类和子包吧。
java.util是Java的一个实用工具类库包,包中包含集合框架,collection类,事件模型,日期和时间,国际化和各种实用工具类。查了一下JDK的API文档,列举了一些java.util下常用的类、接口、子包:
第二个问题是数据库的锁机制,关于数据库的锁机制只是零零散散地知道一些,没有系统地了解,面试的时候也说不出个所以然啊。那就系统地了解一下数据库的锁。
先说说为什么数据库需要锁?数据库是一个多用户使用的共享资源,当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性(脏读,不可重复读,幻读等),可能产生死锁。为了解决这些,就需要通过加锁对数据库实现并发控制。举个加锁的例子,当一个执行sql语句的事务想要操作表记录之前,先向数据库发出请求,对你访问的记录集加锁,在这个事务释放这个锁之前,其他事务不能对这些数据进行更新操作。
数据库事务
上面还提到了事务,接着说锁机制之前还要介绍下数据库的事务。事务是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。举个例子:银行转账,A账户转给B账户10000元,这里存在两个操作,一个是A账户扣款10000元,两一个操作是B账户增加10000元,两者就构成了转账这个事务。要么两个操作都成功,A账户扣款1000元,B账户增加1000元,事务成功;要么两个操作都失败,A账户和B账户金额都没变,事务失败。事务拥有四个特性,即ACID(原子性,一致性,隔离性和一致性)。
上文还说到若不对事务进行并发控制就可能会读取和存储不正确的数据,来看看具体的例子,假设有一个order表,有个字段叫count,作为计数用,当前值为100,
第一类丢失更新(Update Lost):此种更新丢失是因为回滚的原因,所以也叫回滚丢失。此时两个事务同时更新count,两个事务都读取到100,事务一更新成功并提交,count=100+1=101,事务二出于某种原因更新失败了,然后回滚,事务二就把count还原为它一开始读到的100,此时事务一的更新就这样丢失了。
脏读(Dirty Read):此种异常时因为一个事务读取了另一个事务修改了但是未提交的数据。举个例子,事务一更新了count=101,但是没有提交,事务二此时读取count,值为101而不是100,然后事务一出于某种原因回滚了,然后第二个事务读取的这个值就是噩梦的开始。
不可重复读(Not Repeatable Read):此种异常是一个事务对同一行数据执行了两次或更多次查询,但是却得到了不同的结果,也就是在一个事务里面你不能重复(即多次)读取一行数据,如果你这么做了,不能保证每次读取的结果是一样的,有可能一样有可能不一样。造成这个结果是在两次查询之间有别的事务对该行数据做了更新操作。举个例子,事务一先查询了count,值为100,此时事务二更新了count=101,事务一再次读取count,值就会变成101,两次读取结果不一样。
第二类丢失更新(Second Update Lost):此种更新丢失是因为更新被其他事务给覆盖了,也可以叫覆盖丢失。举个例子,两个事务同时更新count,都读取100这个初始值,事务一先更新成功并提交,count=100+1=101,事务二后更新成功并提交,count=100+1=101,由于事务二count还是从100开始增加,事务一的更新就这样丢失了。
幻读(Phantom Read):幻读和不可重复读有点像,只是针对的不是数据的值而是数据的数量。此种异常是一个事务在两次查询的过程中数据的数量不同,让人以为发生幻觉,幻读大概就是这么得来的吧。举个例子,事务一查询order表有多少条记录,事务二新增了一条记录,然后事务一查了一下order表有多少记录,发现和第一次不一样,这就是幻读。
数据库事务隔离级别
数据库事务的并发操作既然会对数据的存取造成这么多影响,那该如何解决?上面各种异常情况都是多个事务之间相互影响造成的,这说明两个事务之间需要某种方式将他们从某种程度上分开,降低直至避免相互影响。这时候数据库事务隔离级别就粉墨登场了,而数据库的隔离级别实现一般是通过数据库锁实现的。
读未提交(Read Uncommitted):该隔离级别指即使一个事务的更新语句没有提交,但是别的事务可以读到这个改变,几种异常情况都可能出现。极易出错,没有安全性可言,基本不会使用。
读已提交(Read Committed):该隔离级别指一个事务只能看到其他事务的已经提交的更新,看不到未提交的更新,消除了脏读和第一类丢失更新,这是大多数数据库的默认隔离级别,如Oracle,Sqlserver。使用写锁(排他锁)来实现。加了写锁,就可以保证读的都是提交之后的数据,但是会造成不可重读,即读的时候不加锁,一个读的事务过程中,如果读取数据两次,在两次之间有写事务修改了数据,将会导致两次读取的结果不一致,从而导致逻辑错误。
可重复读(Repeatable Read):该隔离级别指一个事务中进行两次或多次同样的对于数据内容的查询,得到的结果是一样的,但不保证对于数据条数的查询是一样的,这也是MySql的默认事务隔离级别。在事务中加读锁来实现,在读的时候需要加锁并且保持。
串行化(Serializable):一个事务执行的时候不允许别的事务并发执行.完全串行化的读,只要存在读就禁止写,但可以同时读,消除了幻读。这是事务隔离的最高级别,虽然最安全最省心,但是效率太低,一般不会用。它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。
终于说到锁了,上文提到数据库使用锁来实现对并发事务的控制,下面来说说锁的分类及其机理。
数据库锁一般可以分为两类,一个是悲观锁,一个是乐观锁。
乐观锁一般是指用户自己实现的一种锁机制,假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。乐观锁的实现方式一般包括使用版本号和时间戳。对于读操作远多于写操作的时候,大多数都是读取,这时候一个更新操作加锁会阻塞所有读取,降低了吞吐量。最后还要释放锁,锁是需要一些开销的,我们只要想办法解决极少量的更新操作的同步问题。可以使用update … where … and version=”old version”这样的语句,根据返回结果是0还是非0来得到通知,如果是0说明更新没有成功,因为version被改了,如果返回非0说明更新成功。
悲观锁一般就是我们通常说的数据库锁机制,顾名思义,就是很悲观,它对于数据被外界修改持保守态度,认为数据随时会修改,所以整个数据处理中需要将数据加锁。悲观锁一般都是依靠关系数据库提供的锁机制,事实上关系数据库中的行锁,表锁不论是读写锁都是悲观锁。
悲观锁按照使用性质划分:
共享锁(Share locks简记为S锁):也称读锁,事务A对对象T加s锁,其他事务也只能对T加S,多个事务可以同时读,但不能有写操作,直到A释放S锁。
排它锁(Exclusivelocks简记为X锁):也称写锁,事务A对对象T加X锁以后,其他事务不能对T加任何锁,只有事务A可以读写对象T直到A释放X锁。InnoDB
引擎默认的修改数据语句,update,delete,insert
都会自动给涉及到的数据加上排他锁,select
语句默认不会加任何锁类型。如果加排他锁
可以使用select ...for update
语句,加共享锁
可以使用select ... lock in share mode
语句。
更新锁(简记为U锁):用来预定要对此对象施加X锁,它允许其他事务读,但不允许再施加U锁或X锁;当被读取的对象将要被更新时,则升级为X锁,主要是用来防止死锁的。因为使用共享锁时,修改数据的操作分为两步,首先获得一个共享锁,读取数据,然后将共享锁升级为排它锁,然后再执行修改操作。这样如果同时有两个或多个事务同时对一个对象申请了共享锁,在修改数据的时候,这些事务都要将共享锁升级为排它锁。这些事务都不会释放共享锁而是一直等待对方释放,这样就造成了死锁。如果一个数据在修改前直接申请更新锁,在数据修改的时候再升级为排它锁,就可以避免死锁。
悲观锁按照作用范围划分:
行锁:锁的作用范围是行级别,数据库能够确定那些行需要锁的情况下使用行锁,如果不知道会影响哪些行的时候就会使用表锁。
表锁:锁的作用范围是整张表。
找了张图来表示数据库各种锁之间的关系:
以上是关于面试经历的主要内容,如果未能解决你的问题,请参考以下文章
2021-12-24:划分字母区间。 字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。 力扣763。某大厂面试