面经大杂烩1

大耗子 2021年02月04日 43次浏览

搜索树、平衡树,都有哪些特点呢?

平衡树(Balance Tree,BT) 指的是,任意节点的子树的高度差都小于等于1。常见的符合平衡树的有,B树(多路平衡搜索树)、AVL树(二叉平衡搜索树)等。

二叉搜索树的特点:对于树中的每个节点X,它的左子树中所有关键字值小于X的关键字值,而它的右子树中所有关键字值大于X的关键字值。

各种排序算法及其复杂度

排序方法时间复杂度(平均)时间复杂度(最坏)时间复杂度(最好)空间复杂度稳定性复杂性
插入排序O(n2)O(n2)O(n)O(1)稳定简单
希尔排序O(nlog2n)O(n2)O(n1.3)O(1)不稳定较复杂
选择排序O(n2)O(n2)O(n2)O(1)不稳定简单
堆排序O(nlog2n)O(nlog2n)O(nlog2n)O(1)不稳定较复杂
冒泡排序O(n2)O(n2)O(n)O(1)稳定简单
快速排序O(nlog2n)O(n2)O(nlog2n)O(nlog2n)不稳定较复杂
归并排序O(nlog2n)O(nlog2n)O(nlog2n)O(n)稳定较复杂
基数排序O(d(n+r))O(d(n+r))O(d(n+r))O(n+r)稳定较复杂

参考博客https://www.cnblogs.com/zwtgyh/p/10631760.html

什么是稳定排序?什么是不稳定排序?

  • 排序算法的稳定性通俗地讲就是能保证排序前,相等的数前后位置顺序和排序后它们两个的前后位置顺序相同。
  • 常见的冒泡排序、基数排序、插入排序、归并排序、桶排序、二叉树排序等都是稳定的排序算法。
  • 常见的选择排序,希尔排序,堆排序,快速排序等都是不稳定的排序算法。

AVL树有了解吗?

AVL树本质上是一颗二叉查找树,但是它又具有以下特点:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为平衡二叉树。

红黑树的特点是什么?AVL与红黑树的区别是什么?

红黑树的性质如下:

  1. 每个结点非红即黑;
  2. 根节点是黑的;
  3. 每个叶节点(叶节点即树尾端NULL指针或NULL结点)都是黑的;
  4. 如果一个结点是红色的,则它的子节点必须是黑色的;
  5. 对于任何结点而言,其到叶子点树NULL指针的每条路径都包含相同数目的黑结点。

区别:

  1. AVL树是高度平衡的,频繁的插入和删除,会引起频繁的rebalance,导致效率下降;
  2. 红黑树不是高度平衡的,算是一种折中,插入最多两次旋转,删除最多三次旋转。

说说TCP三次握手,四次挥手

https://codemouse.online/archives/2020-06-30202243

HTTP了解吗?与HTTPS的区别是什么呢?

  • http的全称是Hypertext Transfer Protocol Vertion (超文本传输协议),说通俗点就是用网络链接传输文本信息的协议。

  • http协议的缺点:
    通信使用明文,内容可能被窃听(重要密码泄露)
    不验证通信方身份,有可能遭遇伪装(跨站点请求伪造)
    无法证明报文的完整性,有可能已遭篡改(运营商劫持)

  • https就是再http的基础上套了一层ssl/tls的加密壳。

HTTPS如何保证安全传输鸭?

通过非对称加密传输密钥+对称加密传输数据+证书的方式。
https://www.cnblogs.com/kubidemanong/p/9390021.html

mysql事物

  1. 在MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。
  2. 事务处理可以用来维护数据库的完整性, 保证成批的 SQL 语句要么全部执行, 要么全部不执行。
  3. 事务用来管理 insert,update,delete 语句。

事务四大特征:
原子性( Atomicity, 或称不可分割性) 、
一致性( Consistency) 、
隔离性( Isolation, 又称独立性) 、
持久性( Durability) 。

mysql隔离级别

使用系统变量设置事务隔离级别
SET GLOBAL tx_isolation='REPEATABLE-READ'; #全局
SET SESSION tx_isolation='REPEATABLE-READ'; #局部

