【Redis】面试题 GEO地理位置信息
目录
面试
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 删库
在上下文管理器中不需要主动提交:
在with里面出了异常也会回滚:
悲观锁使用:
本质是锁定一个对象。
如果是all就是表锁。
python中的锁会锁住所有的请求,所有人并行变成串行。mysql行锁会锁住一行,其他人可以修改别的行,锁的粒度小,效率高。
mysql读锁和写锁的粒度更小?
操作同一行,需要等待前一个请求操作完。
循环的目的是 ---> 如果库存足一定能成功。
乐观锁和悲观锁的对比:
乐观锁先进入程序也不一定成功。悲观锁先进入程序,基本上就成功了。
并发量大,被修改的概率大,如果不重试,乐观锁失败概率就更大了.
今日内容
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 不一样的部分剔除出来 人工审查一下。
存储经纬度:
获取经纬度:
计算两个经纬度之间的距离:
直线距离:
距离北京100km内有哪些城市:
可以实现附近的人功能。
# 补充
存储到mysql表中,计算的时候可以使用redis计算。
redis geo存储的是什么类型?有序集合
经纬度相当于 有序集合的'分数'
在内存中有序集合对象来存储。
每个人登录,记录登录地址(经纬度)。记录IP地址和真实地址。