跳至主要內容

海康威视

kfkfka zkye...大约 24 分钟

海康威视

测试开发

进程与线程的区别,2者与程序的关系?

  1. 线程由进程创建,在进程下运行,一个进程可以包含多个线程

  2. 不同进程间很难通信(pipe无名管道,有名管道,共享内存,信号量,);线程间通讯相对容易,他们共享本进程的资源

  3. 进程间互不影响,一个线程挂掉可能导致整个进程挂掉

  4. 进程可以扩展到多核,比如同一进程创建的线程可运行于不同核心。python 因为GIL 所以进程的多个线程,都在同一CPU中,C,Java 等的多线程, 没有这个限制 是真正的并行。

  5. 进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。如"互斥锁"

  6. 进程使用的内存地址可以限定使用量,信号量

  7. 线程只由相关堆栈open in new window系统open in new window栈或用户栈open in new window寄存器open in new window和线程控制表TCB组成。寄存器open in new window可被用来存储线程内的局部变量open in new window,但不能存储其他线程的相关变量。

  8. 进程open in new window是资源分配的基本单位。所有与该进程有关的资源,都被记录在进程控制块open in new windowPCB中。以表示该进程拥有这些资源或正在使用它们。

    另外,进程也是抢占处理机的调度单位,它拥有一个完整的虚拟地址空间。当进程发生调度时,不同的进程拥有不同的虚拟地址空间,而同一进程内的不同线程共享同一地址空间。

  9. 线程上下文切换比进程上下文切换要快得多。

进程5个状态

创建、阻塞【阻塞挂起】、就绪【就绪挂起】、运行、结束

数据库可以根据哪些字段(不)索引?

建议索引:

  • 经常需要搜索的列上,可以加快搜索的速度;
  • 在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构;
  • 经常用在连接的列上,这 些列主要是一些外键,可以加快连接的速度;
  • 经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的;
  • 经常需要排序的列上创 建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;
  • 经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度。

不建议:

  • 第一,对于那些在查询中很少使用或者参考的列不应该创建索引。这是因 为,既然这些列很少使用到,因此有索引或者无索引,并不能提高查询速度。相反,由于增加了索引,反而降低了系统的维护速度和增大了空间需求。
  • 第二,对于那 些只有很少数据值的列也不应该增加索引。这是因为,由于这些列的取值很少,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即需要在表中搜索的数据行的比例很大。增加索引,并不能明显加快检索速度。
  • 第三,对于那些定义为text, image和bit数据类型的列不应该增加索引。这是因为,这些列的数据量要么相当大,要么取值很少。
  • 第四,当修改性能远远大于检索性能时,不应该创建索 引。这是因为,修改性能和检索性能是互相矛盾的。当增加索引时,会提高检索性能,但是会降低修改性能。当减少索引时,会提高修改性能,降低检索性能。因 此,当修改性能远远大于检索性能时,不应该创建索引。

数据库建立索引的优缺点

数据库索引的作用优点和缺点 · 建议/不建议创建索引的情况open in new window

优点:

  • 大大提高查询效率
  • 通过创建唯一索引,可以保证数据库表中每一行的数据唯一性
  • 可以加快表与表之间的连接
  • 在使用分组和排序子句进行数据索引时也可显著减少耗时
  • 可以使用优化隐藏器,提高系统性能

缺点:

  • 选取的字段不当不但无法提高查询效率,还可能拖慢查询时间;
  • 由于建立索引需要额外的时间,索引是动态维护的,当表中数据变动时,索引都要相应改变,所以一定范围的数据量级需要考虑建立索引的开销和直接查询的开销。
  • 需要占用额外的物理空间,且与数据量呈正相关。

数据库有哪些锁?

行锁、表锁、页锁【间隙锁,颗粒度不同】,读写锁,共享锁

删除数据表的方式

drop、delete、truncate,在执行速度上,drop > truncate > delete;

drop:drop table 表名称 删除内容和定义,删除的是整个表(结构和数据),同时删除表的结构所依赖的约束、触发器、索引,将表所占用的空间全释放掉。无法回滚,所以删除是不能恢复的,如果再次使用的话需要新建表。