事务隔离级别脏读不可重复读幻读
读未提交(READ-UNCOMMITTED)
不可重复读(READ-COMMITTED)
可重复读(REPEATABLE-READ)
串行化(SERIALIZABLE)

mysql并发事务会出现的问题解决方法

并发事务问题
1.可以交给应用解决
更新丢失(Lost Update)

2.需要数据库提供事务间的隔离机制来解决

  1. 脏读(Dirty Reads)
  2. 不可重复读(Non-Repeatable Reads)
  3. 幻读(Phantom Reads)

3.实现隔离机制的方法主要有两种

  1. 加读写锁
  2. 一致性快照读, 即 MVCC, MVCC只在repeatable read和read committed两个隔离级别下工作。

mysql行级锁,表级锁,页级锁

按照锁的粒度把数据库锁分为行级锁(INNODB引擎)、 表级锁(MYISAM引擎)和页级锁(BDB引擎 )。

行级锁:行级锁是Mysql中锁定粒度最细的一种锁, 表示只针对当前操作的行进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小, 但加锁的开销也最大。 行级锁分为共享锁 和 排他锁。
特点: 开销大, 加锁慢; 会出现死锁; 锁定粒度最小, 发生锁冲突的概率最低, 并发度也最高。

表级锁:表级锁是MySQL中锁定粒度最大的一种锁, 表示对当前操作的整张表加锁, 它实现简单, 资源消耗较少, 被大部分MySQL引擎支持。 最常使用的MYISAM与INNODB都支持表级锁定。 表级锁定分为表共享读锁( 共享锁) 与表独占写锁( 排他锁) 。
特点: 开销小, 加锁快; 不会出现死锁; 锁定粒度大, 发出锁冲突的概率最高, 并发度最低。

页级锁:页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。 表级锁速度快, 但冲突多, 行级冲突少, 但速度慢。所以取了折衷的页级, 一次锁定相邻的一组记录。 BDB支持页级锁
特点: 开销和加锁时间界于表锁和行锁之间; 会出现死锁; 锁定粒度界于表锁和行锁之间, 并发度一般

锁的类型

  • MySQL的表锁有两种模式:

    1. 表共享读锁( Table Read Lock)
    2. 表独占写锁( Table Write Lock)
  • InnoDB两种类型的行锁:

    1. 共享锁( S ) : 允许一个事务去读一行, 阻止其他事务获得相同数据集的排他锁。
    2. 排他锁( X ) : 允许获取排他锁的事务更新数据, 阻止其他事务取得相同的数据集共享读锁和排他写锁。
  • InnoDB两种内部使用的意向锁( Intention Locks) , 这两种意向锁都是表锁。

    1. 意向共享锁( IS) : 事务打算给数据行共享锁, 事务在给一个数据行加共享锁前必须先取得该表的IS锁。
    2. 意向排他锁( IX) : 事务打算给数据行加排他锁, 事务在给一个数据行加排他锁前必须先取得该表的IX锁。

mysql索引

  • 根据存储分类
    B-树索引
    哈希索引

  • 根据用途分类
    普通索引,唯一性索引,主键索引,空间索引,全文索引

    索引类型特点
    普通索引最基本的索引, 没有任何限制
    唯一索引与"普通索引"类似, 不同的就是: 索引列的值必须唯一, 但允许有空值。
    主键索引它是一种特殊的唯一索引, 不允许有空值。
    全文索引仅可用于 MyISAM 表, 针对较大的数据, 生成全文索引很耗时好空间。
    组合索引为了更多的提高 mysql 效率可建立组合索引, 遵循”最左前缀“原则。 创建复 合索引时应该将最常用(频率) 作限制条件的列放在最左边, 依次递减
  • 索引的实现原理
    MyISAM 引擎使用 B+Tree 作为索引结构,叶节点的 data 域存放的是数据记录的地址。
    InnoDB 也使用 B+Tree 作为索引结构,InnoDB 的数据文件本身就是索引文件。

MySQL的索引是如何实现的?

