并发安全就是程序在并发的情况下执行的结果都是正确的;
Go中数据类型分为两大类:
字节型、布尔型、整型、浮点型取决于操作系统指令值,在64位的指令集架构中可以由一条机器指令完 成,不存在被细分为更小的操作单位,所以这些类型的并发赋值是安全的,但是这个也跟操作系统的位 数有关,比如int64在32位操作系统中,它的高32位和低32位是分开赋值的,此时是非并发安全的。
复数类型、字符串、结构体、数组,切片,字典,通道,接口, 这些底层都是struct,不同成员的赋值 都不是一起的,所以都不是并发安全的。
单例模式的作用是确保无论对象被实例化多少次,全局都只有一个实例存在。根据这一特性,我们可以将其应用到全局唯一性配置、数据库连接对象、文件访问对象等。
饿汉式实现单例模式非常简单,直接看代码:
package singletontype singleton struct{}var instance = &singleton{}func GetSingleton() *singleton { return instance}
singleton 包在被导入时会自动初始化 instance 实例,使用时通过调用 singleton.GetSingleton() 函数即可获得 singleton 这个结构体的单例对象。
这种方式的单例对象是在包加载时立即被创建,所以这个方式叫作饿汉式。与之对应的另一种实现方式叫作懒汉式,懒汉式模式下实例会在第一次被使用时被创建。
需要注意的是,尽管饿汉式实现单例模式的方式简单,但大多数情况下并不推荐。因为如果单例实例化时初始化内容过多,会造成程序加载用时较长。
接下来我们再来看下如何通过懒汉式实现单例模式:
package singletontype singleton struct{}var instance *singletonfunc GetSingleton() *singleton { if instance == nil { instance = &singleton{} } return instance}
相较于饿汉式的实现,懒汉式将实例化 singleton 结构体部分的代码移到了 GetSingleton() 函数内部。这样能够将对象实例化的步骤延迟到 GetSingleton() 第一次被调用时。
不过通过 instance == nil 的判断来实现单例并不十分可靠,如果有多个 goroutine 同时调用 GetSingleton() 就无法保证并发安全。
Go 的内建 map 是不支持并发写操作的,原因是 map 写操作不是并发安全的,当你尝试多个 Goroutine 操作同一个 map,会产生报错:fatal error: concurrent map writes。
因此官方另外引入了 sync.Map 来满足并发编程中的应用。
sync.Map 的实现原理可概括为:
type Map struct { // 加锁作用,保护 dirty 字段 mu Mutex // 只读的数据,实际数据类型为 readOnly read atomic.Value // 最新写入的数据 dirty map[interface{}]*entry // 计数器,每次需要读 dirty 则 +1 misses int}
其中 readOnly 的数据结构为:
type readOnly struct { // 内建 map m map[interface{}]*entry // 表示 dirty 里存在 read 里没有的 key,通过该字段决定是否加锁读 dirty amended bool}
entry 数据结构则用于存储值的指针:
type entry struct { p unsafe.Pointer // 等同于 *interface{}}
属性 p 有三种状态:
Map 常用的有以下方法:
redis的基本数据结构有: 1、String(字符串);2、Hash(哈希);3、List(列表);4、Set(集合);5、zset(有序集合)。
String 类型是 Redis 中最基本、最常用的数据类型,甚至被很多用户当成 Redis 少数的数据类型去使用。String 类型在 Redis 中是二进制安全(binary safe)的,这意味着 String 值关心二进制的字符串,不关心具体格式,你可以用它存储 json 格式或 JPEG 图片格式的字符串。
应用:
Hash的数据结构我们可以简单理解为java中的 Map,这种结构就特别适合存储对象,上面的String的类型确实也可以存储对象,但每次修改对象中的某一个属性,都要拿出整个json字符串在修改这个属性,之后在重新插入,而hash的接口特点让我们可以只修改该对象的某一个属性。
hash数据类型在存储上述类型的数据时具有比 String 类型更灵活、更快的优势,具体的说,使用 String 类型存储,必然需要转换和解析 json 格式的字符串,即便不需要转换,在内存开销方面,还是 hash 占优势。
应用:
List类型是按照插入顺序排序的字符串链表,一个列表非常多可以存储2^32-1个元素。我们可以简单理解为就相当于java中的LinkesdList。和数据结构中的普通链表一样,我们可以在其头部(left)和尾部(right)添加新的元素。在插入时,如果该键并不存在,Redis将为该键创建一个新的链表。与此相反,如果链表中所有的元素均被移除,那么该键也将会被从数据库中删除。
应用:
Redis 中的 set和Java中的HashSet 有些类似,它内部的键值对是无序的、少数的。它的内部实现相当于一个特殊的字典,字典中所有的value都是一个值 NULL。当集合中最后一个元素被移除之后,数据结构被自动删除,内存被回收。
应用:
Sorted-Sets中的每一个成员都会有一个分数(score)与之关联,Redis正是通过分数来为集合中的成员进行从小到大的排序。成员是少数的,但是分数(score)却是可以重复的。
应用: 作为有序的,不可重复的列表,可以做一些排行榜相关的场景:
当大量缓存数据在同一时间过期(失效)或者 Redis 故障宕机时,如果此时有大量的用户请求,都无法在 Redis 中处理,于是全部请求都直接访问数据库,从而导致数据库的压力骤增,严重的会造成数据库宕机,从而形成一系列连锁反应,造成整个系统崩溃,这就是缓存雪崩
我们的业务通常会有几个数据会被频繁地访问,比如秒杀活动,这类被频地访问的数据被称为热点数据。
如果缓存中的某个热点数据过期了,此时大量的请求访问了该热点数据,就无法从缓存中读取,直接访问数据库,数据库很容易就被高并发的请求冲垮,这就是缓存击穿。
当发生缓存雪崩或击穿时,数据库中还是保存了应用要访问的数据,一旦缓存恢复相对应的数据,就可以减轻数据库的压力,而缓存穿透就不一样了。
当用户访问的数据,既不在缓存中,也不在数据库中,导致请求在访问缓存时,发现缓存缺失,再去访问数据库时,发现数据库中也没有要访问的数据,没办法构建缓存数据,来服务后续的请求。那么当有大量这样的请求到来时,数据库的压力骤增,这就是缓存穿透的问题。
约束主要有:主键约束、外键约束、非空约束、检査约束(bentwen and ,大于、小于、等于、不等于)、唯一约束。
B+树是多叉树结构,每个结点都是一个16k的数据页,能存放较多索引信息,所以扇出很高。三层左右就可以存储2kw左右的数据(知道结论就行,想知道原因可以看之前的文章)。也就是说查询一次数据,如果这些数据页都在磁盘里,那么最多需要查询三次磁盘IO。
跳表是链表结构,一条数据一个结点,如果最底层要存放2kw数据,且每次查询都要能达到二分查找的效果,2kw大概在2的24次方左右,所以,跳表大概高度在24层左右。最坏情况下,这24层数据会分散在不同的数据页里,也即是查一次数据会经历24次磁盘IO。
因此存放同样量级的数据,B+树的高度比跳表的要少,如果放在mysql数据库上来说,就是磁盘IO次数更少,因此B+树查询更快。
而针对写操作,B+树需要拆分合并索引数据页,跳表则独立插入,并根据随机函数确定层数,没有旋转和维持平衡的开销,因此跳表的写入性能会比B+树要好。
HTTP2.0(Hypenext TransferProtocol version2)是超文本传输协议的第二版,HTTP2.0相比于HTTP1x,大幅度的提升了web性能,同时向下兼容HTTP1.X协议版 本。
主要核心优势有
1、采用二进制格式传输数据,而非htp1.1文本格式,二进制格式在协议的解析和优化扩展上带来了跟多的优势和可能
2、对消息头采用Hpack进行压缩传输,能够节省消息头占用的网络流量,htp1.1每次请求,都会携带大量冗余的头信息,浪费了很多宽带资源,
3、异步连接多路复用
4、Server Push,服务器端能够更快的把资源推送到客户端。
5、保持与HTTP 1.1语义的向后兼容性也是该版本的一个关键
本文转载自微信公众号「王中阳Go」,作者「王中阳Go」,可以通过以下二维码关注。
转载本文请联系「王中阳Go」公众号。
本文链接:http://www.28at.com/showinfo-26-98859-0.html阿里校招面试,我来瞅瞅怎么回事
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com