新技术演进
软件架构变更 : Bypass OS kernel
SPDK:
- Kernel Space -> User Space
- 避免syscal带来性能的损耗,直接从用户态访问磁盘
- 中断 -> 轮询
- 磁盘性能提高之后,中断次数随之上升,不利于IO性能
- SPDK poller可以绑定特定的cpu核不断轮询,减少cs,提高性能
- 无锁数据结构
- 使用 Lock-free queue,降低并发操作时的开销
AI增强 : 存储格式转换
通过AI技术实时分析,决定行列存储方式
新硬件革命 : 存储介质变更,计算单元变更,网络硬件变更
- RDMA网络
- 传统的网络协议栈,需要基于多层网络协议处理数据包,存在用户态和内核态的切换,足够通用但是性能不是最佳。
- RDMA时kernel bypass的流派,不经过传统的网络协议栈,可以把用户态虚拟内存映射给网卡,减少拷贝开销,减少cpu开销
- persistent memory
在NVMe SSD和main memory之间有一种全新的存储产品:persistent memory
- IO时延介于SSD和memory之间,约百纳秒量级
- 可以用作易失性内存,也可以用作持久化介质
- 可编程交换机
- P4,Switch,配有编译器,计算单元,DRAM,可以在交换机层对网络包做计算逻辑,在数据库场景下,可以实现缓存一致性协议等。
- CPU/GPU/DPU
- CPU:从multi-core走向many-core
- GPU:强大的算力和越来越大的显存空间
- DPU:异构计算,减轻CPU的workload
数据库实践 -- AI
这一技术利用人工智能来实时分析数据,并决定如何以最优的方式进行行列存储。行列存储方式在不同的数据访问模式下具有不同的优势,AI可以根据实际的数据访问模式来动态调整数据的存储格式,以提高查询性能和减少存储开销。
作业
实现一个 (分布式) key-value 存储系统
要求:
- 基于本地文件系统实现,支持常用的put(k,v)、get(k, v)scan by_prefix(prefix)接口
- 支持存储server独立进程部署,支持跨进程或者网络访问
- IO操作做到低时延
- 4.可选:支持扩展成分布式架构,多台存server组成一个分布式keyvalue存储系统,并保证全局的数据一致性
以下只完成了第一个部分。
1. 存储数据
首先,我们将使用 Go 语言来创建一个本地键-值存储系统。我们将使用 map 来保存键值对,并将数据保存到本地文件中。
goCopy code
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"strings"
"sync"
)
var (
dataMutex sync.RWMutex
data = make(map[string]string)
)
func main() {
// 启动 HTTP 服务器来提供接口
// ...
}
// Put 存储键值对
func Put(key, value string) error {
dataMutex.Lock()
defer dataMutex.Unlock()
data[key] = value
// 保存数据到本地文件
if err := saveToFile(key, value); err != nil {
return err
}
return nil
}
// scanByPrefix 根据前缀扫描键值对
func scanByPrefix(prefix string) (map[string]string, error) {
dataMutex.RLock()
defer dataMutex.RUnlock()
result := make(map[string]string)
for key, value := range data {
if strings.HasPrefix(key, prefix) {
result[key] = value
}
}
return result, nil
}
// 保存数据到本地文件
func saveToFile(key, value string) error {
// 这里可以将数据保存到本地文件,使用 JSON 或其他格式
// 为简化示例,这里只输出到控制台
fmt.Printf("Saved: key=%s, value=%s\n", key, value)
return nil
}
// 从本地文件加载数据
func loadFromFile() error {
// 从本地文件加载数据,使用 JSON 或其他格式
// 为简化示例,这里没有实现文件加载
return nil
}
上述代码创建了一个简单的键-值存储系统,支持 Put 存储键值对和 scanByPrefix 根据前缀扫描键值对。数据将保存在内存中,并且每次 Put 操作都会将数据写入本地文件。
2. 启动 HTTP 服务器
为了让其他进程或网络访问这个键-值存储系统,您可以创建一个 HTTP 服务器来提供接口。以下是一个简单的示例,使用 Go 的标准库 net/http 创建一个基本的 HTTP 服务器:
goCopy code
package main
import (
"encoding/json"
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/put", putHandler)
http.HandleFunc("/scan", scanHandler)
port := 8080
fmt.Printf("Server is listening on port %d...\n", port)
http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
}
func putHandler(w http.ResponseWriter, r *http.Request) {
key := r.URL.Query().Get("key")
value := r.URL.Query().Get("value")
if key == "" || value == "" {
http.Error(w, "Key and value must be provided", http.StatusBadRequest)
return
}
if err := Put(key, value); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
func scanHandler(w http.ResponseWriter, r *http.Request) {
prefix := r.URL.Query().Get("prefix")
if prefix == "" {
http.Error(w, "Prefix must be provided", http.StatusBadRequest)
return
}
result, err := scanByPrefix(prefix)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// 返回 JSON 格式的结果
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(result)
}
在上述代码中,我们创建了两个 HTTP 处理程序:putHandler 处理 Put 请求,scanHandler 处理 scanByPrefix 请求。您可以通过发送 HTTP 请求来与存储系统进行交互。