MyISAM 引擎使用 B+Tree 作为索引结构,叶节点的 data 域存放的是数据记录的地址。
InnoDB 也使用 B+Tree 作为索引结构,InnoDB 的数据文件本身就是索引文件。

MvCC机制知道吗?

https://blog.csdn.net/filling_l/article/details/112854716

https://blog.csdn.net/whoamiyang/article/details/51901888

什么是聚簇索引?什么是非聚簇索引?

聚簇索引首先并不是一种索引类型,而是一种数据存储方式,是否为聚簇索引实际上指的就是b+树的具体实现方式,也就是每个节点的data域里面到底放的是具体的数据,还是指向数据的地址。数据则是聚簇。

InnoDB存储引擎的主键使用的是聚簇索引,而非主键使用的称作:“辅助索引”、“二次索引”,而MqISAM存储引擎无论主键,还是非主键使用的索引都是一样的:“非聚簇索引”。

聚簇索引,被索引的列必须是主键列,如果没有主键,会选择一个唯一的非空索引代替,如果也没有这样的索引,那么会隐式定义一个主键来作为聚簇索引。因此,也可以说聚簇索引就是按照表的主键构造的一个b+树,同时叶子节点里面存储了表的行数据。

如何选择合适的存储引擎

  1. 使用场景是否需要事务支持;
  2. 是否需要支持外键;
  3. 是否需要支持高并发, InnoDB的并发度远高于MyISAM;
  4. 是否需要支持外键;
  5. 高效缓冲数据, InnDB对数据和索引都做了缓冲, 而MyISAM只缓冲了索引;

Redis的数据类型有哪些?

Redis支持五bai种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

  • 集合(Set):
    set集合存储一组不重复的数据,有两种实现方法:(1)有序数组实现,(2)散列表实现。
    当存储的数据都是整数,并且存储的元素个数小于512个时,通过有序数组实现,否则通过散列表实现。

  • 有序集合(SortSet):
    Redis中的有序集合存储的是一组数据,并且每个数据都附带一个分数。有两种实现方式:(1)压缩列表,(2)跳表
    zset的编码有ziplist(压缩链表)和skiplist(跳表)两种。

redis的zset是如何做到快速插入时候,查找出插入节点是重复的?

skiplist 编码的 Zset 底层为一个被称为 zset 的结构体,这个结构体中包含一个字典和一个跳跃表。跳跃表按 score 从小到大保存所有集合元素,查找时间复杂度为平均 O(logN),最坏 O(N) 。字典则保存着从 member 到 score 的映射,这样就可以用 O(1)的复杂度来查找 member 对应的 score 值。虽然同时使用两种结构,但它们会通过指针来共享相同元素的 member 和 score,因此不会浪费额外的内存。

typedef struct zset {
    dict *dict;
    zskiplist *zsl;
} zset;

Redis这些数据类型的底层使用的哪种数据结构,有什么特点呢

https://www.cnblogs.com/ysocean/p/9080942.html

介绍Redis的持久化?

RDB持久化
AOF持久化

Redis的内存淘汰机制是什么吗?

https://codemouse.online/archives/2021-01-26-23-22-45
LRU是最近最少使用页面置换算法(Least Recently Used),也就是首先淘汰最长时间未被使用的页面!

LFU是最近最不常用页面置换算法(Least Frequently Used),也就是淘汰一定时期内被访问次数最少的页!

说说协程你有了解吗?与进程和线程的区别是什么?

进程拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度。

线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度(标准线程是的)。

协程和线程一样共享堆,不共享栈,协程由程序员在协程的代码里显示调度。

进程和线程的区别:进程是资源的单位,线程是调度(CPU调度)执行的最小单位。
进程与线程的关系
协程和线程的区别是:协程避免了无意义的调度,由此可以提高性能,但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力。
进程线程协程的关系

