面试题 GEO地理位置信息

发布时间 2023-04-23 15:44:01作者: clever-cat

面试

1 http协议详情,http协议版本,http一些请求头

	-特点:
    	1 基于请求响应--》服务端不能主动给客户端推送消息---》websocket协议
        2 无状态无连接---》不能做会话保持---》才出现了cookie,session,token
        3 基于tcp之上的应用层协议
    -详情:
    	-请求协议:
        	请求首行:请求方式(get,post,delete),请求地址,请求http协议版本号/r/n
            请求头:key:value   (cookie,useragent,referer,x-forword-for)
            请求体:编码方式
        -响应协议:
        	响应首行:响应状态码(1xx,2xx),响应单词描述
            响应头:key:value    跨域问题的响应头
            响应体:html格式:浏览器中看到的   json格式给客户端使用
   	-协议版本
    	-0.9:
            HTTP协议的最初版本,功能简陋,仅支持请求方式GET,并且仅能请求访问HTML格式的资源
        -1.0:
            但是1.0版本的工作方式是每次TCP连接只能发送一个请求(默认短链接),当服务器响应后就会关闭这次连接,下一个请求需要再次建立TCP连接,就是不支持keep-alive
        -1.1:
            引入了持久连接(persistent connection),即TCP连接默认不关闭,可以被多个请求复用,不用声明Connection: keep-alive。客户端和服务器发现对方一段时间没有活动,就可以主动关闭连接。
        -2.0:
           多路复用:对于 HTTP/1.x,即使开启了长连接,请求的发送也是串行发送的,在带宽足够的情况下,对带宽的利用率不够,HTTP/2.0 采用了多路复用的方式,可以并行发送多个请求,提高对带宽的利用率 
        -3.0:
            HTTP3.0又称为HTTP Over QUIC,其弃用TCP协议,改为使用基于UDP协议的QUIC协议来实现
	

2 GET请求和POST请求的区别

    post更安全(不会作为url的一部分)
    post发送的数据更大(get有url长度限制)
    post能发送更多的数据类型(get只能发送ASCII字符)
    post比get慢
    post用于修改和写入数据,get一般用于搜索排序和筛选之类的操作

3 如何实现服务器给客户端发送消息,websocket是什么?

-websocket:一种通信协议,区别于http协议,可在单个TCP连接上进行全双工通信。允许服务端主动向客户端推送数据。浏览器和服务器只需要完成一次tcp连接,两者之间就可以建立持久性的连接,并进行双向数据传输
    -没有跨域问题
    -应用场景:
    	-只要涉及到服务端主动给客户端发送消息的场景,都可以用
        -实时聊天:我项目没写
        -服务端发生变化,主动向客户端推送一个通知
        -监控服务端的内存使用情况
    -djanog中使用channles模块实现
     
   -https://zhuanlan.zhihu.com/p/371500343

4 悲观锁和乐观锁,如何实现

-无论是悲观锁还是乐观锁,都是人们定义出来的概念,仅仅是一种思想,与语言无关
    -并发控制:
    	-1 并发控制:当程序中出现并发问题时,就需要保证在并发情况下数据的准确性,以此确保当前用户和其他用户一起操作时,所得到的结果和他单独操作时的结果是一样的,这种手段就叫做并发控制
        -2 没有做好并发控制,就可能导致脏读、幻读和不可重复读等问题
    -悲观锁:
    悲观锁:当要对一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行加锁以防止并发,让并行变成串行
    -这种借助数据库锁机制,在修改数据之前先锁定,再修改的方式被称之为悲观并发控制【Pessimistic Concurrency Control,缩写"PCC",又名"悲观锁"】
    - 之所以叫做悲观锁,是因为这是一种对数据的修改持有悲观态度的并发控制方式。总是假设最坏的情况,每次读取数据的时候都默认其他线程会更改数据,因此需要进行加锁操作,当其他线程想要访问数据时,都需要阻塞挂起。
    
    -悲观锁实现方式:
    	1 传统的关系型数据库(如mysql)使用这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁
    	2 编程语言中的线程锁,比如python的互斥锁
    	3 分布式锁:redis实现的分布式锁等

    -乐观锁:
    	通过程序实现(没有真正的一把锁),不会产生死锁
    	总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,只在更新的时候会判断一下在此期间别人有没有去更新这个数据,如果更新了,我们就不做修改
        
    -乐观锁实现方案:
    	1 CAS(Compare And Swap) 即比较并交换。是解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS 操作包含三个操作数——内存位置(V)、预期原值(A)和新值(B),执行CAS操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不做任何操作
  CAS会出现ABA问题,比如,你看到桌子上有100块钱,然后你去干其他事了,回来之后看到桌子上依然是100块钱,你就认为这100块没人动过,其实在你走的那段时间,别人已经拿走了100块,后来又还回来了。这就是ABA问题
  解决ABA问题:既然有人动了,那我们对数据加一个版本控制字段,只要有人动过这个数据,就把版本进行增加,我们看到桌子上有100块钱版本是1,回来后发现桌子上100没变,但是版本却是2,就立马明白100块有人动过
    2 版本号控制:在数据表中增加一个版本号字段,每次更新数据时将版本号加1,同时将当前版本号作为更新条件,如果当前版本号与更新时的版本号一致,则更新成功,否则更新失败
    3 时间戳方式:在数据表中增加一个时间戳字段,每次更新数据时将时间戳更新为当前时间戳,同时将当前时间戳作为更新条件,如果当前时间戳与更新时的时间戳一致,则更新成功,否则更新失败
        
        
        
        
        