delete:delete from 表名称 [where 列名称 = 值] 删除表中的行,不删除表的结构。执行删除的过程是每次从表中删除一行,并且将该行的删除操作作为事务在日志中保存,以便进行进行回滚操作。delete会根据指定的条件删除表中满足条件的数据,where就是条件判断。如果不指定where子句,那么删除表中所有记录。 delete操作不会减少表或索引所占用的空间。

truncate:truncate table 表名称 只是清空表,删除内容,释放空间,但不删除定义(保留表的数据结构open in new window)。且不会把删除操作记录记入日志保存,无法回滚,所以删除是不能恢复的。并且在删除的过程中不会激活与表有关的删除触发器【级联更新?】。执行速度快。

相同点

truncate和不带where子句的delete,drop都会删除表内的数据;

drop,truncate都是DDL语句(数据定义语言),执行后会自动提交;

不同点

语句类型:delete语句是数据库操作语言(DML),truncate,drop是数据库定义语言(DDL);

是否删除表结构:truncate和delete 只删除数据不删除表结构,truncate 删除后将重建索引(新插入数据后id从0开始记起),而 delete不会删除索引 (新插入的数据将在删除数据的索引后继续增加),drop语句将删除表的结构包括依赖的约束,触发器,索引等;

安全性:drop和truncate删除时不记录MySQL日志,不能回滚,delete删除会记录MySQL日志,可以回滚;

SQL的 char 和 vchar 区别

属性charvchar
长度0-255【2^8 - 1】0-65535【2^16 - 1】
可变性定长实际占用空间比预分配的空间大一到两字节(用于记录分配的实际空间长度)
碎片由于存储空间都是一次性分配的。为此某个字段的内容,其都是存储在一起的。单从这个角度来讲,其不存在碎片的困扰存储的长度是可变的。当其更改前后数据长度不一致时,就不可避免的会出现碎片的问题。故使用可变长度的字符型数据时,数据库管理员要时不时的对碎片进行整理。如执行数据库导出导入作业,来消除碎片

vchar不可因为其实际占用空间是变长,就给的过于慷慨,虽然物理空间如此,但内存确实按开始设置的值分配的。

对于vchar,若更改后,其原先的存储位置已经无法满足其存储的需求。此时系统就需要进行额外的操作。如根据存储引擎不同,有的会 采用拆分机制,而有的则会采用分页机制。这也是碎片的一个原因。

项目的难点

前期,起点高,没有基础

期间,redis事件,当初年少无知【源于一次后端无故挂掉,老师找来,一开始只是简单重启,半天后发现没问题就以为是什么玄学问题没放在心上;过了一天,又出现了,那肯定是出什么大问题了,我就开始观察出错的场景,发现问题列表获取没问题,但是用户登入接口却报错了,导致需要用户认证相关服务全部用不了了,然而我们也没动这方面代码,去看我们的关联代码没发现有什么问题,于是我们打算重启再观察一天,期间想起了平台的日志,过了一天果然又挂了,然后我在日志里发现了问题所在,Django里有一条报错,大致是说redis快照持久化写入硬盘出错,更进一步查看redis发现是该位置没有写入权限,当时查了很多资料,有说是配置文件里内存不足或硬盘空间不够,首先排除硬盘空间,那我把内存调高,结果一天后还是报错,很诡异的是还是一天后出问题,然后百思不得其解,我再导出它的配置仔细看,终于发现不对了,因为之前我是特意看过redis配置的,发现它的持久化目录被改到了好像是/etc/还是哪个需要管理员权限的目录下,我当时就有点懵逼,咋自己目录就变了,再有目标地一查发现,原来是被黑客攻击了,当时我为了图方便,直接将redis的端口暴露到了公网,也没设密码,年少无知呐】