分页和分段的区别是什么?

  • 参考博客https://blog.csdn.net/weixin_40237626/article/details/82290475

  • 分页和分段有许多相似之处,比如两者都不要求作业连续存放.但在概念上两者完全不同,主要表现在以下几个方面:

    1. 页是信息的物理单位,分页是为了实现非连续分配,以便解决内存碎片问题,或者说分页是由于系统管理的需要.段是信息的逻辑单位,它含有一组意义相对完整的信息,分段的目的是为了更好地实现共享,满足用户的需要.
    2. 页的大小固定,由系统确定,将逻辑地址划分为页号和页内地址是由机器硬件实现的.而段的长度却不固定,决定于用户所编写的程序,通常由编译程序在对源程序进行编译时根据信息的性质来划分.
    3. 分页的作业地址空间是一维的.分段的地址空间是二维的.

分页存储管理

  • 用户程序的地址空间被划分成若干固定大小的区域,称为“页”,相应地,内存空间分成若干个物理块,页和块的大小相等。可将用户程序的任一页放在内存的任一块中,实现了离散分配。
  • 页表:分页系统中,允许将进程的每一页离散地存储在内存的任一物理块中,为了能在内存中找到每个页面对应的物理块,系统为每个进程建立一张页面映射表,简称页表。页表的作用是实现从页号到物理块号的地址映射。

分段存储管理

  • 将用户程序地址空间分成若干个大小不等的段,每段可以定义一组相对完整的逻辑信息。存储分配时,以段为单位,段与段在内存中可以不相邻接,也实现了离散分配。

段页式存储管理

  • 分页系统能有效地提高内存的利用率,而分段系统能反映程序的逻辑结构,便于段的共享与保护,将分页与分段两种存储方式结合起来,就形成了段页式存储管理方式。
  • 在段页式存储管理系统中,作业的地址空间首先被分成若干个逻辑分段,每段都有自己的段号,然后再将每段分成若干个大小相等的页。对于主存空间也分成大小相等的页,主存的分配以页为单位。

什么是上下文切换?

  • 在多任务处理系统中,CPU需要处理所有程序的操作,当用户来回切换它们时,需要记录这些程序执行到哪里。上下文切换就是这样一个过程,他允许CPU记录并恢复各种正在运行程序的状态,使它能够完成切换操作。
  • 在上下文切换过程中,CPU会停止处理当前运行的程序,并保存当前程序运行的具体位置以便之后继续运行。
  • 在三种情况下可能会发生上下文切换:中断处理,多任务处理,用户态切换。在中断处理中,其他程序”打断”了当前正在运行的程序。

什么是中断?

  • 中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。

如何创建线程?参数是什么?线程的同步与互斥有哪些方法?

int pthread_create(pthread_t*restrict_tidp,const pthread_attr_t restrict_attr,void(start_rtn)(void),void *restrict_arg);
第一个参数为指向线程标识符的指针。
第二个参数用来设置线程属性。
第三个参数是线程运行函数的起始地址。
第四个参数是运行函数的参数。

Posix线程中的线程属性pthread_attr_t主要包括scope属性、detach属性、堆栈地址、堆栈大小、优先级。在pthread_create中,把第二个参数设置为NULL的话,将采用默认的属性配置。

临界区(Critical Section)、bai互斥du量(Mutex)、信号量(Semaphore)、事件(Event)的区别
1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。在任意时刻只允许一个线程对共享资源进行访问,如果有多个线程试图访问公共资源,那么在有一个线程进入后,其他试图访问公共资源的线程将被挂起,并一直等到进入临界区的线程离开,临界区在被释放后,其他线程才可以抢占。只能在本进程内使用。
2、互斥量:采用互斥对象机制。 只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程访问。互斥不仅能实现同一应用程序的公共资源安全共享,还能实现不同应用程序的公共资源安全共享。使用原子变量可以达到类似效果。
3、信号量:它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目
4、事 件: 通过通知操作的方式来保持线程的同步,还可以方便实现对多个线程的优先级比较的操作

进程间通信有哪些方法?

1、无名管道通信

无名管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。

2、高级管道通信

高级管道(popen):将另一个程序当做一个新的进程在当前程序进程中启动,则它算是当前程序的子进程,这种方式我们成为高级管道方式。

3、有名管道通信

有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

4、消息队列通信

消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

5、信号量通信

信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

6、信号

信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

7、共享内存通信

共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。

8、套接字通信

