mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2025-10-21 10:17:24 +08:00
add filer support
This commit is contained in:
11
weed/filer/mysql_store/filer_mapping.sql
Normal file
11
weed/filer/mysql_store/filer_mapping.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
CREATE TABLE IF NOT EXISTS `filer_mapping` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
`uriPath` char(256) NOT NULL DEFAULT "" COMMENT 'http uriPath',
|
||||
`fid` char(36) NOT NULL DEFAULT "" COMMENT 'seaweedfs fid',
|
||||
`createTime` int(10) NOT NULL DEFAULT 0 COMMENT 'createdTime in unix timestamp',
|
||||
`updateTime` int(10) NOT NULL DEFAULT 0 COMMENT 'updatedTime in unix timestamp',
|
||||
`remark` varchar(20) NOT NULL DEFAULT "" COMMENT 'reserverd field',
|
||||
`status` tinyint(2) DEFAULT '1' COMMENT 'resource status',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `index_uriPath` (`uriPath`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
224
weed/filer/mysql_store/mysql_store.go
Normal file
224
weed/filer/mysql_store/mysql_store.go
Normal file
@@ -0,0 +1,224 @@
|
||||
package mysql_store
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
const (
|
||||
sqlUrl = "%s:%s@tcp(%s:%d)/%s?charset=utf8"
|
||||
maxIdleConnections = 100
|
||||
maxOpenConnections = 50
|
||||
maxTableNums = 1024
|
||||
tableName = "filer_mapping"
|
||||
)
|
||||
|
||||
var (
|
||||
_init_db sync.Once
|
||||
_db_connections []*sql.DB
|
||||
)
|
||||
|
||||
type MySqlConf struct {
|
||||
User string
|
||||
Password string
|
||||
HostName string
|
||||
Port int
|
||||
DataBase string
|
||||
}
|
||||
|
||||
type MySqlStore struct {
|
||||
dbs []*sql.DB
|
||||
}
|
||||
|
||||
func getDbConnection(confs []MySqlConf) []*sql.DB {
|
||||
_init_db.Do(func() {
|
||||
for _, conf := range confs {
|
||||
|
||||
sqlUrl := fmt.Sprintf(sqlUrl, conf.User, conf.Password, conf.HostName, conf.Port, conf.DataBase)
|
||||
var dbErr error
|
||||
_db_connection, dbErr := sql.Open("mysql", sqlUrl)
|
||||
if dbErr != nil {
|
||||
_db_connection.Close()
|
||||
_db_connection = nil
|
||||
panic(dbErr)
|
||||
}
|
||||
_db_connection.SetMaxIdleConns(maxIdleConnections)
|
||||
_db_connection.SetMaxOpenConns(maxOpenConnections)
|
||||
_db_connections = append(_db_connections, _db_connection)
|
||||
}
|
||||
})
|
||||
return _db_connections
|
||||
}
|
||||
|
||||
func NewMysqlStore(confs []MySqlConf) *MySqlStore {
|
||||
ms := &MySqlStore{
|
||||
dbs: getDbConnection(confs),
|
||||
}
|
||||
|
||||
for _, db := range ms.dbs {
|
||||
for i := 0; i < maxTableNums; i++ {
|
||||
if err := ms.createTables(db, tableName, i); err != nil {
|
||||
fmt.Printf("create table failed %s", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ms
|
||||
}
|
||||
|
||||
func (s *MySqlStore) hash(fullFileName string) (instance_offset, table_postfix int) {
|
||||
hash_value := crc32.ChecksumIEEE([]byte(fullFileName))
|
||||
instance_offset = int(hash_value) % len(s.dbs)
|
||||
table_postfix = int(hash_value) % maxTableNums
|
||||
return
|
||||
}
|
||||
|
||||
func (s *MySqlStore) parseFilerMappingInfo(path string) (instanceId int, tableFullName string, err error) {
|
||||
instance_offset, table_postfix := s.hash(path)
|
||||
instanceId = instance_offset
|
||||
tableFullName = fmt.Sprintf("%s_%04d", tableName, table_postfix)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *MySqlStore) Get(fullFilePath string) (fid string, err error) {
|
||||
instance_offset, tableFullName, err := s.parseFilerMappingInfo(fullFilePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
fid, err = s.query(fullFilePath, s.dbs[instance_offset], tableFullName)
|
||||
if err == sql.ErrNoRows {
|
||||
//Could not found
|
||||
err = nil
|
||||
}
|
||||
return fid, err
|
||||
}
|
||||
|
||||
func (s *MySqlStore) Put(fullFilePath string, fid string) (err error) {
|
||||
var tableFullName string
|
||||
|
||||
instance_offset, tableFullName, err := s.parseFilerMappingInfo(fullFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if old_fid, localErr := s.query(fullFilePath, s.dbs[instance_offset], tableFullName); localErr != nil && localErr != sql.ErrNoRows {
|
||||
err = localErr
|
||||
return
|
||||
} else {
|
||||
if len(old_fid) == 0 {
|
||||
err = s.insert(fullFilePath, fid, s.dbs[instance_offset], tableFullName)
|
||||
} else {
|
||||
err = s.update(fullFilePath, fid, s.dbs[instance_offset], tableFullName)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *MySqlStore) Delete(fullFilePath string) (err error) {
|
||||
var fid string
|
||||
instance_offset, tableFullName, err := s.parseFilerMappingInfo(fullFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fid, err = s.query(fullFilePath, s.dbs[instance_offset], tableFullName); err != nil {
|
||||
return err
|
||||
} else if fid == "" {
|
||||
return nil
|
||||
}
|
||||
if err := s.delete(fullFilePath, s.dbs[instance_offset], tableFullName); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MySqlStore) Close() {
|
||||
for _, db := range s.dbs {
|
||||
db.Close()
|
||||
}
|
||||
}
|
||||
|
||||
var createTable = `
|
||||
CREATE TABLE IF NOT EXISTS %s_%04d (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
uriPath char(256) NOT NULL DEFAULT "" COMMENT 'http uriPath',
|
||||
fid char(36) NOT NULL DEFAULT "" COMMENT 'seaweedfs fid',
|
||||
createTime int(10) NOT NULL DEFAULT 0 COMMENT 'createdTime in unix timestamp',
|
||||
updateTime int(10) NOT NULL DEFAULT 0 COMMENT 'updatedTime in unix timestamp',
|
||||
remark varchar(20) NOT NULL DEFAULT "" COMMENT 'reserverd field',
|
||||
status tinyint(2) DEFAULT '1' COMMENT 'resource status',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY index_uriPath (uriPath)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`
|
||||
|
||||
func (s *MySqlStore) createTables(db *sql.DB, tableName string, postfix int) error {
|
||||
stmt, err := db.Prepare(fmt.Sprintf(createTable, tableName, postfix))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
_, err = stmt.Exec()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *MySqlStore) query(uriPath string, db *sql.DB, tableName string) (string, error) {
|
||||
sqlStatement := "SELECT fid FROM %s WHERE uriPath=?"
|
||||
row := db.QueryRow(fmt.Sprintf(sqlStatement, tableName), uriPath)
|
||||
var fid string
|
||||
err := row.Scan(&fid)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fid, nil
|
||||
}
|
||||
|
||||
func (s *MySqlStore) update(uriPath string, fid string, db *sql.DB, tableName string) error {
|
||||
sqlStatement := "UPDATE %s SET fid=?, updateTime=? WHERE uriPath=?"
|
||||
res, err := db.Exec(fmt.Sprintf(sqlStatement, tableName), fid, time.Now().Unix(), uriPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = res.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *MySqlStore) insert(uriPath string, fid string, db *sql.DB, tableName string) error {
|
||||
sqlStatement := "INSERT INTO %s (uriPath,fid,createTime) VALUES(?,?,?)"
|
||||
res, err := db.Exec(fmt.Sprintf(sqlStatement, tableName), uriPath, fid, time.Now().Unix())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = res.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *MySqlStore) delete(uriPath string, db *sql.DB, tableName string) error {
|
||||
sqlStatement := "DELETE FROM %s WHERE uriPath=?"
|
||||
res, err := db.Exec(fmt.Sprintf(sqlStatement, tableName), uriPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = res.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
60
weed/filer/mysql_store/mysql_store_test.go
Normal file
60
weed/filer/mysql_store/mysql_store_test.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package mysql_store
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"hash/crc32"
|
||||
"testing"
|
||||
)
|
||||
|
||||
/*
|
||||
To improve performance when storing billion of files, you could shar
|
||||
At each mysql instance, we will try to create 1024 tables if not exist, table name will be something like:
|
||||
filer_mapping_0000
|
||||
filer_mapping_0001
|
||||
.....
|
||||
filer_mapping_1023
|
||||
sample conf should be
|
||||
|
||||
>$cat filer_conf.json
|
||||
{
|
||||
"mysql": [
|
||||
{
|
||||
"User": "root",
|
||||
"Password": "root",
|
||||
"HostName": "127.0.0.1",
|
||||
"Port": 3306,
|
||||
"DataBase": "seaweedfs"
|
||||
},
|
||||
{
|
||||
"User": "root",
|
||||
"Password": "root",
|
||||
"HostName": "127.0.0.2",
|
||||
"Port": 3306,
|
||||
"DataBase": "seaweedfs"
|
||||
}
|
||||
]
|
||||
}
|
||||
*/
|
||||
|
||||
func TestGenerateMysqlConf(t *testing.T) {
|
||||
var conf MySqlConf
|
||||
conf = append(conf, MySqlInstConf{
|
||||
User: "root",
|
||||
Password: "root",
|
||||
HostName: "localhost",
|
||||
Port: 3306,
|
||||
DataBase: "seaweedfs",
|
||||
})
|
||||
body, err := json.Marshal(conf)
|
||||
if err != nil {
|
||||
t.Errorf("json encoding err %s", err.Error())
|
||||
}
|
||||
t.Logf("json output is %s", string(body))
|
||||
}
|
||||
|
||||
func TestCRC32FullPathName(t *testing.T) {
|
||||
fullPathName := "/prod-bucket/law632191483895612493300-signed.pdf"
|
||||
hash_value := crc32.ChecksumIEEE([]byte(fullPathName))
|
||||
table_postfix := int(hash_value) % 1024
|
||||
t.Logf("table postfix %d", table_postfix)
|
||||
}
|
Reference in New Issue
Block a user