Redis基本数据类型

  • 字符串(STRING
  • 列表(LIST
  • 集合(SET
  • 哈希(HASH
  • 有序集合(ZSET
结构类型存储的值
STRING字符串、整数或浮点数
LIST一个链表,上面的每个节点都是一个字符串
SET包含若干个字符串的无序集合,且集合中的元素都是唯一的
HASH包含键值对的无序散列表
ZSET成员中的字符串与分值的有序映射,其排序由分值决定

其中有序集合可以说是redis特有,不过应该是在Python3.6后它的dict和set也支持有序集合。

Redis 用户登录缓存选用何种数据类型,理由是?

hash,实现的 dict

Redis持久化

RDB:在指定时间间隔内生成数据集的时间点快照

AOF:

  • 记录服务器所有的写操作
  • 新的操作命令将被通过追加的方式写入文件尾部
  • 当服务器启动时,通过重新执行这些命令还原数据集

优缺点

Redis主从复制

扩展 Redis的读性能,并为 Redis提供故障转移支持

执行复制的从服务连接主服务器

  • 接受主服务器发送的初始副本
  • 接受主服务器执行的所有写命令

在从服务器上执行所有写操作,实时更新数据集

读命令可以发往任意服务器

保障:哨兵机制

Redis哨兵机制

由redis相互监督,一个作为主,间隔呼叫其他redis

Redis 3种缓存失效问题

缓存雪崩:由于某时刻数据大批量过期,此时数据库收到大量查询导致压力过大挂掉。

缓存击穿:由于某条热点数据不在缓存中但在数据库中,导致大量查询直接打到数据库,数据库瞬间压力过大挂掉。

缓存穿透:由于某条数据在缓存和数据库中都不存在,所有哪怕查询过数据库也无法将确切的值写入缓存,下次请求照样打到数据库,就好像缓存不存在一样,当请求量一大,数据库照样挂掉。

解决方法

雪崩

  • 为缓存数据固定的过期时间加上一个合理的随机时间,防止同时过期。
  • 使用Redis集群,将热点数据平均分配到各个服务器上,避免单点故障。
  • 设置热点事件永不过期。

击穿

  • 设置热点数据永不过期。
  • 接口限流与熔断,进行服务降级,失败快速返回响应机制。
  • 设置互斥锁,在并发查询请求中,只允许第一个请求线程拿到锁执行数据库查询,其他请求线程进行阻塞,等到数据写入缓存后,直接走缓存。【可借助Redis分布式锁】

穿透

  • 接口层增加校验。如用户鉴权校验,id做基础校验,id<=0的直接拦截。
  • 缓存设置无效key,直接key-null,不过需要将失效时间设置短点,如30秒,防止正常数据访问失败。
  • 布隆过滤器。

Redis 过期时间删除策略

定时删除:放入数据后,设置定时器,读秒完毕将对应数据从dict中删除。

  • 优点:内存友好,数据一旦过期就会从内存中删除。
  • 缺点:CPU不友好,定时器需要占用较多的CPU资源,同时频繁的删除操作也是。

惰性删除:数据过期时,不做任何操作。当访问到过期数据时,直接返回NULL,再将数据从内存删除。

  • 优点:CPU友好,过期数据只有在访问到时才会被删除。
  • 缺点:内存不友好,可能会占用大量内存。

定期删除:上述2者的折中方案,每隔一段时间对redis中的所有数据库的expires依次进行随机抽取检查。

redis中会维护一个current_db变量来标志当前检查的数据库。current_db++,当超过数据库的数量的时候,会重新从0开始。

定期检查就是一个循环,每轮循环都会对current_db对应的数据库依次随机取出x个key,查看是否过期【expireIfNeeded()】,过期则执行删除,如果过期的key占到了25%以上,就会继续检查当前数据库,若小于25%则继续检查下一个数据库。当执行时间超过规定的最大执行时间会退出本次检查,下次检查将延续当前current_db对应的数据库。

  • 优点: 通过控制定时时间来动态的调整CPU和内存之间的状态,十分灵活。
  • 缺点:定期删除的定时时间十分重要,如果时间过短,就会对CPU造成很大压力。如果时间过长,就会造成过期数据挤压内存。

Redis 采取的是 惰性删除 + 定期检查的策略

主从复制时,从不会主动删除主数据库设置的过期key,而是根据惰性删除策略直接返回null,删除由主库执行过期删除后发送 del 命令进行同步。【我想这也是主从中主的概念,防止出现数据冲突】

Redis 内存淘汰策略

Redis 中的内存只有达到了阀值,才会触发内存淘汰算法,这个阀值就是我们设置的最大运行内存,在配置文件redis.conf中,通过参数 maxmemory <bytes> 来设置。

1、volatile-lru:淘汰所有设置了过期时间的键值中最久未使用的键值;istep选这个

2、allkeys-lru:淘汰整个键值中最久未使用的键值;

3、volatile-random:随机淘汰设置了过期时间的任意键值;

4、allkeys-random:随机淘汰任意键值;

5、volatile-ttl:优先淘汰更早过期的键值;

6、noeviction:不淘汰任何数据,当内存不足时,新增操作会报错,Redis 默认内存淘汰策略;

QPS 和 TPS

QPS即每秒查询率,是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准。

  • QPS = 并发量 / 平均响应时间
  • 并发量 = QPS * 平均响应时间

TPS【Transactions Per Second】(每秒传输的事物处理个数),即服务器每秒处理的事务数。TPS包括一条消息入和一条消息出,加上一次用户数据库访问。(业务TPS = CAPS × 每个呼叫平均TPS)

TPS是软件测试结果的测量单位。一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程。客户机在发送请求时开始计时,收到服务器响应后结束计时,以此来计算使用的时间和完成的事务个数。

一般的,评价系统性能均以每秒钟完成的技术交易的数量来衡量。系统整体处理能力取决于处理能力最低模块的TPS值。

7层模型 / 5层模型

【TCP/IP 5层模型】物理层 -> 数据链路层 -> 网络层 -> 运输层 -> 应用层

【传统 7层模型】网络层 -> 数据链路层 -> 网络层 -> 传输层 -> 会话层 -> 表示层 -> 应用层

作用

TCP / UDP 区别

TCP 3次握手

TCP 4次挥手

Http 和 Https

Http 连接过程

Https 连接过程

在 Http 连接的基础上,在它之前先进行 SSL 握手,

常见状态码

2xx

  • 200:

4xx

  • 403:
  • 404:

5xx

3xx

  • 301:
  • 302:
  • 304:

Python 基本数据类型,list 和 set 、和 tuple 区别

Python 闭包、装饰器

闭包

看看维基百科中的解释:

在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。

官方的解释总是不说人话,but talk is cheap,show me the code:

# print_msg是外围函数
def print_msg():
    msg = "I'm closure"

    # printer是嵌套函数
    def printer():
        print(msg)

    return printer


# 这里获得的就是一个闭包
closure = print_msg()
# 输出 I'm closure
closure()

msg是一个局部变量,在print_msg函数执行之后应该就不会存在了。但是嵌套函数引用了这个变量,将这个局部变量封闭在了嵌套函数中,这样就形成了一个闭包。

结合这个例子再看维基百科的解释,就清晰明了多了。闭包就是引用了(上层函数)自有变量的函数,这个函数保存了执行的上下文,可以脱离原本的作用域独立存在

装饰器

装饰器这一语法体现了Python中函数是第一公民,函数是对象、是变量,可以作为参数、可以是返回值,非常的灵活与强大。

普通的装饰器一般是这样:

import functools

def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('call %s():' % func.__name__)
        print('args = {}'.format(*args))
        return func(*args, **kwargs)

    return wrapper

这样就定义了一个打印出方法名及其参数的装饰器。

调用之:

@log
def test(p):
    print(test.__name__ + " param: " + p)
    
test("I'm a param")

输出:

call test():
args = I'm a param
test param: I'm a param

装饰器在使用时,用了@语法,让人有些困扰。其实,装饰器只是个方法,与下面的调用方式没有区别:

def test(p):
    print(test.__name__ + " param: " + p)

wrapper = log(test)
wrapper("I'm a param")

@语法只是将函数传入装饰器函数,并无神奇之处。

值得注意的是@functools.wraps(func),这是python提供的装饰器。它能把原函数的元信息拷贝到装饰器里面的 func 函数中。函数的元信息包括docstring、name、参数列表等等。可以尝试去除@functools.wraps(func),你会发现test.__name__的输出变成了wrapper。

带参数的装饰器

装饰器允许传入参数,一个携带了参数的装饰器将有三层函数,如下所示:

import functools

def log_with_param(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print('call %s():' % func.__name__)
            print('args = {}'.format(*args))
            print('log_param = {}'.format(text))
            return func(*args, **kwargs)

        return wrapper

    return decorator
    
@log_with_param("param")
def test_with_param(p):
    print(test_with_param.__name__)

看到这个代码是不是又有些疑问,内层的decorator函数的参数func是怎么传进去的?和上面一般的装饰器不大一样啊。

其实道理是一样的,将其@语法去除,恢复函数调用的形式一看就明白了:

# 传入装饰器的参数,并接收返回的decorator函数
decorator = log_with_param("param")
# 传入test_with_param函数
wrapper = decorator(test_with_param)
# 调用装饰器函数
wrapper("I'm a param")

输出结果与正常使用装饰器相同:

call test_with_param():
args = I'm a param
log_param = param
test_with_param

至此,装饰器这个有点费解的特性也没什么神秘了。

Python 赋值、浅拷贝、深拷贝(具体函数)

等号赋值:相当于为原来的对象打一个新的标签,两个引用指向同一个对象,修改其中的一个:若是可变对象,将在原对象上直接修改,即指向的地址值发生改变,因此另一个也会产生变化;若是不可变对象,则是新分配了一个地址空间,直接将该变量指向该处,对于原对象不做修改,故不会影响另一个。

浅拷贝:两种情况,1. 浅拷贝的值是不可变对象(数值、字符、元组)时,和等于赋值一样,对象的id值和浅拷贝原来的值相同;2. 如果是可变对象(列表、字典等),a. 一个简单的没有嵌套的对象,复制前后的对象相互之间不会影响,b. 对象中有复杂子对象,如列表嵌套,如果改变原来的对象中复杂子对象的值,浅拷贝的值也会受影响,因为在浅拷贝时只复制了子对象的引用(只拷贝父对象)

深拷贝:新建一个对象,把原来对象的内存完全复制过来,改变复制后的对象,不会改动原来内存的内容。(两个对象在复制之后是完全独立的对象)

举个例子,注意区分浅拷贝和直接赋值的差异:

# 可以用 id() 来直接查看各变量的 id 值(标识一个对象唯一身份)
# =直接赋值,不可变对象
>>> a = 1
>>> b = a
>>> a = 2
>>> b
1
# =直接赋值,可变对象
>>> import copy
>>> a = [1,2,3]
>>> b = a
>>> a.pop()
3
>>> b
[1, 2]
# 浅拷贝
>>> a = [1,2,3,[4]]
>>> b = copy.copy(a)
>>> b
[1, 2, 3, [4]]
>>> a.pop(0)
1
>>> b
[1, 2, 3, [4]]
>>> a[-1][0] = 0
>>> b
[1, 2, 3, [0]]
>>> a[-1] = 0
>>> b
[1, 2, 3, [0]]  # 生动说明浅拷贝只会复制可变对象内可变对象的引用
# 深拷贝
>>> a = [1,2,3,[4]]
>>> b = copy.deepcopy(a)
>>> a[-1][0] = 666
>>> b
[1, 2, 3, [4]]

Python 怎么写多线程?

#coding=utf-8
from multiprocessing import Pool
from threading import Thread

from multiprocessing import Process


def loop():
    while True:
        pass

if __name__ == '__main__':

    for i in range(3):
        t = Process(target=loop)
        t.start()

    while True:
        pass

多线程

#coding=utf-8
from multiprocessing import Pool
from threading import Thread

from multiprocessing import Process

"""
    threading.local的作用:为每个线程开辟一块空间进行数据的存储
    空间与空间之间数据是隔离的
"""

def loop():
    while True:
        pass

if __name__ == '__main__':

    for i in range(3):
        t = Thread(target=loop)
        t.start()

    while True:
        pass

Python 多线程、GIL,与其他程序区别

全局解释锁,由其解释器 CPython 支持,是一把全局排他锁几乎等于Python是个单线程的程序。

Python 多线程与 GIL

Python 内存管理机制,垃圾回收机制

内存管理:

垃圾回收:

Django、FastApi、Flask

上牛客

PyTest 详细介绍

自动化测试框架

接口自动化测试平台框架底层逻辑交互实现

测试按测试层次划分

单元测试 -> 集成测试 -> 系统测试

单元测试:

集成测试:

系统测试:

接口测试、回归测试

常用 Linux 命令

chmod 777 test # 数字代表的权限
du -h
free -h
df -h
lsof # 当前系统打开的文件情况,在linux环境下,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件。
top # 查看CPU及内存使用情况,有进程的运行情况。输入“1”可以查看单核运行情况
tail -f log # -f 表示循环读取,以此实时查看日志
more / less / cat # more只能Enter键往下翻,less可以移动光标,cat直接全部输出
sed / head

Linux 怎么实现定时任务,Django呢?

Docker

镜像 - 容器 - Engine,镜像和容器的关系就好比类和实例化对象

docker域 - 守护进程

Postgresql

测试实例设计

微信视频

微信点赞

微信转账

怎么看待测开岗?(对测开职位的理解?)

一开始所实话也以为是测试什么程序的岗位,后来我上网了解了很多才发觉,

未来职业规划

说实话,我这个人不太喜欢长远的规划,不过会有一个大体的方向在心中。

首先,我是想在杭州定居的,所以最近的打算就是找份好工作安定下来,我认为海康就是很好的选择,说实话,我一开始也面试过字节、阿里,人往高处走嘛,这点无可厚非,当然结果是没过,后来也和阿里的学长交流过,现在觉得,人看清自己的位置很重要,好的位置更能创造价值。所以,假如这次我能加入海康,那最好在接下来的表现中拿到转正,那我的想法就是好好工作,未来3-5年内争取能够带领小团队,根据我个人及公司情况往管理或者技术继续发展。

对Hikvision的了解,为什么选择Hikvision?

  1. 首先,自然是海康的实力是广受认可的,无论在技术上还是在市场上。
  2. 其次,可能是受我表哥的影响吧,我和他关系挺好,他就在杭州工作,家刚好也在滨江区,和海康挺近。我在高中的时候就考虑在杭州生活了,所以大学也是选在了杭州,海康在杭州自然是不错的选择。
  3. 之前也有面试过杭州的其他大厂,比如字节、阿里,我的技术栈和所招岗位需求不太匹配,而海康的这个测开岗是我目前了解到最匹配的岗位,加之我大学选择的也是移动互联方向,也学过一点硬件知识,和海康更是有更多的联系。
  4. 最后,我对将来的规划一是保研,如果不行就找个好平台好工作,目前情况保研可能有一些困难,我个人也是更偏向于就业的,如果我表现的好,也希望可以争取转正的机会,继续留在海康。

大学面临的最大挑战

istep算是一个吧,再讲讲我数学建模的经历吧。

学生工作中印象最深的一次经历

看你简历中写了2个安卓开发,为什么不选择安卓开发?

我可以把它当作课余的爱好,

你如何看待xxx消息?(一些关于公司的负面消息)

我觉得这些都是幸存者偏差罢了,只是极少数人的问题被放大了,目前站在一个第三者,我觉得这顶多说明少数组存在一定问题,但如果是我,既然当初我选择加入了公司,就应当真正把自己作为公司的一员,应当是与它一起慢慢成长,慢慢变好,而不是这种没用,甚至损害公司利益的行为。

牛客收藏

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.8