精华内容
下载资源
问答
  • 操作系统经典问题

    千次阅读 2014-09-07 13:57:37
    操作系统经典问题
    1、什么是进程(Process)和线程(Thread)?有何区别? 

    进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。进程与应用程序的区别在于应用程序作为一个静态文件存储在计算机系统的硬盘等存储空间中,而进程则是处于动态条件下由操作系统维护的系统资源管理实体。线程是进程的一个实体,是CPU调度和 分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和 栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。比如父子进程间的全局变量是独立的,虽然采用了写时复制的策略;但是同一个进程中的线程共享该进程的全局变量,它们的操作相互影响,只有函数中的局部变量才有私有的

    2、进程调度的方法

    完全公平(CFS)调度算法、O(1)调度算法、先来先服务、短执行进程优先算法、最高优先级优先调度算法(核心是确定进程的优先级)、时间片轮转法

    3、进程同步的方法?

    进程间同步的主要方法有原子操作、信号量机制、自旋锁、管程、会合、分布式系统等。

    4、死锁在两个或多个并发进程中,如果每个进程持有某种资源而又都等待别的进程释放它或它们现在保持着的资源,在未改变这种状态之前都不能向前推进,称这一组进程产生了死锁。通俗地讲,就是两个或多个进程被无限期地阻塞、相互等待的一种状态。

    产生死锁的原因主要是(1) 因为系统资源不足。 (2) 进程运行推进的顺序不合适。(3) 资源分配不当等。


    产生死锁的四个必要条件

     (1) 互斥条件:一个资源每次只能被一个进程使用。

    (2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

    (3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。

    (4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

    这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。


    死锁的预防
    该策略旨在创造条件预防死锁。即破坏产生死锁的四个必要条件之一,就不会出现死锁,但是效果都不理想

    1. 互斥条件

    如果系统中的资源可以由多个进程共享,那么就永远不会发生死锁。然而,这种共享不切实际。例如,磁带机、绘图仪或打印机就不能在多个进程之间共享使用。

    2. 请求与保持条件

    方法一:进程一开始就声明它期望使用的全部资源,但显而易见的是,它不能达到预期效果而且很浪费。

    方法二:操作系统必须让请求某些资源的进程先放弃已经占用的资源,然后再尝试请求所有要用的资源。如果尝试成功,被放弃的资源才可以重新分配给该进程,这样该进程才可以继续运行。如果失败,被放弃的资源恢复空闲,而进程则必须一直等到那些资源可用为止。每次检查后,进程都放弃已占用的资源,这样就永远不会出现死锁。同样地,这种技术可用于表、信号量等共享资源,但不适用于打印机和磁带机这类资源。想象一下,某个进程在打印到一半的时候放弃使用打印机,而某个其他进程占用该打印机将产生什么样的后果。

    3. 非抢占条件

    确保不满足"非抢占"条件很困难。如果允许将资源分配给可以强制夺取该资源的进程,也许可以解决死锁问题,但会出现更糟糕的问题。从一个只处理了部分记录的进程强制性地夺走磁带机--因为其他进程请求使用该资源,由此带来的加载/卸载、定位等问题一定令人无法接受。

    4. 循环等待条件

    解决该问题的一个更好的方法就是对所有的资源编号,任何进程都必须在执行期间按照编号递增的顺序请求所需的资源,从而不会产生死锁。


    死锁避免系统对进程发出每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,如果分配后系统可能发生死锁,则不予分配,否则予以分配.这是一种保证系统不进入死锁状态的动态策略 。死锁避免和死锁预防的区别在于:死锁预防是设法至少破坏产生死锁的四个必要条件之一,严格的防止死锁的出现,而死锁避免则不那么严格的限制产生死锁的必要条件的存在,因为即使死锁的必要条件存在,也不一定发生死锁.死锁避免是在系统运行过程中注意避免死锁的最终发生.

    常用方法:

    1)银行家算法:每个进程申请资源时,系统采用银行家算法先进行判断,即假设我把这些资源分配给该进程,如果后序能找到一个安全区状态(即不会发生死锁的状态),就进行真正的分配,如果找不到,则不分配。具体参考这里

    2)鸵鸟算法:它假设出现死锁的概率很小,并且出现之后处理死锁会花费很大的代价,还不如不做处理,OS中这种置之不理的策略称之为鸵鸟算法。


    4、用户进程间通信主要哪几种方式?

    1)管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信。

    2)命名管道(named pipe):命名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建。

    3)信号(Signal:信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)。

    4)消息队列:消息队列是消息的链接表,包括Posix消息队列、system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺

    5)共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。

    6)信号量(semaphore:主要作为进程间以及同一进程不同线程之间的同步手段。

    7)套接字(Socket:更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:LinuxSystem V的变种都支持套接字。


    5、系统调用和库函数的区别

    (1)调用形式不同。过程(函数)使用一般调用指令,其转向地址是固定不变的,包含在跳转语句中;但系统调用中不包含处理程序入口,而仅仅提供功能号,按功能号调用。
    (2)被调用代码的位置不同。过程(函数)调用是一种静态调用,调用者和被调用代码在同一程序内,经过连接编辑后作为目标代码的一部份。当过程(函数)升级或修改时,必须重新编译链接。而系统调用是一种动态调用,系统调用的处理代码在调用程序之外(在操作系统中),这样一来,系统调用处理代码升级或修改时,与调用程序无关。而且,调用程序的长度也大大缩短,减少了调用程序占用的存储空间。
    (3)提供方式不同。过程(函数)往往由编译系统提供,不同编译系统提供的过程(函数)可以不同;系统调用由操作系统提供,一旦操作系统设计好,系统调用的功能、种类与数量便固定不变了。
    (4)调用的实现不同。程序使用一般机器指令(跳转指令)来调用过程(函数),是在用户态运行的;程序执行系统调用,是通过中断机构来实现,需要从用户态转变到核心态,在管理状态执行,因此,安全性好。


    6、内核同步(即并发访问)的方法

    1):中断屏蔽
    在单CPU范围内避免竞态的一种简单方法是在进入临界区之前屏蔽系统的中断。由于linux内核的进程调度等操作都依赖中断来实现,内核抢占进程之间的并发也就得以避免了。
    特点:由于linux系统的异步IO,进程调度等很多重要操作都依赖于中断,在屏蔽中断期间所有的中断都无法得到处理,因此长时间的屏蔽是很危险的,有可能造成数据丢失甚至系统崩溃,这就要求在屏蔽中断之后,当前的内核执行路径应当尽快地执行完临界区的代码。中断屏蔽只能禁止本CPU内的中断,因此,并不能解决多CPU引发的竞态,所以单独使用中断屏蔽并不是一个值得推荐的避免竞态的方法,它一般和自旋锁配合使用。
    2):原子操作
    定义:原子操作指的是在执行过程中不会被别的代码路径所中断的操作。
    3):
    自旋锁
    Linux内核中最常见的锁是自旋锁(spin lock),自旋锁最多只能被一个可执行线程持有,如果一个执行线程试图获得一个被争用(已经被持有)的自旋锁,那么该线程就会一直进行忙循环—旋转—等待锁重新可用,要是锁未被争用,请求锁的执行线程便能立刻得到它,继续执行,在任意时间,自旋锁都可以防止多于一个的执行线程同时进入临界区,注意同一个锁可以用在多个位置—例如,对于给定数据的所有访问都可以得到保护和同步。
    一个被争用的自旋锁使得请求它的线程在等待锁重新可用时自旋(特别浪费处理器时间),所以自旋锁不应该被长时间持有,事实上,这点正是使用自旋锁的初衷,在短期间内进行轻量级加锁,还可以采取另外的方式来处理对锁的争用:让请求线程睡眠,直到锁重新可用时再唤醒它,这样处理器就不必循环等待,可以去执行其他代码,这也会带来一定的开销——这里有两次明显的上下文切换,被阻塞的线程要换出和换入。因此,
    持有自旋锁的时间最好小于完成两次上下文切换的耗时,当然我们大多数人不会无聊到去测量上下文切换的耗时,所以我们让持 有自旋锁的时间应尽可能的短就可以了,信号量可以提供上述第二种机制,它使得在发生争用时,等待的线程能投入睡眠,而不是旋转。
    自旋锁可以使用在中断处理程序中(此处不能使用信号量,因为它们会导致睡眠),在中断处理程序中使用自旋锁时,一定要在获取锁之前,首先禁止本地中断(在 当前处理器上的中断请求),否则,中断处理程序就会打断正持有锁的内核代码,有可能会试图去争用这个已经持有的自旋锁,这样以来,中断处理程序就会自旋, 等待该锁重新可用,但是锁的持有者在这个中断处理程序执行完毕前不可能运行,这正是我们在前一章节中提到的双重请求死锁,注意,需要关闭的只是当前处理器上的中断,如果中断发生在不同的处理器上,即使中断处理程序在同一锁上自旋,也不会妨碍锁的持有者(在不同处理器上)最终释放锁。
    其实介绍的几种信号量和互斥机制,其底层源码都是使用自旋锁,可以理解为自旋锁的再包装。所以从这里就可以理解为什么自旋锁通常可以提供比信号量更高的性能。
    4):读写自旋锁
    如 果临界区保护的数据是可读可写的,那么只要没有写操作,对于读是可以支持并发操作的。对于这种只要求写操作是互斥的需求,如果还是使用自旋锁显然是无法满 足这个要求(对于读操作实在是太浪费了)。为此内核提供了另一种锁-读写自旋锁,读自旋锁也叫共享自旋锁,写自旋锁也叫排他自旋锁。
    5):顺序琐
    顺序琐(seqlock)是对读写锁的一种优化,若使用顺序琐,读执行单元绝不会被写执行单元阻塞,也就是说,读执行单元可以在写执行单元对被顺序琐保护的共享资源进行写操作时仍然可以继续读,而不必等待写执行单元完成写操作,写执行单元也不需要等待所有读执行单元完成读操作才去进行写操作。
    但是,写执行单元与写执行单元之间仍然是互斥的,即如果有写执行单元在进行写操作,其它写执行单元必须自旋在哪里,直到写执行单元释放了顺序琐。
    如果读执行单元在读操作期间,写执行单元已经发生了写操作,那么,读执行单元必须重新读取数据,以便确保得到的数据是完整的,这种锁在读写同时进行的概率比较小时,性能是非常好的,而且它允许读写同时进行,因而更大的提高了并发性,注意,顺序琐由一个限制,就是它必须被保护的共享资源不含有指针,因为写执行单元可能使得指针失效,但读执行单元如果正要访问该指针,将导致Oops。
    6):
    信号量
    Linux中的信号量是一种睡眠锁,如果有一个任务试图获得一个已经被占用的信号量时,信号量会将其推进一个等待队列,然后让其睡眠,这时处理器能重获自由,从而去执行其它代码,当持有信号量的进程将信号量释放后,处于等待队列中的哪个任务被唤醒,并获得该信号量。
    7):读写信号量
    类似于自旋锁,信号量也有读写信号量。首先要说明的是所有的读写信号量都是互斥信号量。读锁是共享锁,就是同时允许多个读进程持有该信号量,但写锁是独占锁,同时只能有一个写锁持有该互斥信号量。

    自旋锁和信号量区别

    在驱动程序中,当多个线程同时访问相同的资源时(驱动程序中的全局变量是一种典型的共享资源),可能会引发"竞态",因此我们必须对共享资源进行并发控制。Linux内核中解决并发控制的最常用方法是自旋锁与信号量(绝大多数时候作为互斥锁使用)。

    自旋锁与信号量"类似而不类",类似说的是它们功能上的相似性,"不类"指代它们在本质和实现机理上完全不一样,不属于一类。
    自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环查看是否该自旋锁的保持者已经释放了锁,"自旋"就是"在原地打转"。而信号量则引起调用者睡眠,它把进程从运行队列上拖出去,除非获得锁。这就是它们的"不类"。
    但是,无论是信号量,还是自旋锁,在任何时刻,最多只能有一个保持者,即在任何时刻最多只能有一个执行单元获得锁。这就是它们的"类似"。
    鉴于自旋锁与信号量的上述特点,一般而言,自旋锁适合于保持时间非常短的情况,它可以在任何上下文使用;信号量适合于保持时间较长的情况,却只能在进程上下文使用。
    如果被保护的共享资源只在进程上下文访问,则可以以信号量来保护该共享资源,如果对共享资源的访问时间非常短,自旋锁也是好的选择。但是,如果被保护的共享资源需要在中断上下文访问(包括底半部即中断处理句柄和顶半部即软中断),就必须使用自旋锁。至于信号量为什么不能用于中断上下文,是因为中断上下文不能睡眠。至于为什么不能睡眠简单的来说就是中断上下文不是一个进程上下文,其没有一个专门用来描述CPU寄存器等信息的数据结构,所以无法被调度器调度,中断(硬中断、软中断)处理都是些耗时不是很长,对实时性要求很高,执行频度较高的应用,所以,如果采用一个专门的后台daemon对其处理,显然并不合适。


    7、什么是中断?中断时CPU做什么工作?

    中断是指在计算机执行期间,系统内发生任何非寻常的或非预期的急需处理事件,使得CPU暂时中断当前正在执行的程序而转去执行相应的事件处理程序。待处理完毕后又返回原来被中断处继续执行或调度新的进程执行的过程。


    8、内存管理

    8.1:物理地址与虚拟地址

    8.2:虚拟内存

    为了扩充内存空间,引入了虚拟存储器(磁盘空间的一部分),可以将进程的虚拟地址空间映射到磁盘空间中,并且由页表记录映射位置,当访问到某个地址的时候,通过页表的有效位,可以得知数据是否在内存中,如果不是则通过缺页异常,将磁盘对应的数据拷贝到内存中,如果没有空闲内存,则选择牺牲页面,替换其他页面。
    事实上,在每个进程创建加载时,内核只是为进程创建了虚拟内存的布局,具体就是初始化进程控制表中内存相关的链表,实际上并不立即就把虚拟内存对应位置的程序数据和代码拷贝到物理内存中,只是建立好虚拟内存和磁盘文件之间的映射就好,叫做内存文件映射,等到运行到对应程序时,才会通过缺页异常来拷贝数据。还有进程在运行过程中,要动态分配内存,比如malloc时,也只是分配了虚拟内存,即为这块虚拟内存对应的页表项做相应设置,当进程真正访问到此数据时,才引发缺页异常。缺页异常的处理过程,就是把进程需要的数据从磁盘上拷贝到物理内存中,如果内存已经满了,没有空地方了,那就找一个页覆盖,当然如果被覆盖的页曾经被修改过,需要将此页写回磁盘

    好处:

    1)既然每个进程的内存空间都是一致而且固定的,所以链接器在链接可执行文件时,可以设定内存地址,而不用去管这些数据最终实际的内存地址,这是有独立内存空间的好处

    2)当不同的进程使用同样的代码时,比如库文件中的代码,物理内存中可以只存储一份这样的代码,不同的进程只需要把自己的虚拟内存映射过去就可以了,节省内存

    3)在程序需要分配连续的内存空间的时候,只需要在虚拟内存空间分配连续空间,而不需要实际物理内存的连续空间,可以利用碎片。

    8.3:内存映射文件

    虚拟内存使用物理内存或者交换区来保留和提交一个地址空间的区域,而在内存映射文件中,使用的是一个位于磁盘上的文件来映射一个进程的地址空间区域,一旦映射文件,就可以访问这个文件,如同已经把这个文件加载到内存。
    内存映射文件文件可以用于3个目的:
    a、系统使用内存映射文件来加载和执行可执行文件和动态链接库,这样可以大大节省页面文件空间以及应用程序启动运行所需要的时间。
    b、使用内存映射文件来访问磁盘中的数据文件,这样就不必对文件执行I/O操作,并且不必对文件进行缓存。
    c、使用内存映射文件,在同一台机器上运行的多个进程之间共享数据。windows也提供了其他在进程之间进行通信的方法,不过这些方法都是使用内存映射文件来实现的,这使得内存映射文件成为在单个计算机上的多进程之间进行通信的最好方法。
    8.4:进程的虚拟地址空间
    每个进程都有自己的虚拟地址空间,32位的是4GB,64位的是16EB。每个进程都拥有自己私有的地址空间,其他进程的内存地址空间被系统隐藏,例如:进程A和B可以在地址0x12345678中存放自己的数据结构,当A访问0x12345678地址时访问的是A的数据结构,B访问地址0x12345678时使用的是B的数据结构
    8.5:分段和分页的区别
    页是信息的物理单位,分页是为实现离散分配方式,以消减内存的外零头,提高内存的利用率;或者说,分页仅仅是由于系统管理的需要,而不是用户的需要。段是信息的逻辑单位,它含有一组其意义相对完整的信息。分段的目的是为了能更好的满足用户的需要。页的大小固定且由系统确定,把逻辑地址划分为页号和页内地址两部分,是由机器硬件实现的,因而一个系统只能有一种大小的页面。段的长度却不固定,决定于用户所编写的程序,通常由编辑程序在对源程序进行编辑时,根据信息的性质来划分。分页的作业地址空间是一维的,即单一的线性空间,程序员只须利用一个记忆符,即可表示一地址。分段的作业地址空间是二维的,程序员在标识一个地址时,既需给出段名,又需给出段内地址。

    9:虚拟文件系统
    虚拟文件系统作为内核子系统,为用户空间应用程序提供了文件和文件系统相关的接口。通过虚拟文件系统,程序可以利用标准的UNIX系统调用对不同的文件系统,甚至不同介质上的文件系统进行读写操作。比如:应用程序调用write(fd,buf,len)系统调用,这个系统调用首先被VFS的通用系统调用sys_write()处理,sys_write()要先找到fd所在的文件系统实际给出的是哪个写操作,然后再调用该文件系统的特殊的写操作进行真正的写动作。

    展开全文
  • 算法经典问题整理

    千次阅读 2018-02-06 10:11:09
    当时备考整理了一下各种思想下的经典问题,如今在这里认真做一下总结和回顾,力求一句话说清楚每个问题,找到每个算法中那个小小的闪光内核。 分治 分治的策略就是大事化小,特别要注意观察找出与原问题同质的子...

    一学期的算法课结束了,整个过程感觉就像在做益智小游戏,特别有趣,我也在这个过程中拓展了思维,受益匪浅。
    当时备考整理了一下各种思想下的经典问题,如今在这里认真做一下总结和回顾,力求一句话说清楚每个问题,找到每个算法中那个小小的闪光内核。


    分治

    分治的策略就是大事化小,特别要注意观察找出与原问题同质的子结构。

    1.归并排序
    一半一半地分治,排好序的左右两半以O(n)合并到一起。
    2.K-th Number:找到一个序列中第K大/小的数
    从序列中选一个目标数,把序列分成比小于目标数、等于目标数和大于的三部分,此时比较三部分中数的个数可知第K个数落在哪部分。
    3.整数乘法:两n位整数相乘要做n^2次一位的乘法,找一个更高效的做法
    两乘数都分为左右两部,可写成(Xl+Xr)(Yl+Yr)的形式,这里面的四次乘法可以化简为三次。
    4.矩阵乘法:两n*n矩阵相乘复杂度为n^3,找一个更高效的算法
    类似地每个矩阵分解为[[A B][C D]]四块,易得用8个子矩阵乘积表示的结果,而经过精细复杂的代数分解,可以降到7个。
    5.快速傅里叶变换:两d次多项式做乘法复杂度为d^2,找一个更高效的算法
    乘积为2d次的多项式,可由2d+1个点唯一确定。如C(x) = A(x) * B(x),选好2d+1个x后,分别算出A(xi)和B(xi)对应相乘即可得到2d+1个C(xi),最后插值得到C的多项式。其中,对多项式做变形发现求得A(xi)则易知A(-xi),因此把问题规模降了一半。
    这里只说清楚了算法框架,毕竟十大算法之一,具体的实现细节也浸透着算法的智慧


    贪心

    这种思想特别贴合人类最朴素思维。我在往贪心上想的时候往往会问自己,如果不是机器做解题,而是人来解那会往哪个方向贪?最关注的点是什么?不过也是特别容易陷进死胡同。

    1.课程规划:给一些课程,时间可能冲突,怎样尽量多上一些课(忍不住要吐槽,明明大家都是尽量少上课多赚学分0.0)
    有几个版本,详见我之前的博客
    2.最小生成树:选出一些边构成树,要包含所有顶点且边的和最小
    Prim:从起始点开始,从连接选中点集和剩余点集的边中选一条最短的,然后将该边对应的新点加进选中点集中,直到选中所有点。
    Kruskal:对边排序,从最短的开始,只要不会使得当前选中的子图成环,那么就选择这条边,直到选中所有点。


    图论

    图论呢,要说它是一种算法思想,那大概是指将问题抽象为图的表示这点。在读题的时候忍不住要在纸上画一些点,以及连一些线来表示点之间的联系的,基本上可以转化为图没跑了。
    而要说图论是一类问题,它涵盖面又很广,上面的最小生成树也是图论中的问题。作为一个独立的数学分支,我一个小菜鸟来看,目前接触的算法都是被前人仔细研究然后确定下来了的,也就是转化好问题之后有固定的套路可以用,不像其他算法有很大活用的空间。
    最基本的遍历算法BFS、DFS我就不多说了。

    基于dfs引出图的分解

    1.拓扑排序
    dfs访问完节点的顺序是拓扑序逆序。
    也有基于入度为0的解法
    2.找强连通分量:一个强连通分量内部任意两个顶点互相可达
    Kosaraju:先dfs遍历,记录访问完成时间;然后按时间降序dfs遍历反向图,得到的每个联通块就是一个SCC。
    Tarjan:每个SCC对应dfs中的一棵子树,回溯可达父节点说明与父节点在同一SCC。

    基于bfs引出路径问题

    路径问题又有很多分类,像最短/长路(下面又有细分以及各种解法)、欧拉/汉密尔顿回路等等,学得不好,就不展开了,只讲两种最基本的单源最短路算法
    1.Bellman-Ford:每个点到源点的距离在bfs的过程中更新,遍历到一个点就检查是否有边能使得距离更新,即对于e(u, v),Distant[u] + w(u, v) < Distant[v]。
    2.Dijkstra:每次从可达点中找一个最近的,并试着更新这个点的邻居的最短距离。


    动态规划

    这是一很强大的算法,适用范围广,老师也喜欢考。做的练习比较多,所以就多罗列了一些问题,力求涵盖各种题型。
    动态规划是最讲套路的了,关键就是那个状态转移方程。尝试着定义子状态dp[i],赋予它一定含义,顺着题目要求看看状态如何转移。这个定义行不通就换一个,一维状态不够就加一维,题目见多了就会熟能生巧。下面对各种题型做了简单的分类,相同题型的状态转移方程有相似性。

    单序列前i

    1.最长递增子序列:求其长度
    给每个数维护一个L值,表示到这个数为止能找到的最大长度。
    优化方案是维护一个最优秀的递增子序列(最终找到的结果)新数和这一列最末的数比较
    2.DAG上的最短/长路
    拓扑排序然后Bellman-Ford。

    单序列i到j

    1.矩阵连乘:找到一个计算顺序(使用结合律)使得矩阵链乘所做乘法次数最少
    最优的下一状态可以表示为一个最优分割,最外层循环是子问题规模。
    优化方案是用到 记忆化搜索

    双序列

    1.编辑距离:允许插入、删除、替换操作,求把一个字符串变成另一个字符串的操作步数
    在三种操作对应的代价中取最小作为下一状态值。
    2.最长公共子序列:可不连续
    dp[i][j]为两个序列前面一段的最长公共长度。
    3.最长公共子串:连续
    dp[i][j]表示子串必须以i、j结尾,仍然保留这条转移方程dp[i][j] = dp[i-1][j-1] + 1。

    加条件

    1.最短可靠路径:规定路径跳数不超过k
    定义dist(v,i)为从源到v跳数i的最短路径长度。
    2.01背包问题:不超过容量使总价值最大,物品只有一件选或不选
    定义f[i][v]为前i件物品放进背包获得的最大值,只要放得下,第i件在前i-1的基础上要么不放,要么容量减少价值增加。
    3.重复背包问题:物品可以取任意多件
    f[v]表示N种物品放入一个容量为v的背包可以获得的最大价值,f[v]=max{ f[v-c[i]]+w[i] | v>=c[i], i=1, 2, …, N}。
    4.二维背包问题:除了体积,选择某种物品还多了一种代价——多维背包类似
    费用加了一维,只需状态也加一维即可。f[i][v][u]表示前i件物品付出两种代价分别为v和u时可获得的最大价,f[i][v][u]=max{ f[i-1][v][u], f[ i-1 ][ v-a[i] ][ u-b[i] ] + w[i] }。
    5.分组背包:一组内最多选一件物品
    每组物品有若干种策略:是选择本组的某一件,还是一件都不选。设f[k][v]表示前k组物品花费费用v能取得的最大权值,f[k][v]=max{ f[k-1][v], f[k-1][v-c[i]]+w[i] | 物品i属于组k}。

    1.树中的最小点覆盖:覆盖是指每条边的两个顶点至少有一个被选中,要求选中点最少
    用ans[i][0]表示在不选择结点i的情况下,以i为根的子树,最少需要选择的点数;ans[i][1]表示选的。选了根孩子们可选可不选——挑一种少的;不选根孩子们都要选。
    可转化为最大独立集和最大团问题

    其他

    1.所有顶点间的最短路径
    定义dist(i, j, k)为从i到j只经过1,2…k的最短路,则可以从子状态(i, j, k-1)转移过来。跑三层循环得到结果,最外层为k。
    或者调用|V|次Bellman-Ford
    2.带权有向图的最长简单路径:不走重复节点
    ans[j][i] = max(graph[j][k] + ans[k][s])表示j经过点集i中的点一次的路径最长值,k是i中挑出的一个点,s=i-k。
    有一个状态压缩的小技巧

    展开全文
  • 电商项目中的经典问题

    万次阅读 多人点赞 2018-04-26 15:39:24
    【回答技巧】 从3个方面来回答这个问题: |--系统背景及系统概述 |--系统包括的业务模块及主业务流程 |--责任模块【回答示例】 第一个方面:系统背景及系统概述优购时尚商城是香港上市公司百丽国际公司为拓宽旗下...

    请描述一下这个系统?

    【回答技巧】

    从3个方面来回答这个问题:

    |--系统背景及系统概述

    |--系统包括的业务模块及主业务流程

    |--责任模块

    【回答示例】

    第一个方面:系统背景及系统概述

    优购时尚商城是香港上市公司百丽国际公司为拓宽旗下运动品牌服饰市场而开发的一个专业销售购物网站户外运动装备的网站。

     

    第二个方面:系统包括的业务模块及主业务流程

    改项目分为前台和后天2大模块:

    前台又包含:全部商品分类、运动馆、户外馆、鞋靴馆、女装馆、男装馆、母婴馆、生活馆、会员中心、秒杀、闪团、INNET运动鞋、潮流馆。每一个分类都是对该范围类商品的一些具体分类以及明星商品的展示、新品展示、品牌连接等等。前台还包含用户的登录注册、我的优购、我的订单、公告等模块,主要用于客户下单。结算、收货等一系列购物操作。以及个人中心的个人信息、订单信息的查看和维护。

    后台后台包含:商品管理、订单管理、代客下单、支付管理、广告管理、合作伙伴管理、会员管理、权限管理、系统配置、报表管理等模块。

     

    第三方面:责任模块

    这个系统我主要负责的是商品管理模块的CRUD以及商品的属性管理、商品的上下架、品牌管理、订单管理

    前台主要负责商品的主页面展示、商品的筛选、商品详情展示。在商品详情页面采用freemarker页面静态化技术。降低服务器压力减少对服务器的I/O。商品详情页实现了添加购物车和结算功能。购物车根据与项目经理协商采用cookie技术实现。【此处可以加入几种保存方案:1.保存数据库{问题是造成数据库压力倍增} 2.使用cookie{用户更换电脑或浏览器添加购物车商品丢失,但是此网站不考虑}  3.最好解决方案保存到redis数据库。前两个问题全部解决】。提交订单功能可能出现并发问题。原因在于大量用户都可能购买。库存出现不足问题。这里可以插入自己对数据库的理解:锁机制【悲观所:线程等待。效率太低。乐观锁:解决了悲观所的效率问题。可选。但是update语句本身就带锁。所以不用加锁就能解决这个并发修改库存问题】,前台我还负责了个人中心的模块。包括:我的订单、退换货订单、我的收藏、个人资料、收货地址等功能模块。

    说说系统的架构?

    本系统使用maven进行构建,

    将系统分成了技术基础架构模块、前台工程模块、后台工程模块、主工程模块、文件系统工程模块。

    扩展问题:

    Spring在系统中如何使用?

    Spring对控制层、业务层、持久层的bean进行统一管理。

    对控制层的action,通过@controller注解,自动组件扫描方式将action在spring容器注册。

    对业务层的service,在spring配置文件进行配置,好处是方便系统开发与维护。Spring对业务层进行事务控制。

    对持久层的mapper,通过spring与mybatis整合包的mapper扫描器自动扫描编写的mapper。

     

    本系统如何用maven开发?

    本系统采用maven进行模块划分,将系统分成了核心基础架构模块、前台工程模块、后台工程模块、主工程模块、文件系统工程模块

     

    核心基础架构模块:

    此模块包括了对spring、mybatis的配置信息

    pom.xml中配置jar包的依赖,方便统一管理本项目的jar包依赖。

    前台和后台所有业务和数据访问层的编码实现。方便子模块重复调用。

     

    前台工程模块:

    此模块包括系统前台所用到所有controller。负责调用核心基础模块中的业务方法进行前台数据展示

     

    后台工程模块:

    此模块包括系统后台所用到所有controller。负责调用核心基础模块中的业务方法进行商家的后台管理

     

    主工程模块:

    此模块是将各各子模块进行聚合,最终生成一个war包。

     

    文件系统工程模块:

    次模块用于保存系统的所有文件。包括商品图片、报表模板、企业公告等文件。用于分散服务器I/O提高项目访问效率。

     

    扩展问题:

    maven仓库是怎么构建?

    实际开发在局域网中公司创建了一个maven私服,私服上存放系统所用到的jar包。

    本系统实现国际化了吗?是怎么做?

    本系统没有实现国际化,本系统是具有中国特色的电商项目,不需要实现国际化

    这个系统mybatis是怎么用的?或这个系统持久层如何实现的?

    1、mybatis框架

    使用mybatis官方出的mybatis与spring整合包将mybatis和spring整合。

     

    针对单表的增、删、改、查使用mybatis官方提供的逆向工程,根据数据库表的结构生成mybatis当中的mapper.xml、mapper.java、po及相关的类。在service中直接使用自动生成的mapper接口。

     

    针对自动生成的mapper无法满足业务需求时,自定义mapper,一般情况下多表查询需要自定义mapper。

     

    Mapper开发完成通过mybatis与spring整合包中的mapper扫描器将mapper在spring容器中进行注册。

     

     

     

    扩展问题:(回答问题切莫问什么答什么。自己要发散思维。扩展的回答面试问题)

    1、 mapper的扫描器是如何使用的

    在spring配置文件配置mapper扫描器,配置项指定扫描的包的路径。

    使用扫描器需要遵循一些mapper编写规则:

    Mapper.xml和mapper.java在同一个目录,且文件名相同。

    Mapper.xml中的statement 的id和mapper.java中的方法名一致。

    Mapper.xml中的statement的parameterType和mapper.java中的方法输入参数一致。

    Mapper.xml中的statement的resulttype和mapper.java中的方法输出结果类型一致。

    这个系统springmvc是怎么用的?

    使用springmvc注解开发。

     

    1、 对于页面展示类的方法,控制器方法返回string ,string就是逻辑视图名。

    2、 对于提交类的方法,控制器方法返回json数据,使用@Responsebody注解将action方法的返回值转为json输出。

    Responsebody注解内部使用jackson将java对象转为json

    本系统ajax+json具体是怎么做的?action的方法返回的json是如何实现的?

     

    ajax+json:页面采用ajax提交,服务端返回是json,

     

    页面提交统一采用ajax Form提交方式,使用了jquery提供form组件,在开发时和原始form的post 提交方法配合使用,使用jquery Form组件更能简化开发提高用户体验和开发效率。

     

    扩展问题:

    系统哪些地方使用到了json?

    1、 图片上传返回的相对和绝对路径。方便图片回显和URL保存。

    2、 购物车中的最小销售单元的数据保存

    3、 权限列表使用json数据表示。

    4、 个人中心的省市县联动数据使用json返回

    使用json目的:使用json方便客户端页面解析数据。

    这个系统的用户认证是怎么实现的?

    使用用户名密码认证方式。

     

    1、 用户认证

    2、 用户身份校验

    用户身份校验使用springmvc提供拦截器完成。

     流程:

     

    公开权限:用户不需要登录就访问地址。在单独的配置文件进行配置。比如系统登录页面。

     

     

     

    电商项目介绍

    电商行业的发展

     

    从截图我们可以发现,市场需求推动了电商的飞速发展,而且我们应该坚信电商会一直火下去,为了“钱途”我们必须学好这个项目!

    电商行业技术特点

     

    ①技术新:(NoSql推广首在社区网站和电商项目),发展快,需求推动技术的革新。

    ②技术范围广:除了java,像淘宝前端还使用了PHP,数据库MySQL或者oracle,nosql,服务器端使用Linux,服务器安全、系统安全

    ③分布式:以前是在一台机器上做运算,现在是分散到很多机器上,最后汇总起来。(集中式向分布式进行考虑)由需求来推动

    ④高并发、集群、负载均衡、高可用:由并发问题采用集群进行处理,其中,集群会涉及服务器的主从以及分布问题,使用负载均衡。(权重高低)高可用是对用户而言,用户的服务不中断(系统升级,服务不中断,淘宝每周更新2次)。

    ⑤海量数据:双11,570亿的背后,订单有多少?浏览次数有多少?商品会有多少?活动相关数据?

    ⑥业务复杂:不要简单的认为是:商品展示出来后,加入购物车后购买就完成了。后台特别复杂,比如优惠(包邮、满减)

    ⑦系统安全:系统上线必须通过系统安全部门审核通过。前年CSDN数据泄露。快捷酒店数据泄露(通过身份证就可以查看你的开房记录)。近几年,安全意识逐步在提高。

    电商行业的一些概念

     

    B2C:商家对客户,京东、当当、发展为B2C平台,天猫(B2C平台淘宝商城由马云提出,率先发展为平台),1号店也是(在上海)

    B2B:商家对商家,阿里巴巴(不零售,只批发,淘宝很多商家都会去阿里巴巴进货);

    C2C:个人对个人,淘宝市场,淘宝,QQ商城;

    系统功能

     

    本商城系统是一个综合性的B2C平台,类似京东商城、天猫商城。

    会员可以在商城浏览商品、下订单,以及参加各种活动。

    商家可以在入住淘淘商城,在该平台上开店出售自己的商品,并且得到淘淘商城提供的可靠的服务。

    管理员、运营可以在平台后台管理系统中管理商品、订单、会员等。

    客服可以在后台管理系统中处理用户的询问以及投诉。

    思维导图

     

    说明:

    上图中,数字标号为功能模块开发优先级,鉴于本系统搜索模块使用的技术点Lucene,由2级别提升为1级别。期初的电商项目是没有购物车功能的(不同商家发货,付款方式也多样化,这样过于复杂)

    上述功能不要强行记忆,以商品为中心进行发散理解记忆!如下图

     

    首先我们要有商品,管理员可以在系统中管理商品,用户可以查看商品。

    商品多了之后要有类目模块,管理员可以管理类目信息,用户可以根据类目检索商品。

    有了商品之后,要有人(会员)去买东西,普通用户注册为会员,会员可以登录到系统管理自己的信息(密码等)

    买了之后会生成订单,会员可以购买商品并且可以下单,管理员可以管理订单。

    有了订单之后需要支付(在线支付/货到付款)

    这样,我们就可以把整个电商项目的功能记清楚了!

    分布式系统架构

    分布式系统架构


    传统架构

     

    各个系统说明:

    后台管理系统:管理商品、订单、类目、商品规格属性、用户管理以及内容发布等功能。

    前台系统:用户可以在前台系统中进行注册、登录、浏览商品、首页、下单等操作。

    会员系统:用户可以在该系统中查询已下的订单、收藏的商品、我的优惠券、团购等信息。

    订单系统:提供下单、查询订单、修改订单状态、定时处理订单。

    搜索系统:提供商品的搜索功能。

    单点登录系统:为多个系统之间提供用户登录凭证以及查询登录用户的信息。

    谈到分布式架构,我们必须对比传统架构才能彰显其优势。

    最为明显的一点,在传统的架构中,如果某个功能需要进行维护,那么我们必须停掉整个服务,这对于公司的运营会造成损失。分布式系统在核心功能模块使用单独服务器,维护部分模块不影响用户的其他操作。

    在海量数据处理方面,传统架构显得比较乏力;分布式系统架构采用服务器集群,使用负载均衡,海量数据处理游刃有余!

    在性能(检索)以及维护方面,分布式系统架构也有较为明显的优势。

    本系统人员配置情况

    产品经理:3人,确定需求以及给出产品原型图。

    项目经理:1人,项目管理。

    前端团队:3人,根据产品经理给出的原型制作静态页面。

    后端团队:20人,实现产品功能。

    测试团队:3人,测试所有的功能。

    运维团队:2人,项目的发布以及维护。

    开发流程

     

    后台开发环境

     

    需要注意,在几个环境中,预发布环境和生产环境几乎一样,开发环境和测试环境是独立存在的,每一个阶段的活是由对应的工作人员来负责的。

    此外,我们还需要熟悉 SVN主干、分支、标签开发过程流程:

     

    整个开发沿着主线进行,在一个版本定型后,前一个版本出现bug,那么此时会对bug进行修复,投入使用的依旧是之前定型的那个版本,待前一个版本的bug修复好了之后,会定义一个新的版本,主线不会改变,如果改动不大,那么只需修订问题,继续沿用此版本(1.0.x),只有出现较大改动时,才会升级一个新的版本号(1.1.x)。所有动作,交替进行,沿主线向前推进!

    涉及技术

    Spring、SpringMVC、Mybatis

    JSP、JSTL、jQuery、jQuery plugin、EasyUI、KindEditor(富文本编辑器)、CSS+DIV

    Redis(缓存服务器)

    Lucene、Solr(搜索)

    httpclient(调用系统服务)

    Mysql

    Nginx(web服务器)

    Quartz(定时任务)

    RabbitMQ(消息队列)

    开发工具和环境

     Eclipse 4.4.1

     Maven 3.2.3

     Tomcat 7.0.47(Maven Tomcat Plugin)

    JDK 1.7

    Mysql 5.6

     Nginx 1.5.1

     Redis 2.8.9

     Win7 操作系统

     SVN(版本管理)

     

    需要注意所有开发工具的版本号要对应(一致),否则会出现冲突问题,面试大忌!(常识:电商项目火起来的时间不是很久,在开发的时候,很多开发工具和环境不像之前那些传统系统那么老旧,这点一定要引起注意!)

     

    电商面试题

    2.1 说说你最近做的这个项目的背景,简单的介绍一下你这个项目?

    背景

    电商项目的背景一般是由市场推动的,比如行业竞争或者经营方式的改变(营销理念)竞争的形态也发生了巨大的变化,从以产品、价格为主的竞争转向以服务为主的竞争,服务成为主导竞争格局的重要因素。渠道作为企业完成客户沟通、产品/服务交换过程以及实现价值、产生效益的重要载体,发挥了采集、传达客户和竞争对手等市场信息,为买卖双方提供便利,协调供需矛盾,为客户提供合适的产品与服务,向客户传递产品/服务信息,实现营销/服务目标等重要的功能。

    XXX商城之前主要以实体店为主,进行批发与零售。业务也相对比较传统,为了提升业务绩效,增强客户满意度和粘性,另一方面,也为基于互联网的商务模式创新奠定基础。针对上述行业环境变化和业务战略目标,xxx商城网上终端预约销售基础上,即将启动网上商城建设项目,用于建立网上终端、营销案在线销售及相关辅助功能,包含商品管理、订单管理、类目管理、客户管理、合作商管理、客服管理、购物平台、内容管理等,很大程度上分担了人工的压力,对提高客户服务效率和客户满意度能够起到较好作用。基于此,XXX公司提出建设网上商城建设项目工程。

    项目介绍

    xxx商城项目打造的是社区+电商的模式,用户不只是在社区中有自己的圈子,还可以将电商加入到社区中,整个电商网站实现的功能非常之多,采用分布式的架构设计,包括后台管理、前台系统、订单系统、单点登录系统、搜索系统、会员系统等。
    该项目是自己公司的产品,我们公司负责整个网站的运营,属于B2C平台;
    系统的用途,主要是提供B2C的平台,其中自营商品也有商家入住,类似天猫。
    系统架构,采用分布式的系统架构,其中前台系统和单点登录系统采用了集群的方式部署,在后台管理系统中采用了Maven的多模块化的管理,其中采用了水平切分的方式,将pojodaoserviceweb分层开发,这样做的好处就是可以重用性更高。

    系统内部接口调用采用Httpclient,并且使用Httpclient的连接池技术,接口提供端采用RESTful方式的接口定义;

    系统之间的通知机制采用MQ的方式,使用RabbitMQ的实现,使用了RabbitMQ的消息订阅模式的消息机制;

    系统的接口还对JS的跨域做了支持,采用了jsonp的解决方法,在后台接口中扩展了spirng提供的jackson数据转化器实现;

    部署方面,采用了Nginx+tomcat的模式,其中nginx的作用一方面是做反向代理、负载均衡、另一方面是做图片等静态资源的服务器。

    2.2 整个项目的架构如何?

    一般情况这个问题要从两个方面去回答,从技术架构和功能架构去谈。

    技术架构:本项目使用市场上较为主流的框架spring+springmvc+mybatis进行开发,采用分布式的系统架构,前台系统和单点登录系统采用了集群的方式部署,后台管理系统中采用了Maven的多模块化的管理,其中采用了水平切分的方式,将pojodaoserviceweb分层开发,这样做的好处就是可以重用性更高。系统内部接口调用采用Dubbo在后台接口中扩展了spirng提供的jackson数据转化器实现;搜索系统使用了solr实现,在保证系统高性能的前提下,尽可能为公司节约成本,我们使用MySQL数据库进行集群(oracle收费)。在部署方面,采用了Nginx+tomcat的模式,其中nginx的作用一方面是做反向代理、负载均衡、另一方面是做图片等静态资源的服务器。

    功能架构:分布式系统架构

     

    各个系统说明:

    后台管理系统:管理商品、订单、类目、商品规格属性、用户管理以及内容发布等功能。

    前台系统:用户可以在前台系统中进行注册、登录、浏览商品、首页、下单等操作。

    会员系统:用户可以在该系统中查询已下的订单、收藏的商品、我的优惠券、团购等信息。

    订单系统:提供下单、查询订单、修改订单状态、定时处理订单。

    搜索系统:提供商品的搜索功能。

    单点登录系统:为多个系统之间提供用户登录凭证以及查询登录用户的信息。

    谈到分布式架构,我们必须对比传统架构才能彰显其优势。

    最为明显的一点,在传统的架构中,如果某个功能需要进行维护,那么我们必须停掉整个服务,这对于公司的运营会造成损失。分布式系统在核心功能模块使用单独服务器,维护部分模块不影响用户的其他操作。

    在海量数据处理方面,传统架构显得比较乏力;分布式系统架构采用服务器集群,使用负载均衡,海量数据处理游刃有余!

    在性能(检索)以及维护方面,分布式系统架构也有较为明显的优势。

    传统架构

     

    这个项目为用户提供了哪些服务?包括哪些功能?

    商品管理模块:其中包括品牌管理,属性管理商品录入/上下架管理,商品添加审核,静态页面发布

    订单模块:其中包括使用activiti工作流订单的查询和订单的流转,定时作废

    商品前台首页:其中主要负责首页商品列表筛选,首页上动态展示筛选条件,点击每一个筛选条件下面的商品列表要做联动

    单品页面:采用freemarker来实现页面静态化,展示商品详情信息和商品购买,该页面采用静态化以减轻系统压力,使用了cxf框架发布服务

    提交订单页面:提交用户的订单信息处理并发问题。

    个人中心:包括用户的登录,个人信息的管理,收货地址的管理,用户所下的订单的管理

    购物车:把购物车的信息存在cookie里面管理

    你承担这个项目的哪些核心模块?

    在项目中主要负责相关系统的开发,主要有:

    后台管理系统:主要实现商品管理、商品规格参数管理、订单管理、会员管理等、CMS(内容管理系统)等,并且提供了跨域支持;

    前台系统:主要是面向用户访问,使用Httpclient和后台系统接口做交互,并且该系统在部署上采用集群的方式;

    单点登录系统:主要是提供集中用户登录凭证的集中解决方案,提供和用户信息相关的接口:比如说用户注册、查询等接口。

    订单系统:主要是提供和订单相关的业务接口,在订单系统了做了严格的数据校验以及高并发写的支持(这里可以说使用队列实现),并且使用了Quartz定时任务实现对订单的定时扫描,比如说关闭超时未付款的订单;

    搜索系统:主要是提供商品的搜索,采用开源企业级系统Solr实现,采用了MQ机制保证了商品数据可以及时同步到solr中;

    会员系统:主要是维护用户的信息,已购买订单、优惠券、系统消息、修改密码、绑定手机等功能;

    缓存:主要是用Redis实现,并且对Redis做了集群来保证Redis服务的高可用。

    支付系统:,主要是负责订单的支付、对账等功能,主要是对接了支付宝的接口;

    这些模块的实现思路说一下?

    ①商品管理模块

    品牌管理:
    主要掌握的核心是品牌的添加
    l 图片服务器的搭建,
    l 上传图片到图片服务器
    l 表单的验证,离开焦点的验证,点击完成时的验证,后台服务器的ajax验证,表单规范
    l 防止表单的二次提交
    l 编辑时不需要修改品牌名,区别readOnlydisabled

    l 删除时的二次确认
    注意:
    自定义属性必须掌握:html中自定义的属性可以帮助索引元素
    图片服务器的搭建
    1. 创建一个mavenweb工程,在工程中创建一个存放资源的目录
    2. tomcatweb.xmlDefaultServlet的只读属性改成false
     
    3. 编写上传到图片服务器的代码
    由于应用服务器与图片服务器出于不同的两台机器之中,所以可以提高系统的性能,能起到负载均衡的作用。上传图片时使用Jersey 客户端 API 调用 REST 风格的 Web 服务 Jersey 1 是一个开源的、可以用于生产环境的 JAX-RS(RESTful Web Services 的 Java API 规范,JSR-311)实现。通过 Jersey 可以很方便的使用 Java 来创建一个 RESTful Web Services。

    byte[] fileByte = commFile.getBytes();

    //创建客服端,基于webservice

    Client client = Client.create();

     

    //指定资源路径

    WebResource webResource = client.resource(Constants.picPath+fileName);

     

    //使用put的请求方式把资源文件放到资源服务器上

    webResource.put(String.class, fileByte);

    前台使用ajax提交表单,需要使用jqueryjquery.form.js插件

    $("#form").ajaxSubmit({

        url:url,

        type:"post",

        dataType:"text",

     

        data:{

            ...

        },

     

        //beforeSubmit:validate,

        success:function(responseText){

            var obj = $.parseJSON(responseText);

        },

     

        error:function(){

     

        }

    });

    4. 图片服务器中Upload文件夹中随便建立一个文件,防止空文件夹的情况下发布后upload消失

    表单验证

    1. 提交时做验证
        做好约定,每个文本中设置reg属性和tip自定义的属性,reg存放正则表达式,tip中存放不合法时的提示信息,还有品牌名称重复的验证。
    reg2,tip属于自定义的属性,这种定义方式方便使用jquery的属性选择器

    <p>

        <label><samp>*</samp>品牌名称:</label>

        <input type="text" id="brandName" name="brandName" class="text state" reg2="^[a-zA-Z0-9\u4e00-\u9fa5]{1,20}$" tip="必须是中英文或数字字符,长度1-20"/>

        <span></span>

    </p>

    2. 在表单提交时做验证使用$(“form”).submit(function(){ return false });,必填字段和非必填的字段需要区别对待

    $("#form111").submit(function(){

        var isSubmit = true;

        $(this).find("[reg2]").each(function(){

            var regStr = $(this).attr("reg2");

            //剪掉值中的两侧的字符串

            var value = $.trim($(this).val());

            var tip = $(this).attr("tip");

            //创建正则表达式的对象

            var reg = new RegExp(regStr);

            if(!reg.test(value)){

                $(this).next("span").html(tip);

                isSubmit = false;

                //跳出循环,jqueryeach语句之中跳出循环使用return false; 如果在原生js里面可使用break;, return;:代表终止执行程序

                return false;

            }

        });

        $(this).find("[reg1]").each(function(){

            var regStr = $(this).attr("reg1");

            var value = $.trim($(this).val());

            var tip = $(this).attr("tip");

            var reg = new RegExp(regStr);

            if(value != null && value != ""){

                if(!reg.test(value)){

                    $(this).next("span").html(tip);

                    isSubmit = false;

                    return false;

                }

            }

        }); 

        return isSubmit;

    });

     

    3. 使用离焦事件做友好的提示

    $("input[reg2]").blur(function(){

        var regStr = $(this).attr("reg2");

        var value = $.trim($(this).val());

        var tip = $(this).attr("tip");

        var reg = new RegExp(regStr);

        if(!reg.test(value)){

            $(this).next("span").html(tip);

        }else{

            $(this).next("span").html("");

        }

    });

    4. 表单的二次提交处理

    l 锁屏
    l 锁按钮

    ②商品的查询

    商品查询需要组合条件加分页查询
    l 组合条件:品牌,审核状态,商品名称,需要动态sql

    <select id="queryItemByCondtion" resultMap="BaseResultMap" parameterType="map">

        select *

        from (select a.*, rownum rm

            from (

                select *

                    from eb_item ei

                        <where>

                            <if test="brandId != null">

                             ei.brand_id = #{brandId}  

                          </if>

                          <if test="auditStatus != null">

                             and ei.audit_status = #{auditStatus}  

                          </if>

                          <if test="showStatus != null">

                              and ei.show_status = #{showStatus}  

                            </if>

                          <if test="itemName != null">

                              and ei.item_name like '%${itemName}%'

                          </if>

                        </where>

                    order by ei.item_id desc) a

                 <![CDATA[

                 where rownum < #{endNum}) b

            where b.rm > #{startNum}

                 ]]>

    </select>

    --查询大于当前页首行号,主要解决oracle的rownum不支持大于号的问题

    select *

    from (

        --查询小于当前最大行号的数据

        select rownum rm,a.*

        from (

            --第一个select查询所有的业务数据

           select * from eb_item

           ) a

        where rownum < 21) b

    where b.rm > 10

     

    l 分页查询
    1. 查询结果集的sql,传入开始行数和结束的行数

    select *

    from (select a.*, rownum rm

          from (

              ...

             内部sql

             ...

          ) a where rownum < #{endNum}) b

    where b.rm > #{startNum}

    2. 使用内部sql查询结果集的总条数

    3. 使用分页工具类,创建page对象更换每次的数据总条数和当前页数和每页的条数,查询出结果集后把结果集注入到page对象之中

    public class Page {

        int totalCount = 0;

        int pageSize = 10;

        int currentPageNo = 1;

        int startNum = 0;

        int endNum = 11;

        public Page(int totalCount, int pageSize, int currentPageNo) {

            super();

            this.totalCount = totalCount;

            this.pageSize = pageSize;

            this.currentPageNo = currentPageNo;

        }

        public int getStartNum(){

            return (currentPageNo - 1) * pageSize;

        }

     

        public int getEndNum(){

            return currentPageNo * pageSize + 1;

        }

        public int getTotalPage(){

            int totalPage = totalCount/pageSize;

            if(totalPage == 0 || totalCount%pageSize != 0){

                totalPage ++;

            }

            return totalPage;

        }

        public int getNextPage(){

            if(currentPageNo >= getTotalPage()){

                return currentPageNo;

            }else{

                return currentPageNo + 1;

            }

        }

        public int getPrePage(){

            if(currentPageNo <= 1){

                return currentPageNo;

            }else{

                return currentPageNo - 1;

            }

        }

        List<?> list;

     

        public List<?> getList() {

            return list;

        }

     

        public void setList(List<?> list) {

            this.list = list;

        }

     

        public int getTotalCount() {

            return totalCount;

        }

     

        public void setTotalCount(int totalCount) {

            this.totalCount = totalCount;

        }

     

        public int getPageSize() {

            return pageSize;

        }

     

        public void setPageSize(int pageSize) {

            this.pageSize = pageSize;

        }

     

        public int getCurrentPageNo() {

            return currentPageNo;

        }

     

        public void setCurrentPageNo(int currentPageNo) {

            this.currentPageNo = currentPageNo;

        }

     

        public void setStartNum(int startNum) {

            this.startNum = startNum;

        }

     

        public void setEndNum(int endNum) {

            this.endNum = endNum;

        }

    }

    4 制作前端样式

    var currentPageNo = parseInt($("#currentPageNo").val());

    var totalCount = parseInt($("#totalCount").val());

    var totalPage = parseInt($("#totalPage").val());

    $("#pagePiece").html(totalCount);

    $("#pageTotal").html(currentPageNo+"/"+totalPage);

    if(currentPageNo <= 1){

        $("#previous").hide();

    }else{

        $("#previous").show();

    }

    if(currentPageNo >= totalPage){

        $("#next").hide();

    }else{

        $("#next").show();

    } 

    $("#next").click(function(){

        $("#pageNo").val(parseInt(currentPageNo)+1);

        $("#form1").submit();

    });

    $("#previous").click(function(){

        $("#pageNo").val(parseInt(currentPageNo)-1);

        $("#form1").submit();

    });

     

    Oracle  分页sql的描述:
      1.最内层的写查询当前表的全量即可
      2.对于oracle数据库分页需要依赖于rownum,但是rownum不支持大于号,但是支持小于号,可以rownum小于结束行号查询出来一个结果集(在全量的外层套一个select,它的结果集需要把rownum作为结果返回)
      3.在第二步的结果集基础上再做一次查询,查询条件以第二步查询出来的rownum的值作为条件大于开始行号即可

    ③商品发布

    Consoleportal是分开部署在两台服务器上,发布需要在console端去控制,但是生成的静态化的文件要发布到portal的工程之中,所以发布的服务要在portal上,但是要在console中来调用,异构之间的调用要使用webservice
    1. 采用cxfwebservice框架来整合spring
    2. web.xml中来配置cxf的核心servlet

    <servlet>

        <servlet-name>cxfServlet</servlet-name>

        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>

    </servlet>

    <servlet-mapping>

        <servlet-name>cxfServlet</servlet-name>

        <url-pattern>/services/*</url-pattern>

    </servlet-mapping>

    3. 创建服务的接口和接口的实现类,注意接口上加上@WebService注解

    4. 创建cxf的核心配置文件cxf-servlet.xml,配置带有接口的webservice服务使用<jaxwsserver>标签

    <?xml version="1.0" encoding="UTF-8"?>

    <beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws"

    xmlns:jaxrs="http://cxf.apache.org/jaxrs" xmlns:cxf="http://cxf.apache.org/core"

    xsi:schemaLocation="http://www.springframework.org/schema/beans 

    http://www.springframework.org/schema/beans/spring-beans.xsd

    http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd

    http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd

    http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd">

        <!-- 引入CXF Bean定义如下,早期的版本中使用 -->

        <import resource="classpath:META-INF/cxf/cxf.xml" />

        <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />

        <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

     

        <jaxws:server id="publish" address="/publish" serviceClass="cn.itcast.ecps.ws.service.EbItemWSService">

            <jaxws:serviceBean>

                <bean class="cn.itcast.ecps.ws.service.impl.EbItemWSServiceImpl"></bean>

            </jaxws:serviceBean>

        <!-- 输入输出的拦截器 -->

     

            <jaxws:inInterceptors>

                <bean class="org.apache.cxf.interceptor.LoggingInInterceptor"></bean>

            </jaxws:inInterceptors>

     

            <jaxws:outInterceptors>

                <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"></bean>

            </jaxws:outInterceptors>

        </jaxws:server>

    </beans>

    5. 修改cxf-servlet.xml的位置,在springlistener中加载

    <listener>

        <listener-class>

            org.springframework.web.context.ContextLoaderListener

        </listener-class>

    </listener>

    <context-param>

        <param-name>contextConfigLocation</param-name>

        <param-value>

            classpath*:beans.xml,classpath*:cxf-servlet.xml

        </param-value>

    </context-param> 

    6. 启动服务器发布webservice的服务,使用wsdl2java生成客户端的代码

    Wsdl2java –d . –p cn.itcast.ecps.ws.stub http://.........wsdl?
    7. 在客户端调用。

    ④订单管理模块

    1) 流程设计

     

    2) 订单整合activiti工作流

    1.activiti流程和spring整合
    创建activiti-context.xml文件

    <beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd

    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

        

        <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">

            <!-- 数据源 -->

            <property name="dataSource" ref="dataSource" />

            <!-- 配置事务管理器,统一事务 -->

            <property name="transactionManager" ref="txManager" />

            <!-- 设置建表策略 -->

            <property name="databaseSchemaUpdate" value="true" />

        </bean>

     

        <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">

            <property name="processEngineConfiguration" ref="processEngineConfiguration" />

        </bean>

        <bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />

        <bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" />

        <bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" />

        <bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" />

    </beans>

     

    1. 画流程图
    2. 创建流程服务类

    package cn.itcast.service.impl;

    import java.io.File;

    import org.activiti.engine.RepositoryService;

    import org.activiti.engine.repository.DeploymentBuilder;

    import org.springframework.beans.factory.annotation.Autowired;

    import org.springframework.stereotype.Service;

    import cn.itcast.service.IWorkFlowService;

     

    //@Service

    public class WorkflowServiceImpl implements IWorkFlowService {

     

        @Autowired

        RepositoryService repositoryService;

     

        /*public RepositoryService getRepositoryService() {

            return repositoryService;

        }

     

        public void setRepositoryService(RepositoryService repositoryService) {

            this.repositoryService = repositoryService;

        }*/

     

        public void deployFlow() {

            //创建发布流程配置对象

            DeploymentBuilder builder = repositoryService.createDeployment();

            //指定流程资源路径

            builder.addClasspathResource("activit-orderflow.bpmn").addClasspathResource("activit-orderflow.png");

            builder.deploy();

        }

    }

    1. 部署流程

    2. 查询业务任务
    3. 办理任务

    项目中哪些功能模块涉及了大数据量访问?你是如何解决的?

    系统前台是互联网上的用户访问的,会有大量用户来访问。
    假定有1w个人打开你的网站来订商品,问你如何解决并发问题(可扩展到任何高并发网站要考虑的并发读写问题)
    问题,1w个人来访问,商品没出去前要保证大家都能看到有商品,不可能一个人在看到商品的时候别人就不能看了。到底谁能抢到,那得看这个人的运气(网络快慢等)
    其次考虑的问题,并发,1w个人同时点击购买,到底谁能成交?总共只有一张商品。

    Update eb_sku t sett.stock = t.stock  1 where t.sku_id = #{skuId} and t.stock > 0

    Update eb_sku t set t.sale = t.sale +1 where t.sku_id = #{skuId} 

    首先我们容易想到和并发相关的几个方案  锁同步

        同步更多指的是应用程序的层面,多个线程进来,只能一个一个的访问,java中指的是syncrinized关键字。锁也有2个层面,一个是java中谈到的对象锁,用于线程同步;另外一个层面是数据库的锁;如果是分布式的系统,显然只能利用数据库端的锁来实现。

     

        假定我们采用了同步机制或者数据库物理锁机制,如何保证1w个人还能同时看到有商品,显然会牺牲性能,在高并发网站中是不可取的。使用hibernate后我们提出了另外一个概念:乐观锁(一定要用)悲观锁(即传统的物理锁);采用乐观锁即可解决此问题。乐观锁意思是不锁定表的情况下,利用业务的控制来解决并发问题,这样即保证数据的并发可读性又保证保存数据的排他性,保证性能的同时解决了并发带来的脏数据问题。
    hibernate中如何实现乐观锁:

    前提:在现有表当中增加一个冗余字段,version版本号, long类型

    原理:

    1)只有当前版本号》=数据库表版本号,才能提交

    2)提交成功后,版本号version ++

    实现很简单:在ormapping增加一属性-lock="version"即可,以下是样例片段optimistic

    <hibernate-mapping>

        <class name="com.insigma.stock.ABC" optimistic-lock="version" table="T_Stock" schema="STOCK">

    </hibernate-mapping>

    更新的时候给版本号字段加上 1,然后 UPDATE 会返回一个更新结果的行数,通过这个行数去判断。

    Sku_id

    sale

    Version(乐观锁的标志字段)

    1

    100

    1

    UPDATE 必须这样写:

    Sale = 100

    UPDATE EB_SKU u

       SET u.SALES = #SALES#,

           u.version = u.version + 1

     WHERE u.SKU_ID = #SKUID#

       AND u.version = #version#

    Update user t set t.address = #{address} where t.user_id = #{userId}

    如果更新执行返回的数量是 0 表示产生并发修改了,需要重新获得最新的数据后再进行更新操作。

    HibernateJPA  ORM 框架或者实现,是使用版本号,再判断 UPDATE 后返回的数值,如果这个值小于 1 时则抛出乐观锁并发修改异常。

     

    解决大量用户访问量问题方案是集群部署,防止宕机,负载均衡

    1.我们采用4portal服务器来集群,使用2nginx代理服务器,

        1)反向代理,把4portal的服务(host)集中起来,访问动态链接代理地址(代理IP192.168.1.100)时候会把请求转发到4portal上,如果访问静态页面直接访问nginx上的资源文件就可以了,静态html中有ajax的请求由nginx的反向代理功能来转发。

        2)部署静态资源(html和图片)

    2.Rsync用作资源同步

        console上传的图片,由于rsync的部署,会指定一个具体的同步目录(上传图片的目录),一旦发现目录中有文件就立刻同步到nginx

    3.Redis负责管理session和缓存搜索的数据

        管理session的原因:用于多台服务器之间需要有相同的session,同享策略耗费资源,所以采用redis来存储session。缓存频繁被搜索的数据。

    在做这个项目的时候你碰到了哪些问题?你是怎么解决的?

    .开发webservice接口出现客户端和服务端不同步,导致接口无法测试,产生的原因沟通不畅。

    .订单提交时由于本地bug或者意外故障导致用户钱支付了但是订单不成功,采用对账方式来解决。

    .上线的时候一定要把支付的假接口换成真接口。

    .项目中用到了曾经没有用过的技术,解决方式:用自己的私人时间主动学习

    .在开发过程中与测试人员产生一些问题,本地环境ok但是测试环境有问题,环境的问题产生的,浏览器环境差异,服务器之间的差异

    .系统运行环境问题,有些问题是在开发环境下OK,但是到了测试环境就问题,比如说系统文件路径问题、导出报表中的中文问题(报表采用highcharts),需要在系统jdk中添加相应的中文字体才能解决;

    你做完这个项目后有什么收获?

        首先,在数据库方面,我现在是真正地体会到数据库的设计真的是一个程序或软件设计的重要和根基。因为数据库怎么设计,直接影响到一个程序或软件的功能的实现方法、性能和维护。由于我做的模块是要对数据库的数据进行计算和操作的,所以我对数据库的设计对程序的影响是深有体会,就是因为我们的数据库设计得不好,搞得我在对数据库中的数据进行获取和计算利润、总金时,非常困难,而且运行效率低,时间和空间的复杂也高,而且维护起来很困难,过了不久,即使自己有注释,但是也要认真地看自己的代码才能明白自己当初的想法和做法。加上师兄的解说,让我对数据库的重要的认识更深一层,数据库的设计真的是重中之重。

     

        其次,就是分工的问题。虽然这次的项目我们没有在四人选出一个组长,但是,由于我跟其他人都比较熟,也有他们的号码,然后我就像一个小组长一样,也是我对他们进行了分工。俗话也说,分工合作,分好了工,才能合作。但是这次项目,我们的分工却非常糟糕,我们在分工之前分好了模块,每个模块实现什么功能,每个人负责哪些模块。本以为我们的分工是明确的,后来才发现,我们的分工是那么的一踏糊涂,一些功能上紧密相连的模块分给了两个人来完成,使两个人都感到迷惘,不知道自己要做什么,因为两个人做的东西差不多。我做的,他也在做,那我是否要继续做下去?总是有这样的疑问。从而导致了重复工作,浪费时间和精力,并打击了队员的激情,因为自己辛辛苦苦写的代码,最后可能没有派上用场。我也知道,没有一点经验的我犯这样的错是在所难免,我也不过多地怪责自己,吸取这次的教训就好。分工也是一门学问。

     

        再者,就是命名规范的问题。可能我们以前都是自己一个人在写代码,写的代码都是给自己看的,所以我们都没有注意到这个问题。就像师兄说的那样,我们的代码看上去很上难看很不舒服,也不知道我们的变量是什么类型的,也不知道是要来做什么的。但是我觉得我们这一组人的代码都写得比较好看,每个人的代码都有注释和分隔,就是没有一个统一的规范,每个人都人自己的一个命名规则和习惯,也不能见名知义。还有就是没有定义好一些公共的部分,使每个人都有一个自己的公共部分,从而在拼起来时,第一件事,就是改名字。而这些都应该是在项目一开始,还没开始写代码时应该做的。

     

        然后,我自己在计算时,竟然太大意算错了利润,这不能只一句我不小心就敷衍过去,也是我的责任,而且这也是我们的项目的核心部分,以后在做完一个模块后,一定要测试多次,不能过于随便地用一个数据测试一下,能成功就算了,要用可能出现的所有情况去测试程序,让所有的代码都有运行过一次,确认无误。

     

        最后,也是我比较喜欢的东西,就是大家一起为了一个问题去讨论和去交流。因为我觉得,无论是谁,他能想的东西都是有限的,别人总会想到一些自己想不到的地方。跟他人讨论和交流能知道别人的想法、了解别人是怎样想一个问题的,对于同样的问题自己又是怎样想的,是别人的想法好,还是自己的想法好,好在什么地方。因为我发现问题的能力比较欠缺,所以我也总是喜欢别人问我问题,也喜欢跟别人去讨论一个问题,因为他们帮我发现了我自己没有发现的问题。在这次项目中,我跟植荣的讨论就最多了,很多时候都是不可开交的那种,不过我觉得他总是能够想到很多我想不到的东西,他想的东西也比我深入很多,虽然很多时候我们好像闹得很僵,但是我们还是很要好的! 嘻嘻!而且在以后的学习和做项目的过程中,我们遇到的问题可能会多很多,复杂很多,我们一个人也不能解决,或者是没有想法,但是懂得与他人讨论与交流就不怕这个问题,总有人的想法会给我们带来一片新天地。相信我能做得更好。

     

        还有就是做项目时要抓准客户的要求,不要自以为是,自己觉得这样好,那样好就把客户的需求改变,项目就是项目,就要根据客户的要求来完成。

    你这个项目中使用什么构建的?多模块开发是如何划分的呢?为什么要这么做?

    我们这个项目使用Maven进行构建,并使用了水平划分,这样划分层次清晰,代码重用性高,易于独立维护。

    垂直划分

     

    水平划分

     

    优缺点:

        垂直:功能模块明确,层次不够清晰,代码重用性差。

        水平:层次清晰,代码重用性高,独立维护。

     

    淘淘商城后台管理系统采取水平划分。

    你觉得在文件上传功能上面需要注意什么?

    对上传的文件做校验!

    在你这个项目中,是如何设计商品规格的?

    实现思路很重要!

     

    在这个项目中你是如何实现跨系统调用的?(DUbbo)

     

    有两种调用方式:
    1 Ajax,走前台js,通过jsonp来跨域,关于jsonp请参考:http://www.cnblogs.com/yuzhongwusan/archive/2012/12/11/2812849.html

    a) 效率

    b) 带宽

    document.domain="taotao.com"
    jsonp,部署子域名的情况 
    需要在SpringMVC中扩展MappingJackson2HttpMessageConverter,支持jsonp。跨域问题,因为我们是子域名访问子系统接口的,采用jsonp解决;

    2 后台转发请求,走后台,通过httpclient来调。

    a) 可以加逻辑(加缓存只能这条路走)

    b) 安全,接口不在公网公开

    重点学习httpclient中的示例
    掌握springHttpclient的集成

    我们这个项目2种方式都使用到了。

    你这个项目中CMS系统是如何设计的,简单的说一下其设计思想?

    隐藏在内容管理系统(CMS)之后的基本思想是分离内容的管理和设计。页面设计存储在模板里,而内容存储在数据库或独立的文件中。当一个用户请求页面时,各部分联合生成一个标准的HTML(标准通用标记语言下的一个应用)页面。

    内容管理系统被分离成以下几个层面:各个层面优先考虑的需求不同

        1,后台业务子系统管理(管理优先:内容管理):新闻录入系统,BBS论坛子系统,全文检索子系统等,针对不同系统的方便管理者的内容录入:所见即所得的编辑管理界面等,清晰的业务逻辑:各种子系统的权限控制机制等;

        2Portal系统(表现优先:模板管理):大部分最终的输出页面:网站首页,子频道/专题页,新闻详情页一般就是各种后台子系统模块的各种组合,这种发布组合逻辑是非常丰富的,Portal系统就是负责以上这些后台子系统的组合表现管理;

        3,前台发布(效率优先:发布管理):面向最终用户的缓存发布,和搜索引擎spiderURL设计等……

        内容管理和表现的分离:很多成套的CMS系统没有把后台各种子系统和Portal分离开设计,以至于在Portal层的模板表现管理和新闻子系统的内容管理逻辑混合在一起,甚至和BBS等子系统的管理都耦合的非常高,整个系统会显得非常庞杂。而且这样的系统各个子系统捆绑的比较死,如果后台的模块很难改变。但是如果把后台各种子系统内容管理逻辑和前台的表现/发布分离后,Portal和后台各个子系统之间只是数据传递的关系:Portal只决定后台各个子系统数据的取舍和表现,而后台的各个子系统也都非常容易插拔。

        内容管理和数据分发的分离:需要要Portal系统设计的时候注意可缓存性(Cache Friendly)性设计:CMS后台管理和发布机制,本身不要过多考虑"效率"问题,只要最终页面输出设计的比较Cacheable,效率问题可通过更前端专门的缓存服务器解决。

        此外,就是除了面向最终浏览器用户外,还要注意面向搜索引擎友好(Search engine Friendly)URL设计:通过 URL REWRITE转向或基于PATH_INFO的参数解析使得动态网页在链接(URI)形式上更像静态的目录结构,方便网站内容被搜索引擎收录;

    在这个项目中,你们主要使用什么样的数据格式来进行数据的传输的?你对JSON了解么?能说说JSON对象如何转换成Java对象的?

     

     

     

     

    单点系统的设计思想你了解吗?他在系统架构中的作用是什么?位置如何?

    单点登录SSOSingle Sign On)说得简单点就是在一个多系统共存的环境下,用户在一处登录后,就不用在其他系统中登录,也就是用户的一次登录能得到其他所有系统的信任。单点登录在大型网站里使用得非常频繁,例如像阿里巴巴这样的网站,在网站的背后是成百上千的子系统,用户一次操作或交易可能涉及到几十个子系统的协作,如果每个子系统都需要用户认证,不仅用户会疯掉,各子系统也会为这种重复认证授权的逻辑搞疯掉。实现单点登录说到底就是要解决如何产生和存储那个信任,再就是其他系统如何验证这个信任的有效性,因此要点也就以下几个:

    存储信任

    验证信任

    只要解决了以上的问题,达到了开头讲得效果就可以说是SSO。最简单实现SSO的方法就是用Cookie,实现流程如下所示:

     

    不难发现以上的方案是把信任存储在客户端的Cookie里,这种方法虽然实现方便但立马会让人质疑两个问题:

    Cookie不安全

    不能跨域免登

    对于第一个问题一般都是通过加密Cookie来处理,第二个问题是硬伤,其实这种方案的思路的就是要把这个信任关系存储在客户端,要实现这个也不一定只能用Cookie,用flash也能解决,flashShared Object API就提供了存储能力。

    一般说来,大型系统会采取在服务端存储信任关系的做法,实现流程如下所示:

     

    以上方案就是要把信任关系存储在单独的SSO系统(暂且这么称呼它)里,说起来只是简单地从客户端移到了服务端,但其中几个问题需要重点解决:

    如何高效存储大量临时性的信任数据

    如何防止信息传递过程被篡改

    如何让SSO系统信任登录系统和免登系统

    对于第一个问题,一般可以采用类似与memcached的分布式缓存的方案,既能提供可扩展数据量的机制,也能提供高效访问。对于第二个问题,一般采取数字签名的方法,要么通过数字证书签名,要么通过像md5的方式,这就需要SSO系统返回免登URL的时候对需验证的参数进行md5加密,并带上token一起返回,最后需免登的系统进行验证信任关系的时候,需把这个token传给SSO系统,SSO系统通过对token的验证就可以辨别信息是否被改过。对于最后一个问题,可以通过白名单来处理,说简单点只有在白名单上的系统才能请求生产信任关系,同理只有在白名单上的系统才能被免登录。

    你们这个项目中订单ID是怎么生成的?我们公司最近打算做一个电商项目,如果让你设计这块,你会考虑哪些问题?

    生成订单ID的目的是为了使订单不重复,本系统订单ID生成规则:

        用户ID+当前系统的时间戳

    String orderId = order.getUserId() + "" + System.currentTimeMillis();

    设计的时候我会考虑:

    订单ID不能重复

    订单ID尽可能的短(占用存储空间少,实际使用方便,客服相关

    订单ID要求是全数字(客服

    各个服务器的时间不统一怎么办?

    在各个服务器上做时间的统一;(运维

    在问题17的基础上,可能存在毫秒级的偏差情况,怎么办?

    修改订单生成规则:
        用户ID+当前系统的时间戳+随机数3~4 问题:太长? 把时间戳中的2014中的20拿掉;

    你们线上部署时什么样的,能画一下吗?

     

    多台tomcat之间的session是怎么同步的?

        不用session,我们使用单点登陆,使用redis,存在redis,生成,A同步到BB同步到C

    如何解决并发问题的?

    集群,负载均衡,nginx(主备,一般主在工作,备闲置;资源浪费)lvs(2Nginx前做一个拦截,接收后进行分工)。有问题,如果nginx挂掉,整个系统就挂了。可以主备解决,可以前面搭一个lvs。这块不是你做的,但是你知道怎么解决(非常复杂,但是必须了解。针对具体的情况去具体对待,CPU,内存,不要一刀切。)

    你们生产环境的服务器有多少台?

    面试前要数好,一般是十几到二十台。(用在哪里?这是重点)

    Nginx至少2

    Tomcat至少3台以上

    数据库至少2

    Redis至少一台

    可参考17问图

    备份是怎么做的?有没有做读写分离?

    主从(一主多从,主要是备份主),每天备份,备份的文件不要放到数据库服务器上,可以FTP。要检查有效否。读写分离自己查一下,分库分表做过。

    你们服务器不止一台吧,那么你们的session是怎么同步的?

    此问题与18相同,如果购物车使用session做的话,此问题极易被问到。

    Session放到redis里面,使用单点登录系统。购物车设计思路:未登录(先写到cookie中,登录后写到数据库表中);已登录(直接写到数据库,而不会写到cookie)实际项目是不使用session的,使用redis集中处理处理数据,取代session的作用,应用在单点登录、购物车等。

    你们使用什么做支付的?如果使用易宝做支付,请求超时了怎么处理?

    ①重试,一般三次,每次重试都要停顿一会,比如,以第一次停顿1秒,第二次停顿2秒,第三次停顿3秒;
    ②给订单标识付款异常状态,并且发出警告(邮件、短信)给相关人员。

    ③写个定时任务,定时处理异常状态的订单。

    你刚才不是说付款成功后易宝会有数据返回吗?如果付款后易宝没有返回,或者返回超时了,但是钱又已经扣了,你怎么办?

    ①我们请求了易宝,但是没有接受到响应,我们就认为该订单没有支付成功,并且将订单标识为异常状态;
    ②使用定时任务处理;

    ③做一个对账的任务,实时处理异常状态的订单。

    你们怎么做退款功能的,要多长时间才能把钱退回给用户?

    用户申请退款后,经过客服审核通过会将退款请求提交到易宝,具体到账时间要看易宝的处理。

    你前台portal采用4台服务器集群部署 那你数据库有几台服务器?如果前台高并发访问性能提上去了,那数据库会不会造成一个瓶颈,这一块你是怎么处理的?

     

    你购物车存cookie里边可以实现不登录就可以使用购物车,那么我现在没有登录把商品存购物车了,然后登录了,然后我换台电脑并且登录了还能不能看见我购物车的信息?如果看不到怎么做到cookie同步,就是在另外一台电脑上可以看到购物车信息

    更换电脑,必须登录才能看到之前购物车的商品。

    跨域cookie同步方案:

    场景:有时一个公司可能有多个不同域名的网站,比如sina.comweibo.cn,比如taobao.comtmall.com

    这些网站背后很多是同一套会员体系。由于http协议规定cookie是跟着域名走的,这时就需要在不同的域名下同步登陆状态,避免出现用户体验上出现需要二次登陆验证的情况。

    假设下面这样一个场景:

        用户在 bbb.com上已经登陆,现在要去aaa.com上玩,在aaa.com域名下暂未登录。需要访问的aaa.com/resource.html资源需要登录才能访问。两个网站是同一套会员体系,同一个公司的。这是要让用户体验上做到用户在aaa.com上玩也能识别出登录状态。

    以上面场景为例,下面画了个实现跨域同步简单流程图:

     

    解释如下:

    第一步:用户向aaa.com发起get请求,获取resource.html资源,aaa.com发现用户未登录,返回302状态和外部重定向url:

    Java代码  收藏代码

    j.bbb.com?target=www.aaa.com/resource.html  

    注意j.bbb.com子域名上部署的应用可以认为是专门用了跨域同步。

     

    第二步:用户根据重定向url,访问j.bbb.com?target=www.aaa.com/resource.html,由于在bbb.com上已经登录,所以bbb.com上能拿到从client端传递过来cookie信息。子域j.bbb.com上的应用负责将cookie读取出来,并作为参数再次重定向到

    Java代码  收藏代码 

    p.aaa.com?tartet=www.aaa.com/resource.html&sessionid=xxx&loginId=xxx&……  

    第三步:用户根据第二步重定向url,访问p.aaa.comp.aaa.com子域名上的应用专门负责根据请求参数里的参数对,往aaa.com域写入cookie,并重定向到用户第一步请求的url

    第四步:经过前三步,已经完成了再aaa.com域名下同步bbb.com的登录状态,用户再次请求aaa.com/resource.html,这是就能成功访问了。

    点一个链接访问到一个页面,这个页面上既有静态数据,又有动态数据(需要查数据库的),打开这个页面的时候就是很慢但是也能打开。怎么解决这个问题,怎么优化?

    如果要静态页面的话那就得用freemarker或者通过ajax异步,通过js操作异步刷新表单,通过js对返回结果组装成html

     

     

    缓存、动态页面静态化:

    所谓缓存, 是指将那些经常重复的操作结果暂时存放起来, 在以后的执行过程中, 只要使用前面的暂存结果即可.

    那么在我们开发Web网站的过程中, 到底有多少工作可以采用用缓存呢? 或者说, 我们可以在哪些地方使用缓存呢? 见下图: 下图是客户端浏览器和Web服务器之间的一次完整的通信过程, 红色圆圈标示了可以采用缓存的地方.

     

    首先, 最好的情况是客户端不发送任何请求直接就能获得数据, 这种情况下, 用于缓存的数据保存在客户端浏览器的缓存中.

    其次, 在具有代理服务器的网络环境中, 代理服务器可以针对那些经常访问的网页制作缓存, 当局域网中第一台主机请求了某个网页并返回结果后, 局域网中的第二台主机再请求同一个网页, 这时代理服务器会直接返回上一次缓存的结果, 并不会向网络中的IIS服务器发送请求, 例如: 现在的连接电信和网通线路的加速器等. 但是代理服务器通常有自己专门的管理软件和管理系统, 作为网站开发人员对代理服务器的控制能力有限.

    再次, 前面也说过, 当用户将请求地址发送到IIS服务器时, IIS服务器会根据请求地址选择不同的行为, : 对于*.aspx页面会走应用程序管道, 而对*.html*.jpg等资源会直接返回资源, 那么我们可以把那些频繁访问的页面做成*.html文件, 这样用户请求*.html, 将不用再走应用程序管道, 因此会提升效率. 例如: 网站首页、或某些突发新闻或突发事件等, 可以考虑做成静态网页. 就拿天气预报的发布页面打比方: 天气预报的发布页面的访问用户非常多, 我们可以考虑将发布页做成静态的*.html网页, 之后在整个网站程序启动时, Gboabl.asaxApplication_Start事件处理器中, 创建子线程以实现每3个小时重新获取数据生成新的天气发布页面内容.

    之后的asp.net的处理流程, 作为程序员我们是无法干涉的. 直到启动HttpApplication管道后, 我们才可以通过Global.asaxIHttpModule来控制请求处理过程, 在应用程序管道中适合做整页或用户控件的缓存. : 缓存热门页面, 我们可以自动缓存整个网站中访问量超过一定数值(阀值)的页面, 其中为了减小IO操作, 将缓存的页面放在内容中.

    如果用户一直向购物车添加商品怎么办?并且他添加一次你查询一次数据库?互联网上用户那么多,这样会对数据库造成很大压力你怎么办?

    在回答这个问题前,请想好自己的项目是否真的需要使用购物车?(SKU数少,商品结构单一等就不需要使用购物车了)

    购物车的设计方案:

    购物车的实现不存在哪种方式更好,完全是根据公司和项目架构相关的,类似苏宁使用的是数据库存储,但是国美使用的就是Session,不同的软件架构和不同的业务需求对应的购物车存储也是不一样的

    用数据库存你得给数据库造成多大的负担啊, 而且对于购物车, 这种需要实时操作的东西, 数据库的访问量一大了, 就容易出现并发错误, 或者直接崩溃.

    Session确实效率很高, 而且会话是针对各个连接的, 所以便于管理, 但是用Session也不是完美的, 因为Session是有有效期的, 根据服务器的设置不同而不一样长, 如果你在购物的过程中Session超时了, 那么购物车中的东西就会全没了.不知道你看过当当网的购物车没有, 当你下线之后, 再次上线, 购物车中的东西还是存在的, 这对于用户来说非常方便.所以如果你的服务器够强的话, 你完全可以用一个静态变量来保存所有用户的购物车, 比如用一个静态的Map, IP作为Key,区分不同用户的购物车, 这样就可以使用户在下线的情况下也可以保存购物车中的内容.这种方法实现过, 只是没有用大量的并发访问测试其稳定性, 但是一定是可行的。 

    采用存储过程将购物车存储于数据库相应表的方式,优点:数据稳定,不易丢失。缺点:效率低,增加数据库服务器负担。变量 + Datatable保存于客户端,优点:效率高,减轻数据库服务器负担。缺点:Session保存的变量容易丢失,但是一般情况下不会造成影响。变量 + 购物车对象保存于客户端,这种方式以面向对象为指导思想,逻辑上具有一定的复杂性。优点:效率高,减轻数据库服务器负担,使用便捷。缺点:Session保存的变量容易丢失,但是一般情况下不会造成影响

    购物车的核心功能

     

    购物车数据存数据库好处有很多,可以分析购买行为,可以为客户保存购买信息(不会因为浏览器关闭而丢失)等,我的这个项目的购物车使用的就是将购物车数据存数据库中,未登录时可以加20个商品,登录后可以加50个。

    做促销时,商品详情页面的静态页面如何处理价格问题。

    京东商品详情页虽然仅是单个页面,但是其数据聚合源是非常多的,除了一些实时性要求比较高的如价格、库存、服务支持等通过AJAX异步加载加载之外,其他的数据都是在后端做数据聚合然后拼装网页模板的。整个京东有数亿商品,如果每次动态获取如上内容进行模板拼装,数据来源之多足以造成性能无法满足要求;最初的解决方案是生成静态页,但是静态页的最大的问题:

    1、无法迅速响应页面需求变更;

    2、很难做多版本线上对比测试。如上两个因素足以制约商品页的多样化发展,因此静态化技术不是很好的方案。

     

    数据主要分为四种:商品页基本信息、商品介绍(异步加载)、其他信息(分类、品牌、店铺等)、其他需要实时展示的数据(价格、库存等)。而其他信息如分类、品牌、店铺是非常少的,完全可以放到一个占用内存很小的Redis中存储;而商品基本信息我们可以借鉴静态化技术将数据做聚合存储,这样的好处是数据是原子的,而模板是随时可变的,吸收了静态页聚合的优点,弥补了静态页的多版本缺点;另外一个非常严重的问题就是严重依赖这些相关系统,如果它们挂了或响应慢则商品页就挂了或响应慢;商品介绍我们也通过AJAX技术惰性加载(因为是第二屏,只有当用户滚动鼠标到该屏时才显示);而实时展示数据通过AJAX技术做异步加载

     

    1、接收商品变更消息,做商品基本信息的聚合,即从多个数据源获取商品相关信息如图片列表、颜色尺码、规格参数、扩展属性等等,聚合为一个大的JSON数据做成数据闭环,以key-value存储;因为是闭环,即使依赖的系统挂了我们商品页还是能继续服务的,对商品页不会造成任何影响;

    2、接收商品介绍变更消息,存储商品介绍信息;

    3、介绍其他信息变更消息,存储其他信息

     

    技术选型

    MQ可以使用如Apache ActiveMQ

    Worker/动态服务可以通过如Java技术实现;

    RPC可以选择如alibaba Dubbo

    KV持久化存储可以选择SSDB(如果使用SSD盘则可以选择SSDB+RocksDB引擎)或者ARDBLMDB引擎版);

    缓存使用Redis

    SSDB/Redis分片使用如Twemproxy,这样不管使用Java还是Nginx+Lua,它们都不关心分片逻辑;

    前端模板拼装使用Nginx+Lua

    数据集群数据存储的机器可以采用RAID技术或者主从模式防止单点故障;

    因为数据变更不频繁,可以考虑SSD替代机械硬盘。

     

    核心流程

    1、首先我们监听商品数据变更消息;

    2、接收到消息后,数据聚合Worker通过RPC调用相关系统获取所有要展示的数据,此处获取数据的来源可能非常多而且响应速度完全受制于这些系统,可能耗时几百毫秒甚至上秒的时间;

    3、将数据聚合为JSON串存储到相关数据集群;

    4、前端Nginx通过Lua获取相关集群的数据进行展示;商品页需要获取基本信息+其他信息进行模板拼装,即拼装模板仅需要两次调用(另外因为其他信息数据量少且对一致性要求不高,因此我们完全可以缓存到Nginx本地全局内存,这样可以减少远程调用提高性能);当页面滚动到商品介绍页面时异步调用商品介绍服务获取数据;

    5、如果从聚合的SSDB集群/Redis中获取不到相关数据;则回源到动态服务通过RPC调用相关系统获取所有要展示的数据返回(此处可以做限流处理,因为如果大量请求过来的话可能导致服务雪崩,需要采取保护措施),此处的逻辑和数据聚合Worker完全一样;然后发送MQ通知数据变更,这样下次访问时就可以从聚合的SSDB集群/Redis中获取数据了。

     

    基本流程如上所述,主要分为Worker、动态服务、数据存储和前端展示;因为系统非常复杂,只介绍动态服务和前端展示、数据存储架构;Worker部分不做实现。

    商品搜索框的搜索联想如何实现?比如输入“羽绒” ,然后输入框下会列出很多关于羽绒服的搜索条件 “羽绒服男正品折扣 ”等等。

     

    一个电商项目,在tomcat里面部署要打几个war包?

     

    你说你用了redis缓存,你redis存的是什么格式的数据,是怎么存的?

    购物车知识补充(在设计购物车时需要注意哪些细节)

    为什么购物车的设计很重要?

    购物车是消费的最后一环

    购物车在用户整体消费过程中一般是在最后一环,用户完整的消费体验应该是:打开APP或网站->浏览商品->加入购物车->确认订单并支付,在这个过程中,购物车和支付环节可以合并成一环,基本上用户点开购物车并开始填写地址的时候,就有很大的几率要完成购买,做好商品展现以及推送的环节,如果在最后的购物一环没有好的用户体验,岂不呜呼哀哉。

    购物车隐含的对比收藏功能

    与现实购物车不同的是,网络消费者也比较喜欢把看中但不计划买的商品先放入购物车,或者把商品统一放到购物车直接进行比较,以备日后购买,因此从购物车保存的信息,就能够知道用户的大致偏好。

    购物车的重交易属性

    用户在浏览商品涉及的只是前端展示,但购物车这一环涉及到最终的交易,对于用户来说,需要了解本次交易的基本物品信息、价格信息;而对于商户来说,确认收款、订单生成、物流环节都需要在这里获取到信息,才能完成本次的交易。

    购物车设计需要展示的基本信息

    购物车主要作用就是告诉用户买了什么,价格多少,不同类型的物品可能会有不同展示方式,但最基本的包括商品名称、价格、数量(若是服务,可能是次数)、其他附属信息。

     

    哪些细节要让用户买得舒服?

    亲,记得前面说的用户是如何看待购物车的功能吗?还记得你的用户会多次使用购物车,如果你只是完整做好信息展示不做好其他事情真的好吗?

    登录环节不要放在加入购物车前

    请让用户先加入购物车,并在进行结算的时候在提醒用户需要登录。为什么?过早提醒用户需要登录才能购买,会打断用户浏览的流程(用户可能还要购买其他物品好吗?)这样的设置会让部分用户避而远之。

    这里涉及到的一个点是在APP端需要记忆用户加入购物车的信息,与登录后的购物车信息合并(如果一开始没有这样考虑好,技术那可能会有难度)

    自动勾选用户本次挑选的商品

    用户使用购物车有一个大的作用就是收藏,所以你要知道很多用户在购物车中积累了很多物品,当每次挑选加入购物车的商品,用户每次来到购物车要重新把本次的购买商品选上是很不好的体验。

    所以这里一般是自动勾选本次挑选的商品,同样这里也要储存用户的勾选信息。

    陈列展示,注意沉底商品

    让用户看见当前想买的商品就好了,把一些时间久远的,已经卖完的沉底显示。这样做的好处是能让用户看见之前的选择但没购买的商品,提醒一下说不定就又勾上买了哦!

    归类展示,可能增加购买

    考虑如何进行归类展示,C2C可以按照商家分类,B2C可以按照品牌分类。

    价格和优惠的提醒

    消费用户会关系自己每一次的消费价格,为避免商品列表过长隐藏价格信息,APP端一般会把总价固定底部提示。同时在合计信息中,展示优惠价格,能够促进消费者购买。

     

    哪些细节要推动用户继续购买?

    还差一点就可以有优惠啦!

    凑单,常用的手段包括运费见面或是满减促销,一般在网站底部会展示一些适合凑单的商品;在APP端可以给链接(不过需要权衡用户跳转会不会再跳回来哦!)

    提醒用户有些商品你真的可以买了

    有关调查显示,加入购物车而没有购买的,在4小时以内提醒用户,会有27%的唤醒率哦!

    所以需要提醒的几个点有:

    生成订单但是还没支付的

    商品有优惠信息

    商品库存不足的

    这些信息可以促进消费者购买,注意提醒的时间段,早上9点至晚上8点为宜,其他时间段就可能打扰用户咯(当然也要视产品类型而定啦,只不过大半夜提醒用户买东西确实不好,不是?)

     

    购物车实现的三种方式

    1. cookie

      cookie是由服务器产生,存储在客户端的一段信息。它定义了一种Web服务器在客户端存储和返回信息的机制,cookie文件它包含域、路径、生存期、和由服务器设置的变量值等内容。当用户以后访问同一个Web服务器时,浏览器会把cookie原样发送给服务器。通过让服务器读取原先保存到客户端的信息,网站能够为浏览者提供一系列的方便,例如在线交易过程中标识用户身份、安全要求不高的场合避免用户重复输入名字和密码、门户网站的主页定制、有针对性地投放广告等等。利用cookie的特性,大大扩展了WEB应用程序的功能,不仅可以建立服务器与客户机的联系,因为cookie可以由服务器定制,因此还可以将购物信息生成cookie值存放在客户端,从而实现购物车的功能。用基于cookie的方式实现服务器与浏览器之间的会话或购物车,有以下特点:

     

    cookie存储在客户端,且占用很少的资源,浏览器允许存放300个cookie,每个cookie的大小为4KB,足以满足购物车的要求,同时也减轻了服务器的负荷;

    cookie为浏览器所内置,使用方便。即使用户不小心关闭了浏览器窗口,只要在cookie定义的有效期内,购物车中的信息也不会丢失;

    cookie不是可执行文件,所以不会以任何方式执行,因此也不会带来病毒或攻击用户的系统;

    基于cookie的购物车要求用户浏览器必须支持并设置为启用cookie,否则购物车则失效;

    存在着关于cookie侵犯访问者隐私权的争论,因此有些用户会禁止本机的cookie功能。

     

    2. Session

     

      session是实现购物车的另一种方法。session提供了可以保存和跟踪用户的状态信息的功能,使当前用户在session中定义的变量和对象能在页面之间共享,但是不能为应用中其他用户所访问,它与cookie最重大的区别是,session将用户在会话期间的私有信息存储在服务器端,提高了安全性。在服务器生成session后,客户端会生成一个sessionid识别号保存在客户端,以保持和服务器的同步。这个sessionid是只读的,如果客户端禁止cookie功能,session会通过在URL中附加参数,或隐含在表单中提交等其他方式在页面间传送。因此利用session实施对用户的管理则更为安全、有效。

     

    同样,利用session也能实现购物车,这种方式的特点是:

     

    session用新的机制保持与客户端的同步,不依赖于客户端设置;

    与cookie相比,session是存储在服务器端的信息,因此显得更为安全,因此可将身份标示,购物等信息存储在session中;

    session会占用服务器资源,加大服务器端的负载,尤其当并发用户很多时,会生成大量的session,影响服务器的性能;

    因为session存储的信息更敏感,而且是以文件形式保存在服务器中,因此仍然存在着安全隐患。

     

    3. 结合数据库的方式

     

    这也是目前较普遍的模式,在这种方式中,数据库承担着存储购物信息的作用,session或cookie则用来跟踪用户。这种方式具有以下特点:

    数据库与cookie分别负责记录数据和维持会话,能发挥各自的优势,使安全性和服务器性能都得到了提高;

    每一个购物的行为,都要直接建立与数据库的连接,直至对表的操作完成后,连接才释放。当并发用户很多时,会影响数据库的性能,因此,这对数据库的性能提出了更高的要求;

    使cookie维持会话有赖客户端的支持。

     

    各种方式的选择:

     

    虽然cookie可用来实现购物车,但必须获得浏览器的支持,再加上它是存储在客户端的信息,极易被获取,所以这也限制了它存储更多,更重要的信息。所以一般cookie只用来维持与服务器的会话,例如国内最大的当当网络书店就是用cookie保持与客户的联系,但是这种方式最大的缺点是如果客户端不支持 cookie就会使购物车失效。

     

      Session 能很好地与交易双方保持会话,可以忽视客户端的设置。在购物车技术中得到了广泛的应用。但session的文件属性使其仍然留有安全隐患。

     

    结合数据库的方式虽然在一定程度上解决了上述的问题,但从上面的例子可以看出:在这种购物流程中涉及到对数据库表的频繁操作,尤其是用户每选购一次商品,都要与数据库进行连接,当用户很多的时候就加大了服务器与数据库的负荷.

     

    有的公司采用的是数据库的方式

    1、用户浏览系统,获取用户机器的MAC地址

    2、如果用户购买物品,添加到数据库里面,同时插入机器的MAC地址,也是用户的ID标示

    3、如果用户登录系统,用用户真实的ID,更新当前机器的MAC对应的记录。

    4、如果结帐的话,更新用户的id,删除购物车里面的东西

    5、用户没有登录,购物车记录根据MAC读取记录,如果登录系统根据用户的ID,读取记录

     

     

    希望对大家能够有所帮助!

    展开全文
  • 数据结构面试经典问题汇总

    千次阅读 多人点赞 2020-05-26 16:23:13
    数据结构面试经典问题汇总参考资源:基础深入补充: 参考资源: 基础 数据结构常见面试题 深入 数据结构面试题(三) 数据结构面试必问 数据结构算法常见面试考题 补充: 1.数组和链表的区别,请详细解释。 从逻辑...

    数据结构面试经典问题汇总

    参考资源

    基础

    1. 数据结构常见面试题

    深入

    1. 数据结构面试题(三)
    2. 数据结构面试必问
    3. 数据结构算法常见面试考题

    补充

    1.数组和链表的区别,请详细解释。
    从逻辑结构来看:
    a) 数组必须事先定义固定的长度(元素个数),不能适应数据动态地增减的情况。当数据增加时,可能超出原先定义的元素个数;当数据减少时,造成内存浪费;数组可以根据下标直接存取。
    b) 链表动态地进行存储分配,可以适应数据动态地增减的情况,且可以方便地插入、删除数据项。(数组中插入、删除数据项时,需要移动其它数据项,非常繁琐)链表必须根据next指针找到下一个元素
    从内存存储来看:
    a) (静态)数组从栈中分配空间, 对于程序员方便快速,但是自由度小
    b) 链表从堆中分配空间, 自由度大但是申请管理比较麻烦
    从上面的比较可以看出,如果需要快速访问数据,很少或不插入和删除元素,就应该用数组;相反, 如果需要经常插入和删除元素就需要用链表数据结构了。

    2.排序算法有哪些?< C语言总共有多少种排序法>
    排序算法有很多,每种算法有不同的时间和空间复杂度,效率也有差别,那么针对使用上也有不同的场合。原则上说,数据结构是一门领域,跟语言没有绝对的联系,很多时候同样的算法可以用很多种语言实现。下面列一些常见的算法:插入排序,冒泡排序,选择排序,快速排序,堆排序,归并排序,基数排序,希尔排序等。

    3.怎么理解哈希表,哈希表是什么
    摘自百度:散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数

    4.请写出以下算法的时间复杂度
    冒泡排序法 插入排序法 堆排序法 二叉树排序法
    O(n^2) O(n^2) O(nlog2 n) 最差O(n2)平均O(nlog2n)
    快速排序法 希尔排序法
    最差O(n2)平均O(n
    log2n) O(nlog n)不稳定

    5.数据结构,二叉树的相关知识,开销量,为何使用二叉树等。
    在计算机科学中,二叉树是每个结点最多有两个子树的有序树。通常根的子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用作二叉查找树和二叉堆或是二叉排序树。二叉树的每个结点至多只有二棵子树(不存在出度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。
    文件系统和数据库系统一般都采用树(特别是B树)的数据结构数据,主要为排序和检索的效率。二叉树是一种最基本最典型的排序树,用于教学和研究树的特性,本身很少在实际中进行应用,因为缺点太明显了(看看教科书怎么说的)。就像冒泡排序一样,虽然因为效率问题并不实用,单不失一种教学例子的好手段。

    6.怎么判断链表是否有环?
    两个指针分别按照固定步长行走,P1一次走1布,P2一次走2布,如果链表有环,P1和P2会有相遇的时刻。(追击问题解法)

    7、简述快速排序过程
    1)选择一个基准元素,通常选择第一个元素或者最后一个元素,
    2)通过一趟排序将待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小。另一部分记录的元素值比基准值大。
    3)此时基准元素在其排好序后的正确位置
    4)然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。

    8、各类排序算法对比
    时间复杂度来说:
    (1)平方阶(O(n2))排序
      各类简单排序:直接插入、直接选择和冒泡排序;
    (2)线性对数阶(O(nlog2n))排序
      快速排序、堆排序和归并排序;
    说明:
    当原表有序或基本有序时,直接插入排序和冒泡排序将大大减少比较次数和移动记录的次数,时间复杂度可降至O(n);
    而快速排序则相反,当原表基本有序时,将蜕化为冒泡排序,时间复杂度提高为O(n2);
    原表是否有序,对简单选择排序、堆排序、归并排序和基数排序的时间复杂度影响不大。
    稳定性:
    排序算法的稳定性:若待排序的序列中,存在多个具有相同关键字的记录,经过排序,这些记录的相对次序保持不变,则称该算法是稳定的;若经排序后,记录的相对次序发生了改变,则称该算法是不稳定的。
    稳定的排序算法:冒泡排序、插入排序、归并排序和基数排序
    不是稳定的排序算法:选择排序、快速排序、希尔排序、堆排序

    9、选择排序算法准则:
    设待排序元素的个数为n.
    1)当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序序。
    2)当n较大,内存空间允许,且要求稳定性:归并排序
    3)当n较小,可采用直接插入或直接选择排序。
    直接插入排序:当元素分布有序,直接插入排序将大大减少比较次数和移动记录的次数。
    直接选择排序 :元素分布有序,如果不要求稳定性,选择直接选择排序

    10、用循环比递归效率高吗?
    递归和循环两者完全可以互换。不能完全决定性地说循环地效率比递归的效率高。
    递归算法:
    优点:代码简洁、清晰,并且容易验证正确性。
    缺点:它的运行需要较多次数的函数调用,如果调用层数比较深,需要增加额外的堆栈处理(还有可能出现堆栈溢出的情况),比如参数传递需要压栈等操作,会对执行效率有一定影响。但是,对于某些问题,如果不使用递归,那将是极端难看的代码。在编译器优化后,对于多次调用的函数处理会有非常好的效率优化,效率未必低于循环。
    循环算法:
    优点:速度快,结构简单。
    缺点:并不能解决所有的问题。有的问题适合使用递归而不是循环。如果使用循环并不困难的话,最好使用循环。
    解决哈希冲突的方法
    哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。
    1) 线性探测法
    2) 平方探测法
    3) 伪随机序列法
    4) 拉链法

    11、KMP算法:
    在一个字符串中查找是否包含目标的匹配字符串。其主要思想是每趟比较过程让子串先后滑动一个合适的位置。当发生不匹配的情况时,不是右移一位,而是移动(当前匹配的长度– 当前匹配子串的部分匹配值)位。

    展开全文
  • 动态规划经典问题

    千次阅读 2014-09-04 23:43:10
    动态规划经典问题 目录 一、最长公共子序列O(mn) 二、最优排序二叉树O(n3) 三、最长上升子序列O(nlogn) 四、最优三角剖分O(n3) 五、最大m子段和O(mn) 六、0-1背包问题O(min{nc, 2n, n1.44n}) 七、最优排序...
  • 经典问题】括号匹配问题

    万次阅读 多人点赞 2019-05-27 21:57:35
    括号匹配问题算是栈应用中比较经典问题了,在数据结构的书中还有各种考试中会出现。最近刷题的时候也遇到了,就想写一篇文章整理一下。 例题 题目来自Leetcode中国 给定一个只包括 (,),{,},[,] 的字符串,...
  • 小学奥数平均数经典问题汇总

    万次阅读 2017-09-08 16:33:07
    小学奥数平均数经典问题汇总,掌握这些平均数问题,小学奥数考试拿高分不是梦!
  • 第145课: Spark面试经典系列之Yarn生产环境下资源不足问题、JVM和网络的经典问题详解 1 Yarn生产环境下资源不足无法提交spark 2 yarn-client网络流量的问题 spark streaming 这里是用...
  • 操作系统5————进程同步的经典问题:司机售票员&amp;amp;amp;amp;amp;问题生产者消费者问题&amp;amp;amp;amp;amp;哲学家进餐问题&amp;amp;amp;amp;amp;读者写者问题 一. 目录 操作系统...
  • CSS 布局经典问题初步整理

    千次阅读 2018-01-05 00:00:00
    (点击上方公众号,可快速关注)作者:brianwaybrianway.github.io/2017/05/18/css-layout-classical-problems/本文主要对 CSS 布局中常见的经典问题进行简单说明,并提供相关解决方案的参考链接,涉及到三栏式布局...
  • 博弈论经典问题Nim问题

    千次阅读 2010-08-06 15:40:00
      博弈论经典问题,Nim游戏是一个典型的组合游戏问题,很多游戏问题都可以规约到Nim游戏问题。Nim游戏问题是一个ICG(Impartial Combinatorial Games)问题; ICG问题的特征是: 1.两个人参与...
  • 操作系统进程同步互斥经典问题之读者写者问题
  • java经典问题——生兔子

    千次阅读 2017-11-19 12:14:46
    java经典问题——生兔子 题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?  1.程序分析: 兔子的规律为...
  • 评:C语言18个经典问题答录

    千次阅读 2016-05-25 08:00:46
    评:C语言18个经典问题答录
  • Spark面试经典系列之Yarn生产环境下资源不足问题和网络的经典问题详解1、Yarn资源不足无法提交Spark的问题 2、Yarn-Client下网络流量的问题ResourceManager会接收你的提交请求吗?Yarn一般把自己的资源分成不同的...
  • 离散数学图论经典问题之握手定理

    万次阅读 多人点赞 2017-05-15 15:06:56
    今天学习了图论的一些经典问题,感觉挺有意思的,伟人不愧称之为伟人,想问题的方式果然与常人不同。好了,不说废话了,让我们回到今天的正题,握手定理。首先我认为学到一种新知识最好的检测方式就是利用该知识来...
  • 统计学面试经典问题

    万次阅读 多人点赞 2019-12-29 21:22:00
    极大似然估计的方差在高维情况下会很大,贝叶斯方法通过加先验一定程度上克服了这个问题,形式上就是现在的各种正则化方法,使得估计结果更稳定,更有效。 7. 参数点估计量的评价标准有哪些? 相合性,无偏...
  • 【算法之美-经典问题】九宫格问题

    千次阅读 2016-08-31 21:34:00
    【算法之美-经典问题】九宫格问题题目九宫格是一种古老的数学游戏,它要求在3X3的矩阵中,填入1-9这9个数字,并且横向、纵向、斜向上的3个数字之和皆相等。类似问题在数学上被称为幻方。现将九宫格推广到5X5,7X7等...
  • 经典问题之HashMap碰撞问题

    万次阅读 多人点赞 2018-07-24 12:56:58
    1. HashMap的数据结构 数据结构中有数组和链表来实现对数据的存储,但这两者基本上是两个极端。  数组 数组存储区间是连续的,占用内存严重,故空间复杂的很大。但数组的二分查找时间复杂度小,为O(1);...
  • 算法-------过河经典问题,超详细解析

    万次阅读 多人点赞 2019-04-09 19:02:53
    算法-------过河问题 题目来源POJ 题号为1700http://poj.org/problem?id=1700 描述 一群N人希望用一条船过河,这条船最多只能载两个人。因此,必须安排某种穿梭安排,才能来回划船,以便所有人都能过关。每个人都...
  • 详解贪心算法的几个经典问题(代码详解)

    万次阅读 多人点赞 2018-03-15 21:09:07
    详解贪心算法的几个经典问题(代码详解)贪心算法:贪心法顾名思义就是不断贪心的选取当前最优策略的计算方法。下面介绍几种贪心问题问题一:货币选择问题问题描述:分别有1,5,10,50,100元,分别有5,2,2,3,5张纸币。...
  • Python中对于列表理解的经典问题

    千次阅读 2018-08-22 15:58:07
    ######列表的经典问题######   1.【项目:大奖赛计分】 在歌星大奖赛中,有10个评委为参赛的选手打分,分数为1~100分。选手最后得分为:去掉一个最高分和一个最低分后其余8个分数的平均值。请编写一个程序实现。...
  • SQL 面试经典问题 行列互相转化

    千次阅读 2016-09-20 21:28:25
    SQL 面试经典问题 行列互相转化 1.行转列 select 姓名 as 姓名 , max(case 课程 when '语文' then 分数 else 0 end) 语文, max(case 课程 when '数学' then 分数 else 0 end) 数学, max(case 课程 when '...
  • OI经典问题与基本模型

    千次阅读 2014-01-16 10:07:37
    OI经典问题 1.最小斯坦纳树 2.完全动态最小生成树 3.区间第K大 4.黑白划分棋盘 5.动态凸包
  • python题目-----python七个经典问题

    千次阅读 2017-01-19 19:04:16
    python七个经典问题
  • -5右移3位后结果为-1,-1的二进制为: 1111 1111 1111 1111 1111 1111 1111 1111 // (用1进行补位) -5无符号右移3位后的结果 536870911 换算成二进制: 0001 1111 1111 1111 1111 1111 1111 1111 // (用0进行补位)

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 72,592
精华内容 29,036
关键字:

经典问题