套接字( socket ) : 套接口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。

fork的底层是如何实现的?

当进程调用fork后,当控制转移到内核中的fork代码后,内核会做4件事情:
1、分配新的内存块和内核数据结构给子进程。
2、将父进程部分数据结构内容拷贝至子进程。
3、添加子进程到系统进程列表当中。
4、fork返回,开始调度器调度。

共享存储底层如何实现的?

共享内存

啥是写时复制

  • 写时拷贝思想:父进程和子进程共享页帧而不是复制页帧。然而,只要页帧被共享,它们就不能被修改,即页帧被保护。无论父进程还是子进程何时试图写一个共享的页帧,就产生一个异常,这时内核就把这个页复制到一个新的页帧中并标记为可写。原来的页帧仍然是写保护的:当其他进程试图写入时,内核检查写进程是否是这个页帧的唯一属主,如果是,就把这个页帧标记为对这个进程是可写的。
  • 写时拷贝是一种可以推迟甚至避免拷贝数据的技术。内核此时并不复制整个进程的地址空间,而是让父子进程共享同一个地址空间。只用在需要写入的时候才会复制地址空间,从而使各个进行拥有各自的地址空间。也就是说,资源的复制是在需要写入的时候才会进行,在此之前,只有以只读方式共享。这种技术使地址空间上的页的拷贝被推迟到实际发生写入的时候。

什么是心跳检测?

判断对方(设备,进程或其它网元)是否正常动行,一般采用定时发送简单的通讯包,如果在指定时间段内未收到对方响应,则判断对方已经当掉。用于检测TCP的异常断开。

基本原因是服务器端不能有效的判断客户端是否在线也就是说,服务器无法区分客户端是长时间在空闲,还是已经掉线的情况。所谓的心跳包就是客户端定时发送简单的信息给服务器端告诉它我还在而已。

代码就是每隔几分钟发送一个固定信息给服务端,服务端收到后回复一个固定信息。如果服务端几分钟内没有收到客户端信息则视客户端断开。比如有些通信软件长时间不使用,要想知道它的状 态是在线还是离线就需要心跳包,定时发包收包。

什么是时间轮定时器吗?

  • 可以通过数组+链表实现,数组的每个下标作为时间轮的格子,每个格子可以链接一个长链表。
  • 每个格子代表一个时间,每过一段时间(看程序设定的精度),格子指针走一格,并获取出格子中的链表,执行链表节点的事件。

现在让你写UDP协议,你要如何保证可靠传输呢?

在 UDP 通讯中,当你的数据包发出去后,至于对方有没有正确收到数据,并不知道,那
么,如何保证你发出去的数据,对方一定能收到呢???我们可以借鉴 TCP 协议的做法(回
复+重发+编号 机制)
1)接收方收到数据后,回复一个确认包,如果你不回复,那么发送端是不会知道接收
方是否成功收到数据的
比如 A 要发数据“
”到 B,那 B 收到后,可以回复一个特定的确认包“”,
表示成功收到。
但是如果只做上面的回复处理,还是有问题,比如 B 收到数据后回复给 A 的数据"
"
的包, A 没收到,怎么办呢???
2)当 A 没有收到 B 的"
"包后,要做定时重发数据,直到成功接收到确认包为止,
再发下面的数据,当然,重发了一定数量后还是没能收到确认包,可以执行一下 ARP 的流程,
防止对方网卡更换或别的原因。
但是这样的话, B 会收到很多重复的数据,假如每次都是 B 回复确认包 A 收不到的话。
3)发送数据的包中加个标识符,比如 A 要发送的数据"{标识符|data}"到 B, B 收到后,
先回复“
"确认包,再根据原有的标识符进行比较,如果标识符相同,则数据丢失,如
果不相同,则原有的标识符 = 接收标识符,且处理数据。
当 A 发送数据包后,没有收到确认包,则每隔 x 秒,把数据重发一次,直到收到确认包
后,更新一下标识符,再进行后一包的数据发送。
经过上面 1), 2), 3)点的做法,则可以保证数据百分百到达对方,当然,标识符用
ID 号来代替更好

