网上整理的。
python知识
1.面向对象的概念,特性?
- 面向对象编程,简称OOP,是一种程序设计思想
主要包括:类、继承、多态(子类方法覆盖父类方法)、实例、属性、方法 - 面向对象和面向过程语言的区别:
面向对象是把问题抽象成一个个对象,给这些对象绑上属性和方法,即以功能而非步骤划分问题。 优点:更易低耦合、易维护、易复用; 缺点:因为需要实例化所以开销较大,性能比面向过程要低。 面向过程是把问题拆分成一个个函数和数据,按照一定的顺序来执行这些方法。 优点:性能较高; 缺点:不易维护,不易复用 - Python是解释型语言:代码执行时才一行行动态翻译执行
优点:无需翻译器,方便程序移植;可以交互式运行 缺点:运行效率较低。 - 编译型语言:在程序执行前,需要把源代码编译成机器语言的文件,再执行时可以直接使用该编译结果,以后不需要再次编译。
优点:运行效率高 缺点:难以移植 - 类的定义和概念:
类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。 数据成员: 类变量或者实例变量用于处理类及其实例对象的相关的数据。 方法重写: 如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。 实例变量:定义在方法中的变量,只作用于当前实例的类。 继承: 即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,素以Dog也是一个Animal。子类可以继承父类所有的公有属性和公有方法。 实例化:创建一个类的实例,类的具体对象。 方法: 类中定义的函数。 对象: 通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。 - python的面向对象特性
1.封装(隐藏): 隐藏对象的属性和实现细节,只对外提供必要的方法。相当于将"细节封装起来",只对外暴露“相关调用方法”。通过私有属性、私有方法的方式实现封装。 2.继承: 一个新类可以继承一个设计好的类,即子类可以继承父类所有的公有属性和公有方法。Python支持多重继承,一个子类可以继承多个父类。如果在类定义中没有指定父类,则默认父类是object类,也就是说object类是所有类的父类,因此所有类都有object类的属性和方法。 类成员的继承和重写: 1.成员继承:子类继承了父类除构造方法之外的所有成员。 2.方法重写:子类可以重新定义父类中的方法,这样就会覆盖父类中的方法,也称为重写。 3.多态: 指同一个方法调用,由于对象不同可能会产生不同的行为。 1.多态是方法的多态,属性没有多态 2.多态的存在有两个必要条件:继承、方法重写 (一个类它继承了一个父类,但是它又改写了它父类的方法,这样在调用这个方法时,就会因为实例对象的不同而调用的方法不同,也就是说看这个实例对象实例化时是用父类实例化的,还是子类实例化的,是父类实例化的,结果就是父类的方法,是子类实例化的,结果就是子类的方法)
2.python的数据类型和数据结构
2.1 数据类型 不可变数据(3 个):Number(数字, int、float、bool、complex)、String(字符串)、Tuple(元组); 可变数据(3 个):List(列表)、Dictionary(字典)、Set(集合)。相关知识点: 查看数据类型:type() 数据类型转换:str()、int()、list()、set()
字符串: 1、反斜杠可以用来转义,使用r可以让反斜杠不发生转义。 2、字符串可以用+运算符连接在一起,用*运算符重复。 3、Python中的字符串有两种索引方式,从左往右以0开始,从右往左以-1开始。 4、Python中的字符串不能改变。 List列表: 1、List可以使用+操作符进行拼接。 2、List中的元素是可以改变的,且元素类型可以不一样 Tuple(元组):元组的元素不能修改,且类型可以不一样,写在小括号 () Set集合:进行成员关系测试和删除重复元素,创建一个空集合必须用 set() 字典dict: 是一种映射类型,它的元素是键值对。 2、字典的关键字必须为不可变类型,且不能重复。 3、创建空字典使用 { }。 将整数转化成二进制字符串: (1) >>>bin(12345).replace(‘0b’,’’) ‘11000000111001’ (2) >>> “{0:b}”.format(12345) ‘11000000111001’
2.2数据结构 Python中的内置数据结构(Built-in Data Structure):列表list、元组tuple、字典dict、集合set。 Python基本数据类型和数据结构_qq_45905052的博客-CSDN博客 list.append(x): 把一个元素添加到列表的结尾,相当于 a[len(a):] = [x]。 list.extend(L): 通过添加指定列表的所有元素来扩充列表,相当于 a[len(a):] = L。 list.insert(i, x): 在指定位置插入一个元素。第一个参数是准备插入到其前面的那个元素的索引,例如 a.insert(0, x) 会插入到整个列表之前,而 a.insert(len(a), x) 相当于 a.append(x) 。 list.remove(x): 删除列表中值为 x 的第一个元素。如果没有这样的元素,就会返回一个错误。 list.pop([i]): 从列表的指定位置移除元素,并将其返回。如果没有指定索引,a.pop()返回最后一个元素。元素随即从列表中被移除。(方法中 i 两边的方括号表示这个参数是可选的,而不是要求你输入一对方括号,你会经常在 Python 库参考手册中遇到这样的标记。) list.clear(): 移除列表中的所有项,等于del a[:]。 list.index(x): 返回列表中第一个值为 x 的元素的索引。如果没有匹配的元素就会返回一个错误。 list.count(x): 返回 x 在列表中出现的次数。 list.sort(): 对列表中的元素进行排序。 list.reverse(): 倒排列表中的元素。 **list.copy() :**返回列表的浅复制,等于a[:]。
3.装饰器
装饰器可以在不改变函数代码和调用方式的情况下给函数添加新的功能。
本质上是一个嵌套函数,接收被装饰的函数(func)作为参数,并返回一个包装过的函数,以实现不影响函数的情况下添加新的功能。抽离出大量与函数主体功能无关的代码,增加一个函数的重用性。
应用场景:性能测试(统计程序运行时间)、插入日志、权限校验
4.python的内存管理机制
python的内存管理机制=垃圾回收机制+内存池机制 1 垃圾回收机制 以引用计数法为主,标记-清除为辅。 (1)引用计数法: 指在每个对象的结构体PyObject中定义一个计数变量ob_refcnt,计算该对象被引用的次数,有以下4种情况会造成引用计数变量的增加:1.该对象被引用;2.该对象被加入某容器(列表、字典等);3.对象被创建;4.该对象被作为参数传到函数中。 优点:高效,自动释放,实时性高,即一旦没有引用立即释放; 缺点:1.增加存储空间,维护引用计数消耗资源;2.无法解决循环调用(一组未被外部使用、但相互指向的对象)的情况。 (2)标记-清除 被作为解决循环调用的情况,辅助python进行垃圾回收。 过程:①首先创建对象时,python会将其加入零代链表;②遍历零代链表,如果被本链表内的对象引用,自身的被引用数-1;③清除被引用数为0的对象;④其余未被回收的“活动对象”升级为一代链表 一代同样可能会触发gc机制,从而对一代链表标记清除,将剩下的活动对象升级为二代链表。
2 内存池机制 python引用了一个内存池memory pool机制,即pymalloc机制,用于对小块内存的申请和释放。 当创建大量消耗小内存的对象时,频繁调用malloc/new会导致大量的内存碎片,导致效率降低。内存池即预先在内存中申请一定数量的、大小相等的内存块留作备用,当有新的内存需求时,就先从内存池中分配内存给该需求,不够了再去申请新的内存。对象被释放后,空闲的小内存返回内存池,避免频繁的内存释放动作。 优点:能够减少内存碎片,提升效率 当申请内存小于256k的话,就会直接在内存池中申请内存空间。如果申请内存大于256k的话则会直接执行new/malloc行为来申请新的内存空间。
5.生成器和迭代器
生成器:生成器是列表生成式的简化,列表生成式一次性生成整个列表,如果列表尺寸过大而我们只需要列表的前几个数据进行处理的话就会造成大量的空间浪费,所以引入生成器。 迭代器:迭代器的定义是可以用被next()函数调用并不断返回下一个值的对象。 区别: ①生成器是生成元素的,迭代器是访问集合元素的一种方式; ②迭代输出生成器的内容; ③迭代器是一种支持next()操作的对象; ④迭代器(iterator):其中iterator对象表示的是一个数据流,可以把它看做一个有序序列,但我们不能提前知道序列的长度,只有通过next()函数实现需要计算的下一个数据。可以看做生成器的一个子集。
- 生成器自动实现了迭代器协议,而且将输出状态挂起,有延时输出效果。
- 能够利用for进行遍历,是因为list本身就是一个可迭代对象,内部实质上已经实现了__iter__方法。
- 要想成为迭代器,还需要实现迭代器协议:即实现next方法,要么返回下一个元素,要么引起终止迭代。
6.常用函数
1)lambda函数 匿名函数形式 f=lambda x : x表达式 调用时可采用f(x)的形式 或者直接使用(可以用于return语句) 优点:没有名字,无需担心函数冲突 缺点:只能有一个表达式,没有return,返回值就是该表达式的结果 2)append函数、extend()函数
- append()添加的时候会把添加的数据当成一个整体进行添加,允许添加任意类型的数据
- extend()添加的时候会把添加的数据迭代进行添加(添加元素),只允许添加可迭代对象数据(可迭代对象:
能用for循环进行迭代的对象就是可迭代对象, 比如:字符串,列表,元祖,字典,集合等等)
3)range函数 range()是生成一个list,是一个列表生成式,而xrange()是一个生成器,每次调用返回其中的一个值。xrange做循环的性能比range好,尤其是返回很大的时候。尽量用xrange,除非是要返回一个列表。 4)字符串的拆分方法:
- string对象的split方法,不允许有多个分隔符
- 函数re.split(),允许为分隔符指定多个正则表达式
- str.split(字符串切片)\str.find(查找字符)\str.strip(去除首尾指定字符)
5)深浅拷贝
- 变量:是一个系统表的元素,拥有指向对象的连接空间
- 对象:被分配的一块内存,存储其所代表的值
- 引用:是自动形成的从变量到对象的指针
- 类型:属于对象,而非变量
- 不可变对象:一旦创建就不可修改的对象,包括字符串、元组、数值类型
- 可变对象:可以修改的对象,包括列表、字典、集合
1.赋值: 只是复制了新对象的引用,不会开辟新的内存空间。 2.浅拷贝: 创建新对象,其内容是原对象的引用。三种形式:切片操作,工厂函数,copy模块中的copy函数。 如: lst = [1,2,[3,4]] 切片操作:lst1 = lst[:] 或者 lst1 = [each for each in lst] 工厂函数:lst1 = list(lst) copy函数:lst1 = copy.copy(lst)
- 浅拷贝中存放的是对可变对象的引用,如果把a拷贝成b,a中的可变对象发生变化的话,b中的对应对象也随之发生变化,即ab中的子类可变对象其实是共享的,虽然拷贝对象与原对象的内存地址不同,但是两者中的元素具有相同的内存地址。3.深拷贝: 和浅拷贝对应,深拷贝拷贝了对象的所有元素,包括多层嵌套的元素。深拷贝出来的对象是一个全新的对象,不再与原来的对象有任何关联。
3.深拷贝 用copy模块中的deepcopy实现,是把所有可变对象、不可变对象的内容都通过递归的方式一一拷贝了过来,ab是不相同的内存地址,ab中的元素内存地址也不相同,两者完全独立。a中的可变对象发生改变的话不会影响b中对象的改变。 6)is和==的区别
- is判断两个对象的内存地址是否相同
- ==判断两个对象的值是否相同
7)python传参时需要注意什么 (1) 传入参数是否可以被修改 1.如果没有将外部变量传递到函数来(参数定义种包含该外部变量),则函数内部虽然可以使用,但是不能对该变量进行修改(会报错)——未传参的变量可使用但不可修改 2.将外部变量作为实参传递到函数中来的话,如果外部变量是不可变对象的话不可以修改该变量的原始值——相当于“传值”的方式来传递变量,可以通过改变引用指向的方式,在函数内部获取新的不可变对象,但是对函数外部没有影响(除非加了global)
python函数传入的是原变量引用的一个副本,这两个引用(副本及原引用)在内外均指向同一个内存区(具体存值的地方)。如果在函数内部改变了引用指向的话,只是副本引用指向了一个新的内存区,函数外面的引用仍然指向原内存堆区,不会改变原变量的值(不可变对象的操作、可变对象的赋值操作)。而如果在内部通过append、remove函数改变了堆区的值的话,外部的引用仍然会改变。
可变对象与不可变对象总结 可变对象:list、dict、set 可变对象是指对象的内存值可以改变 不可变对象:int、tuple、str、bool 不可变对象是指对象的内存值不可以被改变。改变变量时,其实质是变量指向新的地址,即变量引用新的对象
(2) 传入参数的顺序和类型 参数顺序:必选参数,默认参数,可变参数,命名关键字参数,关键字参数
可变参数必须在关键字参数之前 默认参数 必须指向不可变对象 可变参数 形式def func(可变参数) 传入参数的个数可以是0个或者多个,其实是传入一个list或者tuple,函数接收的是个tuple。如果有个list或者tuple想直接传入func的话,可以直接用func(list)实现(即把想要传入的可变参数封装成list/tuple传入) 关键字参数 形式def func(**关键字参数) 允许传入0个或多个含参数名的参数,在函数内部组装成字典。传入的时候用参数名=参数值的形式。如果传入dict的话可以直接func(**dict) 命名关键字参数 形式def func(,city,job) 限制某几种参数名的输入,传入的时候必须只有,后面的这几种参数名可以选择,传入用fun(city=xxx,job=xxx)的形式 8)python的list和numpy.array(数组)的区别 1.list可以存放不同类型的数据,比如int,str,bool,float,list,set等,但是numpy数组中存放的数据类型必须全部相同,必须全为int或全为float
2.list中每个元素的大小可以相同,也可以不同,因此不支持一次性读取一列,而numpy数组中每个元素大小相同,支持一次性读取一列。numpy对二维数组的操作更为便捷。
3.list中数据类型保存的是数据存放的地址,即指针而非数据,比如一个有四个数据的列表a=[1,2,3,4]需要4个指针和4个数据,增加了存储空间,消耗CPU,但是a=np.array([1,2,3,4])只需要存放4个数据,节省内存,方便读取和计算
4.一个numpy的array是内存中的一个连续块,array中的元素都是同一类型的,所以一旦确定了一个array,它的内存就确定了,每个元素的内存大小都确定了。而list的每个元素是一个地址的引用,这个地址又指向了另一个元素,这些元素在内存里不一定是连续的,因此每当加入新元素,其实只是把这个元素的地址添加到list中。 9)类中self的概念 self代表实例本身,具体来说是该实例的内存地址
self的三种应用: 1.self为类中函数的第一个参数 在调用实例方法时,python解释器会自己把实例变量传递给函数中的self。如果类的实例方法的第一个参数不是代表实例的self,则调用实例方法时,该方法没有参数来接收自动传入的实例变量,程序会产生异常。
2.在类中,引用实例的属性 self.变量名
3.引用实例的属性目的是为实例绑定属性,写入或读取实例的属性
在类中,调用实例的方法 self.方法名() 10)tuple元祖和列表list,字典dict,集合set什么区别? ? tuple:元组,元素不可改(元组内的元素不可改,整个元组可修改) ? list:列表,元素可修改 可以使用+操作符进行拼接。 ? dict:字典,获取元素的方式不同。列表通过索引值获取,字典通过键获取;数据结构和算法不同。字典是 hash 算法,搜索的速度特别快;占用的内存不同。 ? set:集合,无序不重复元素集合。如果将字典转化为集合,仅仅只是将键(key)收录到集合中。如果想将字典中的值转化为集合,可以用字典函数values()。 11) python文件操作 ? f = open(“file_test”,“r”,encoding = “utf-8”)r只读,w写,r+读写, f1.close() ? f3.readline() 按行读,读取文件一行的内容 ? f.readlines() 读取所有的行到数组里面[line1,line2,…lineN] ? f.write(string):把string字符串写入文件。 ? f.writelines(list):把list中的字符串一行一行地写入文件,是连续写入文件,没有换行。
操作系统
1.进程与线程的区别
- 进程:系统进行资源调度和分配的最小独立单位。具有独立性,动态性,并发性,异步性。实现了os的并发性。进程间不共享数据。
- 线程:程序执行的最小单元,进程内线程间共享资源,系统调度。
- 协程:程序员调度,分解一个线程成为多个“微线程
联系:进程(主线程)创建了多个线程,各个子线程拥有自己的独立栈空间(存储函数参数、局部变量等),多个子线程与主线程共享堆、全局变量等非栈内存。一个程序至少有一个进程,一个进程至少有一个线程,线程依赖于进程而存在。 一个线程挂掉,会导致该线程所属的进程整个挂掉,进程中的其他线程也都挂掉,但是一个进程挂掉,不会影响其他进程。
- 进程间的通信方式:管道、消息队列、信号、共享内存
- 线程间的通信方式:全局变量、消息队列
2.常见锁
4.1 互斥锁 4.2 多重入锁(允许一个进程/线程多次拿到锁) 4.3 自旋锁 (CPU不断检查锁是否可用) 4.*** 4.5 条件 4.6 信号量 (一次允许多个线程操作锁对象) 4.7 读写锁 (一次只有一个写者,多个读者)
3.死锁的概念、原因、解决方法
(1)概念:死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所占用不会释放的资源而处于的一种永久等待状态。死锁的四个必要条件: ? 互斥条件(Mutual exclusion):资源不能被共享,只能由一个进程使用。 ? 请求与保持条件(Hold and wait):已经得到资源的进程可以再次申请新的资源。 ? 非剥夺条件(No pre-emption):已经分配的资源不能从相应的进程中被强制地剥夺。 ? 循环等待条件(Circular wait):系统中若干进程组成环路,该环路中每个进程都在等待相邻进程正占用的资源。 java中产生死锁可能性的最根本原因是:1)是多个线程涉及到多个锁,这些锁存在着交叉,所以可能会导致了一个锁依赖的闭环;2)默认的锁申请操作是阻塞的。如,线程在获得一个锁L1的情况下再去申请另外一个锁L2,也就是锁L1想要包含了锁L2,在获得了锁L1,并且没有释放锁L1的情况下,又去申请获得锁L2,这个是产生死锁的最根本原因。
(2)、避免死锁: ? 方法一:破坏死锁的循环等待条件。 ? 方法二:破坏死锁的请求与保持条件,使用lock的特性,为获取锁操作设置超时时间。这样不会死锁(至少不会无尽的死锁) ? 方法三:设置一个条件遍历与一个锁关联。该方法只用一把锁,没有chopstick类,将竞争从对筷子的争夺转换成了对状态的判断。仅当左右邻座都没有进餐时才可以进餐。提升了并发度。
(3)、死锁解除 ①资源剥夺法 挂起某些死锁资源,抢占其资源分配给其他的死锁进程 缺点:被挂起的进程可能会导致饥饿
②终止进程法 强制终止部分、甚至全部死锁进程,并剥夺其所占有的资源 缺点:付出代价较大,有些进程已近结束,终止就会功亏一篑
③进程回退法 让一个或多个进程回退到不会发生死锁的地步 缺点:要求系统记录进程的历史信息,设置还原点,浪费系统资源
数据库知识
1.事务的四大特性
1.1四大特性
保持事务的原子性是指操作发生异常时,需要对该事务所有之前执行过的操作进行回滚。首先要设置autocommit=0,就是默认不能隐式提交,需要手动commit提交。回滚需要undo日志实现,undo日志存放之前修改过的记录,事务发生异常触发roll back,会按照日志逻辑回滚undo日志的操作。
一致性可以理解为事务对数据完整性约束的遵循。事务执行前后都是合法的数据状态,不会违背任何数据完整性 从数据库层面,数据库通过原子性、隔离性、持久性来保持一致性。
用锁和隔离机制。锁是需要用户自己定义的,隔离机制是数据库提供的。
在无并发事务的情况下,持久性依赖于原子性;在有并发事务的情况下,持久性依赖于原子性和隔离性。
即使数据库系统遇到故障也不会丢失已提交事务的操作,通过redo日志来实现的。基本步骤如下图 :①当在事务中尝试对数据进行更改时;②首先将数据从磁盘读入内存,更新内存缓存的数据。③生成一条redo日志缓存,放在redo日志的缓冲区;④事务真正提交时将缓冲区中的日志写入redo日志做持久化保存;⑤把内存中的数据同步到磁盘上。
1.2隔离级别 在并发状态下,事务会出现一些问题,主要有三种问题:
? 脏读: 一个事务能读到另外一个事务没有提交的数据。(举例:A给B转了100块,但是A转完并没有提交该事务,B读到了自己的账户多了100块,此时A发现转账错误之后就回滚了该操作,此时就称为脏读)
? 不可重复读: 一个事务的两次查询操作数据不一致,可能是两次查询过程中插入了一个事务更新了原有的数据(举例:两个并发事务A和B,A首先查询自己的账户是100块,B此时提走了A账户的50块,A再次查询发现此时账户只剩下了50块,两次查询操作结果不同)
? 幻读: 在一个事务的两次查询中数据不一致,由于其他事务的提交,发现了原来没有的数据或者原有的数据不见了 (不可重复读与幻读相似,不可重复读侧重于另一个事务对数据库的修改操作,而幻读则侧重于另一个事务对数据库的增加和删除操作)
Ⅰ.读未提交 允许读取另一个事务尚未提交的数据,可能会造成脏读、不可重复读、幻读
Ⅱ.读已提交 允许读取并发事务已经提交了的数据,可以阻止脏读,但是不能避免不可重复读和幻读
Ⅲ.可重复读 在一个事务的操作过程中,不能读取到别的事务对该数据库的修改增删操作,可以阻止脏读和不可重复读,但是不能避免幻读(mysql默认级别)
Ⅳ.串行化 所有的事务依次逐个执行,当表被一个事务操作时,其他事务的操作不可以进行,进入排队状态,等待当前操作事务提交后才能继续执行操作。
1.3 锁 按使用方式分为乐观锁、悲观锁
按粒度分为表级锁、行级锁、页级锁 (InnoDB支持行级锁、表锁,MyISAM只支持表锁) 锁的粒度越小,系统开销越大,但相应的并发性就越高。因此选择锁粒度的时候需要在系统开销和并发性间权衡。
锁的类型上划分为互斥锁/写锁/X锁、共享锁/读锁/S锁
2.索引
2.1 索引定义 索引能快速找到某一列中有一特定值的行。不必挨个儿去查看记录的内容。索引是对数据库中一列或者多列的值进行排序的一种数据结构,以索引文件的形式存储在磁盘上,占据一定的物理空间。 2.2 索引优点
- 提高查询的性能,大大减少表的检索行数
- 可以建立唯一索引或者主键索引,保证数据库中每一行数据的唯一性
- 加速表与表之间的连接
- 在使用分组group by和排序order by子句进行数据检索时,可以显著减少查询中分组和排序的时间(数据库的记录会重新排序)
2.3 索引缺点 1.索引文件占据物理空间(空间) 2.对表中数据进行增删改查时,索引也要动态地维护,降低了数据的维护速度(时间) 2.4 索引的分类
- 唯一索引: 数据列不允许重复但是允许为null,一个表允许多列创建多个唯一索引
- 主键索引: 数据列不允许重复,也不允许为null,一个表中只能有一个主键,但是可以有多个列共同组成的联合主键
- 普通索引: 没有唯一性的限制,允许为null,只是简单的加速查询
- 联合索引: 多个索引的组合,必须满足最左前缀原则
- 全文索引: 查找全文中的关键字, mysql的Innodb引擎不支持 myISAM引擎支持
2.5 索引设计原则 1.对查询频次较高、数据量较大的表建立索引 2.使用唯一索引,区分度越高,使用索引的效率越高 3.使用短索引,减少存储空间,提升I/O效率 4.利用最左前缀,在组合索引中比如有(name,city,age)的话,只支持(name)、(name,city)、(name,city,age)这三种组合的检索,查询时必须包含索引的最左列,不能跳过某个字段进行查询 5.为经常需要排序 分组和联合操作的字段建立索引 6.限制索引的数目,索引并非越多越好
2.6 索引失效的场景
-
复合索引不满足最左前缀原则 -
模糊查找时like ‘%'以%开头 -
where索引列有运算 -
where索引列有函数 -
mysql估计用全表扫描要比用索引更快,则不使用索引 -
查询条件中有or的话可能会造成索引失效,除非or的每个列都加上索引
2.7 不推荐使用索引的场景
-
数据唯一性比较差,重复比较多的情况下不要使用索引 -
频繁更新的字段不适用索引(导致索引维护困难)
2.8 索引的数据结构 1.B树 [ceil(m/2)-1]<=n<=m-1 优点:层级结构较低,且冗余节点较少
2.B+树 (1)n叉B+树最多含有n个key,B树最多含有n-1个key (2)B+树的叶子节点保存所有的键值信息和数据,依key大小排序,所有的非叶子节点只存储键值,所有的叶子节点都通过指针连接在一起,形成了一个有序链表(支持翻页)。(B树中每个节点都存储有键值和数据) 支持翻页:每个磁盘块存储一个节点,称为一页。连续查询多个节点则称为翻页 (3)优势: 相同数据集来说B+树的层级结构比二叉树、B树小,因此搜索速度更快;查询任何key都要从root走到叶子,因此查询效率更稳定;所有数据均有序存储在叶子节点,使得范围查找、排序查找、去重查找变得简单易行(B树数据分布在各个节点,包括非叶子节点,不便于范围等查找) (4)缺陷:因为有冗余节点数据,因此会造成内存的浪费。
3.hash 特点: 1.hash表是key-value形式,通过一个散列函数,能够根据key快速找到对应的value 2.检索时无需使用树状结构那样从根节点到叶子节点逐级查找,只需要一次hash算法即可定位到相应位置,速度较快。 hash索引的缺点 1.hash索引只能够进行单值查找,不支持范围查询,而B+树支持范围查询(hash函数过滤后的键值大小关系不能保证和源数据的大小关系一致) 2.hash索引不能利用索引完成排序,以及像like 'xxx%'这样的模糊查询(本质上也是一种范围查询) 3.hash索引不支持多列联合索引的最左匹配原则 4.hash索引在重复值较高的时候,因为存在哈希碰撞导致性能极低。 5.hash索引只适用于存储数据重复度很低、对数据等值查询、无排序和范围查询的情况,效率较高。、
2.9索引的实现原理? 索引底层采用的数据结构是:B + Tree 通过B Tree缩小扫描范围,底层索引进行了排序,分区,索引会携带数据在表中的“物理地址”,最终通过索引检索到数据之后,获取到关联的物理地址,通过物理地址定位表中的数据,效率是最高的。 select ename from emp where ename = ‘SMITH’; 通过索引转换为: select ename from emp where 物理地址 = 0x3;
3.delete drop truncate区别
DML 数据操纵语言(insert、update、delete) DQL 数据查询语言(select from where) DDL 数据定义语言(create 表/视图/索引) 是隐性提交的 不能roll back DCL 数据控制语言(grant授权 roll back commit)
1.delete是DML,数据操纵语言,执行delete时每次从表中删除一行,删除操作会被记录在redo和undo表中以便进行回滚 2.drop是DDL,数据定义语言,会隐式提交,不能回滚,不会触发触发器,会删除表结构以及所有的数据,并将占用的空间全部释放 3.truncate是DDL,会隐式提交,不能回滚,不会触发触发器,会删除表内的所有数据,但是会保留表结构,即留下一个新表。
DML和DDL的区别 1.DML数据操纵语言,手动控制开启事务、提交事务和回滚 2.DDL数据定义语言,隐形提交,不能回滚
4.连接
连接用于连接多个表,使用join关键字,条件语句使用on而非where。查询效率快于子查询 与外键的区别是,允许两个表内各有部分项不相关(外键中子表中出现的字段父表中必须出现过,但是连接的两个表每个都可以有部分字段与另一个表没有联系) 1.内连接 等值连接 非等值连接 自连接 没有关联的行在查询结果中不显示 查询两个表的交集 2.外连接 保留了没有关联的行 左连接 保留左边表没有关联的行 右连接 保留右边表没有关联的行 全外连接 保留左边和右边两个表没有关联的行
5.where having on的区别
where和having区别 ①where先筛选结果再用分组函数(如果有的话)计算, having则是在分组函数计算结果出来之后再进行筛选,查询结果返回符合条件的分组, where是在group by之前执行的,所以where后面不能使用分组函数进行数据过滤,只能使用该表内的字段进行过滤,筛选出过滤后的结果之后才能使用分组函数;having则是在group by之后执行,是从分组结果中用分组函数进行分组的过滤。 where性能优于having ②where优先级高于having,where既可以搭配select子句使用,也可以和group by搭配使用;having只可以搭配group by 语句使用。
on和where区别 所有查询操作都会返回一个临时表,查询结果从临时表中得到 ①on是根据限制条件对数据库记录进行过滤,然后产生临时表,一般用于连接(内外连接);where则是在临时表产生后根据限制条件从临时表中筛选数据。 ②on限制条件发生时间较早,临时表较小,所以性能优于where
6.查询速度慢如何解决
慢查询日志 用于记录响应时间超过阈值的语句,实际时记录运行时间10s以上的语句 首先分两种情况: ①大多数情况正常,偶尔很慢 1.数据库在刷新脏页 因为redo log的容量有限,如果redo log写满了之后需要暂停其他操作,把redo日志里的数据同步到磁盘,导致正常sql语句执行很慢 2.所访问的数据表被加锁 如果该表被加锁或者要使用到的表中的某一行被加锁,也会导致慢查询
②针对一直都很慢的情况 1.可能表中并没有索引,或者没有用到索引(比如使用like关键字时like ‘%xx’ 这种%位于匹配字符的第一个字符时,索引就不会起作用;或者多列索引不遵循最左前缀的原则 尽量使用唯一索引或者主键索引等区分度较大的索引;对查询操作较为频繁的列建立索引) 2.优化数据库结构 对于需要经常联合查询的表,可以建立中间表以提高查询效率。将原来的联合查询改为对中间表的查询。 3.分解关联查询 将一个大的查询分解为多个小查询,对每一个表做一次单表查询,将查询结果在应用程序中进行关联。
7.数据库的三大范式
第一范式 1NF 数据表中所有字段都是不可分割的原子值,字段值还可以继续拆分的就不满足第一范式
第二范式 2NF 在满足第一范式的前提下,除主键外得每一列,都必须完全依赖于主键,称之为满足第二范式。
第三范式 3NF 必须在满足第二范式、第一范式的前提下,除开主键列的其他列之间不能有传递依赖关系。
8.表的数据结构
表中数据结构的区别 int(10) char(10) varchar(10)的区别 (1)int(10)中10表示显示的数据的长度,而不是存储数据的大小,char(10)和varchar(10)的10表示存储数据的大小,即表示存储了多少个字符 (2)char(10)表示存储定长的10个字符,不足的部分用空格补齐(空格表示占位、不算一个字符),占用更多存储空间; (3)varchar(10)表示存储10个变长的字符,无需补0以达到10字符,空格也按照存储空间存放。
Linux常用查询命令
1.文件/目录相关
1.ls 列出当前文件夹下的内容 2.pwd 查看当前所在目录 3.cd 切换目录 . 当前目录 … 上级目录 cd ~ 切换到主目录 cd - 在最近两次操作目录间横跳 4.touch 创建文件 5.mkdir 新建目录 6.rm 删除文件 rm -r 目录名 删除目录 rm -f 强制删除,无该文件也无需提醒 rm -r * 删除当前目录下所有文件和目录 7.cp 复制文件 8.mv 移动文件
2.显示文件内容相关
1.grep 搜索文本 文件名 搜索文本内容 -n 显示行号; -v 不包括该内容的 ; ^a查找以a开头的行; a$ 查找以a结尾的行 2.cat 显示文件完整内容 3.more 分屏显示文件内容 4.less 分屏显示文件内容,上下键控制翻页 5.head 打印文件中的前n行 6.tail 打印文件中的末尾几行 显示10~15行内容 tail 文件名 +10| head +5 7.find find 目录 -name 搜索字符 搜索名字为xxx的文件 可以使用通配符 find 目录 -size 数据块 搜索大小为xxx的文件,1数据块=0.5kB +n 大于 -n 小于 n等于 组合条件:-o 或者;-a 并且 find \ -size +163840 -a -size -204800 查找根目录下大于80MB小于100MB的文件 find 目录 -group xxx 查询所属组为xxx的文件 find 目录 -user xxx 查询所属者为xxx的文件 8.wc wc 文件 -l 统计文件的行数 wc 文件 -w 统计文件的单词数 wc 文件 -c 统计文件的字节数 3.进程与内存相关 1.top 动态实时显示cpu、内存、进程使用情况 2.ps 列出进程 ps -ef | grep xxx 查看xx进程,可以获得pid ps -a 列出所有运行中的进程 pgrep -l xxx 查看xx进程的pid netstat -atnp| grep xxx 查询pid
3.kill 杀死进程
kill -9 pid 强制杀死某进程 4.netstat netstat -atnp | grep xxxx 查看端口号/状态的进程pid 查看端口占用情况 lsof -i 查看指定端口占用情况 lsof -i:端口号 5.free 显示当前内存使用情况 6.df 以字节形式显示磁盘使用情况
|