# 悲观锁乐观锁使用场景
并发量:如果并发量不大,可以使用悲观锁解决并发问题;但如果系统的并发非常大的话,悲观锁定会带来非常大的性能问题, 建议乐观锁
响应速度:如果需要非常高的响应速度,建议采用乐观锁方案,成功就执行,不成功就失败,不需要等待其他并发去释放锁。乐观锁并未真正加锁,效率高
冲突频率:如果冲突频率非常高,建议采用悲观锁,保证成功率。冲突频率大,选择乐观锁会需要多次重试才能成功,代价比较大
重试代价:如果重试代价大,建议采用悲观锁。悲观锁依赖数据库锁,效率低。更新失败的概率比较低
读多写少: 乐观锁适用于读多写少的应用场景,这样可以提高并发粒度
    
    
    
#django中使用:下单,秒杀场景

# django中如何开启事务
	-全局开启:每个http请求都在一个事务中
        DATABASES = {
         'default': {
             'ENGINE': 'django.db.backends.mysql',
             'NAME': 'lqz',
             'HOST': '127.0.0.1',
             'PORT': '3306',
             'USER': 'lqz',
             'PASSWORD': 'lqz123',
              #全局开启事务,绑定的是http请求响应整个过程
             'ATOMIC_REQUESTS': True, 
         }
    }
    -每个视图函数开启
    	from django.db import transaction
        @transaction.atomic
        def seckill(request):
            
            
     -局部开启
    	from django.db import transaction
        def seckill(request):
            with transaction.atomic():
                pass
            return HttpResponse('秒杀成功')
    
    -保存点,回滚保存点Savepoint
    	-设置回滚点:sid = transaction.savepoint()
        -提交回滚点:transaction.savepoint_commit(sid)       transaction.commit()
        -回滚到回滚点:transaction.savepoint_rollback(sid)   transaction.rollback()
    - 事务提交后回调函数
    	transaction.on_commit(send_email)

#django中使用悲观锁
https://zhuanlan.zhihu.com/p/622987268
#django中使用乐观锁
django中使用:下单、秒杀场景。 异步加锁
django Smallinterfield --相当于--> mysql中的tinyint (存储的数据大小不一样)
删掉app下迁移文件 admin auth 删库

在上下文管理器中不需要主动提交:

image-20230419103916543

在with里面出了异常也会回滚:

image-20230419103955379

悲观锁使用:

本质是锁定一个对象。

image-20230419104323193

如果是all就是表锁。

python中的锁会锁住所有的请求,所有人并行变成串行。mysql行锁会锁住一行,其他人可以修改别的行,锁的粒度小,效率高。

mysql读锁和写锁的粒度更小?

image-20230419104923804

操作同一行,需要等待前一个请求操作完。

循环的目的是 ---> 如果库存足一定能成功。

乐观锁和悲观锁的对比:
乐观锁先进入程序也不一定成功。悲观锁先进入程序,基本上就成功了。
并发量大,被修改的概率大,如果不重试,乐观锁失败概率就更大了.

今日内容

1 GEO地理位置信息

# GEO(地理信息定位):存储经纬度,计算两地距离,范围等
	-根据经纬度---》确定具体地址的---》高德开放api---》返回具体地址
    
    
# redis 可以存储经纬度,存储后可以做运算,
	比如:两个经纬度之间距离 (直线距离)
    比如:统计某个经纬度范围内有哪些好友,餐馆
	
    
    
# 经纬度如何获取
	-跟后端没关系:只需要存
    -app,有定位功能
    -网页,集成了高德地图,定位功能
    
    
# redis存储
geoadd key  经度  纬度 名字
# 添加
geoadd cities:locations 116.28 39.55 beijing
# 查看位置信息
geopos cities:locations beijing #获取北京地理信息
    
#计算两个点距离
geodist cities:locations beijing tianjin km
    
# 计算附近的 xx
georadiusbymember cities:locations beijing 150 km
    
# 5大数据类型的 : 有序集合

GEO是用于存储经纬度的。
根据经纬度 ---> 确定具体地址 ---> 高德开放api ---> 返回具体地址 ---> 有误差

再使用百度api 不一样的部分剔除出来 人工审查一下。

存储经纬度:

image-20230419122846526

获取经纬度:

image-20230419122941432

计算两个经纬度之间的距离:

直线距离:

image-20230419123059460

image-20230419123045245

距离北京100km内有哪些城市:

image-20230419123131664

可以实现附近的人功能。

# 补充
存储到mysql表中,计算的时候可以使用redis计算。
redis geo存储的是什么类型?有序集合
经纬度相当于 有序集合的'分数'
在内存中有序集合对象来存储。
每个人登录,记录登录地址(经纬度)。记录IP地址和真实地址。