如何设计线程池?有哪些数据结构?

  • 数据结构需要队列。可以通过数组或者链表实现。
  • 设计线程池要需要做一个任务池,还需要一个监管线程去控制任务的发布。
  • 发布任务后,任务池有任务,线程池中的任务通过条件唤醒,接取任务执行。
  • 任务执行完继续等待任务的接取。
  • 获取任务的时候,一定要将任务队列置于临界区,防止多线程时候,任务被重复接取。

CAP介绍一下

CAP原理指的是,在分布式系统中这三个要素最多只能同时实现两点,不可能三者兼顾。因此在进行分布式架构设计时,必须做出取舍。而对于分布式数据系统,分区容忍性是基本要求,否则就失去了价值。因此设计分布式数据系统,就是在一致性和可用性之间取一个平衡。对于大多数Web应用,其实并不需要强一致性,因此牺牲一致性而换取高可用性,是目前多数分布式数据库产品的方向。

  • 一致性(Consistency):数据在多个副本之间是否能够保持一致的特性。(当一个系统在一致状态下更新后,应保持系统中所有数据仍处于一致的状态)
  • 可用性(Availability):系统提供的服务必须一直处于可用状态,对每一个操作的请求必须在有限时间内返回结果。
  • 分区容错性(Tolerance of network Partition):分布式系统在遇到网络分区故障时,仍然需要保证对外提供一致性和可用性的服务,除非整个网络都发生故障。

CAP定理为什么只能同时满足两个?

  1. 满足C与A的情况:要保证数据响应要快,而且数据必须保证是正确的,那么必然不能有太多的机器,不然同步时间慢。所以P不能满足。
  2. 满足C与P的情况:要保证数据响应要快,而且备份机子要多,此时如果要保证数据还要一致,那么一定会导致响应时间变慢。所以A不能满足。
  3. 满足A与P的情况。要保证数据要正确,而且备份机多的情况下,那么同步时间一定会长,同步完成后才能去响应,所以响应时间变慢。所以A不能满足。

分布式锁如何实现

分布式锁应该具备哪些条件:

1、在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行; 
2、高可用的获取锁与释放锁; 
3、高性能的获取锁与释放锁; 
4、具备可重入特性; 
5、具备锁失效机制,防止死锁; 
6、具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败。

实现方法:

  1. 使用数据库做一个唯一约束,多个请求提交到数据库,数据库会保证只能有一个成功。(排他锁)。注意一定要加过期时间,数据库做一个定时任务定时清理。
  2. 使用redis调用setnx(key,val)函数去获取锁,使用del(key)释放锁。
    只有在key不存在的时候才能设置成功。成功会收到+OK,失败会收到nil。使用setnx需要使用额外命令设置超时expire(key),防止进程死锁。 防止进程崩溃,无人释放锁,在这里需要使用redis事务lua。如果担心释放锁的时候,锁的拥有者不是自己,可以使用四元组:ip+port+starttime+pid的方式去对比,键值设置为四元组,释放时候比对四元组。set("lock",四元组,"NX","EX",30);
  3. zookeeper互斥锁的实现(方案1)
    获取锁:创建同名锁节点/lock,且是临时的节点(此时多个客户端必然只有一个可以创建成功)。其他客户端通过添加watch来监听节点的变化来抢占锁。
    释放锁:delete命令删除节点或者客户端断开连接或超时后节点自动被删除。
  4. zookeeper互斥锁的实现(方案2)
    • 如果zookeeper的连接抢占锁的客户端过多,此时zookeeper需要大量的性能去维护大量的监听,并发送通知,所以思考出了一个客户端一个lock节点,然后通过先后顺序编号(使用zookeeper的顺序临时节点)。编号最小的可以有资格获取锁,没拿到最小锁的监听比自己小一个号的节点。。
    • 获取锁:创建同名,且是临时的顺序节点(此时最先到的客户端是/lock/0),第一个客户端拿到锁资格,其他客户端根据该编号往下延续,并监听前比自己小一个号的节点。
    • 释放锁:delete命令删除节点或者客户端断开连接或超时后节点自动被删除。