Đang lưu dữ liệu

Tài liệu này trình bày 4 phương thức ghi dữ liệu vào Cơ sở dữ liệu theo thời gian thực của Firebase: thiết lập, cập nhật, đẩy và hỗ trợ giao dịch.

Cách tiết kiệm dữ liệu

thiết lập Ghi hoặc thay thế dữ liệu thành một đường dẫn đã xác định, chẳng hạn như messages/users/<username>
cập nhật Cập nhật một số khoá cho đường dẫn đã xác định mà không thay thế tất cả dữ liệu
đẩy Thêm vào danh sách dữ liệu trong cơ sở dữ liệu. Mỗi khi bạn đẩy một nút mới vào danh sách, cơ sở dữ liệu của bạn sẽ tạo một khoá duy nhất, chẳng hạn như messages/users/<unique-user-id>/<username>
giao dịch Sử dụng các giao dịch khi làm việc với dữ liệu phức tạp có thể bị hỏng do các bản cập nhật đồng thời

Đang lưu dữ liệu

Thao tác ghi cơ sở dữ liệu cơ bản là một tập hợp nhằm lưu dữ liệu mới vào tham chiếu cơ sở dữ liệu được chỉ định, thay thế mọi dữ liệu hiện có trong đường dẫn đó. Để hiểu tập hợp, chúng ta sẽ tạo một ứng dụng viết blog. Dữ liệu của ứng dụng được lưu trữ tại tham chiếu cơ sở dữ liệu sau:

Java
final FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("server/saving-data/fireblog");
Node.js
// Import Admin SDK
const { getDatabase } = require('firebase-admin/database');

// Get a database reference to our blog
const db = getDatabase();
const ref = db.ref('server/saving-data/fireblog');
Python
# Import database module.
from firebase_admin import db

# Get a database reference to our blog.
ref = db.reference('server/saving-data/fireblog')
Tiến hành
// Create a database client from App.
client, err := app.Database(ctx)
if err != nil {
	log.Fatalln("Error initializing database client:", err)
}

// Get a database reference to our blog.
ref := client.NewRef("server/saving-data/fireblog")

Hãy bắt đầu bằng cách lưu một số dữ liệu người dùng. Chúng tôi sẽ lưu trữ mỗi người dùng theo một tên người dùng duy nhất, đồng thời chúng tôi cũng sẽ lưu trữ tên đầy đủ và ngày sinh của họ. Do mỗi người dùng sẽ có một tên người dùng duy nhất, nên bạn nên sử dụng phương thức tập hợp ở đây thay vì phương thức đẩy vì bạn đã có khoá và không cần tạo khoá.

Trước tiên, hãy tạo một tham chiếu cơ sở dữ liệu cho dữ liệu người dùng của bạn. Sau đó, sử dụng set() / setValue() để lưu đối tượng người dùng vào cơ sở dữ liệu cùng với tên người dùng, họ tên và ngày sinh của người dùng. Bạn có thể truyền một chuỗi, số, boolean, null, mảng hoặc bất kỳ đối tượng JSON nào. Nếu bạn chuyển null, dữ liệu ở vị trí được chỉ định sẽ bị xoá. Trong trường hợp này, bạn sẽ truyền cho đối tượng:

Java
public static class User {

  public String date_of_birth;
  public String full_name;
  public String nickname;

  public User(String dateOfBirth, String fullName) {
    // ...
  }

  public User(String dateOfBirth, String fullName, String nickname) {
    // ...
  }

}

DatabaseReference usersRef = ref.child("users");

Map<String, User> users = new HashMap<>();
users.put("alanisawesome", new User("June 23, 1912", "Alan Turing"));
users.put("gracehop", new User("December 9, 1906", "Grace Hopper"));

usersRef.setValueAsync(users);
Node.js
const usersRef = ref.child('users');
usersRef.set({
  alanisawesome: {
    date_of_birth: 'June 23, 1912',
    full_name: 'Alan Turing'
  },
  gracehop: {
    date_of_birth: 'December 9, 1906',
    full_name: 'Grace Hopper'
  }
});
Python
users_ref = ref.child('users')
users_ref.set({
    'alanisawesome': {
        'date_of_birth': 'June 23, 1912',
        'full_name': 'Alan Turing'
    },
    'gracehop': {
        'date_of_birth': 'December 9, 1906',
        'full_name': 'Grace Hopper'
    }
})
Tiến hành

// User is a json-serializable type.
type User struct {
	DateOfBirth string `json:"date_of_birth,omitempty"`
	FullName    string `json:"full_name,omitempty"`
	Nickname    string `json:"nickname,omitempty"`
}

usersRef := ref.Child("users")
err := usersRef.Set(ctx, map[string]*User{
	"alanisawesome": {
		DateOfBirth: "June 23, 1912",
		FullName:    "Alan Turing",
	},
	"gracehop": {
		DateOfBirth: "December 9, 1906",
		FullName:    "Grace Hopper",
	},
})
if err != nil {
	log.Fatalln("Error setting value:", err)
}

Khi một đối tượng JSON được lưu vào cơ sở dữ liệu, các thuộc tính của đối tượng sẽ tự động được liên kết với thành phần con của cơ sở dữ liệu các vị trí theo cách lồng ghép. Bây giờ, nếu bạn chuyển đến URL https://docs-examples.firebaseio.com/server/saving-data/fireblog/users/alanisawesome/full_name, chúng ta sẽ thấy giá trị "Alan Turing". Bạn cũng có thể lưu dữ liệu trực tiếp vào vị trí của trẻ:

Java
usersRef.child("alanisawesome").setValueAsync(new User("June 23, 1912", "Alan Turing"));
usersRef.child("gracehop").setValueAsync(new User("December 9, 1906", "Grace Hopper"));
Node.js
const usersRef = ref.child('users');
usersRef.child('alanisawesome').set({
  date_of_birth: 'June 23, 1912',
  full_name: 'Alan Turing'
});
usersRef.child('gracehop').set({
  date_of_birth: 'December 9, 1906',
  full_name: 'Grace Hopper'
});
Python
users_ref.child('alanisawesome').set({
    'date_of_birth': 'June 23, 1912',
    'full_name': 'Alan Turing'
})
users_ref.child('gracehop').set({
    'date_of_birth': 'December 9, 1906',
    'full_name': 'Grace Hopper'
})
Tiến hành
if err := usersRef.Child("alanisawesome").Set(ctx, &User{
	DateOfBirth: "June 23, 1912",
	FullName:    "Alan Turing",
}); err != nil {
	log.Fatalln("Error setting value:", err)
}

if err := usersRef.Child("gracehop").Set(ctx, &User{
	DateOfBirth: "December 9, 1906",
	FullName:    "Grace Hopper",
}); err != nil {
	log.Fatalln("Error setting value:", err)
}

Hai ví dụ trên – việc ghi cả hai giá trị cùng lúc dưới dạng một đối tượng và ghi các giá trị này riêng biệt vào các vị trí con – sẽ dẫn đến việc cùng một dữ liệu được lưu vào cơ sở dữ liệu của bạn:

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing"
    },
    "gracehop": {
      "date_of_birth": "December 9, 1906",
      "full_name": "Grace Hopper"
    }
  }
}

Ví dụ đầu tiên sẽ chỉ kích hoạt một sự kiện trên các ứng dụng đang xem dữ liệu, trong khi ví dụ thứ hai sẽ kích hoạt hai giá trị. Điều quan trọng cần lưu ý là nếu dữ liệu đã tồn tại tại usersRef, phương pháp đầu tiên sẽ ghi đè lên nút đó, nhưng phương thức thứ hai sẽ chỉ sửa đổi giá trị của mỗi nút con riêng biệt trong khi vẫn để lại phần tử con khác của usersRef không thay đổi.

Đang cập nhật dữ liệu đã lưu

Nếu bạn muốn ghi vào nhiều phần tử con của một vị trí cơ sở dữ liệu cùng một lúc mà không ghi đè các vị trí khác nút con, bạn có thể sử dụng phương pháp cập nhật như thể hiện dưới đây:

Java
DatabaseReference hopperRef = usersRef.child("gracehop");
Map<String, Object> hopperUpdates = new HashMap<>();
hopperUpdates.put("nickname", "Amazing Grace");

hopperRef.updateChildrenAsync(hopperUpdates);
Node.js
const usersRef = ref.child('users');
const hopperRef = usersRef.child('gracehop');
hopperRef.update({
  'nickname': 'Amazing Grace'
});
Python
hopper_ref = users_ref.child('gracehop')
hopper_ref.update({
    'nickname': 'Amazing Grace'
})
Tiến hành
hopperRef := usersRef.Child("gracehop")
if err := hopperRef.Update(ctx, map[string]interface{}{
	"nickname": "Amazing Grace",
}); err != nil {
	log.Fatalln("Error updating child:", err)
}

Thao tác này sẽ cập nhật dữ liệu của Grace để bao gồm cả biệt hiệu của cô. Nếu bạn đã sử dụng đặt ở đây thay vì cập nhật, thì cả full_namedate_of_birth sẽ bị xoá khỏi hopperRef của bạn.

Cơ sở dữ liệu theo thời gian thực của Firebase cũng hỗ trợ cập nhật theo nhiều đường dẫn. Điều này có nghĩa là bản cập nhật hiện có thể cập nhật tại nhiều vị trí trong cơ sở dữ liệu cùng một lúc, một tính năng mạnh mẽ cho phép giúp bạn không chuẩn hoá dữ liệu của bạn. Khi cập nhật nhiều đường dẫn, bạn có thể thêm biệt hiệu cho cả Grace và Alan cùng lúc thời gian:

Java
Map<String, Object> userUpdates = new HashMap<>();
userUpdates.put("alanisawesome/nickname", "Alan The Machine");
userUpdates.put("gracehop/nickname", "Amazing Grace");

usersRef.updateChildrenAsync(userUpdates);
Node.js
const usersRef = ref.child('users');
usersRef.update({
  'alanisawesome/nickname': 'Alan The Machine',
  'gracehop/nickname': 'Amazing Grace'
});
Python
users_ref.update({
    'alanisawesome/nickname': 'Alan The Machine',
    'gracehop/nickname': 'Amazing Grace'
})
Tiến hành
if err := usersRef.Update(ctx, map[string]interface{}{
	"alanisawesome/nickname": "Alan The Machine",
	"gracehop/nickname":      "Amazing Grace",
}); err != nil {
	log.Fatalln("Error updating children:", err)
}

Sau lần cập nhật này, cả Alan và Grace đều đã thêm biệt hiệu:

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing",
      "nickname": "Alan The Machine"
    },
    "gracehop": {
      "date_of_birth": "December 9, 1906",
      "full_name": "Grace Hopper",
      "nickname": "Amazing Grace"
    }
  }
}

Lưu ý rằng việc cố gắng cập nhật đối tượng bằng cách ghi đối tượng có đường dẫn sẽ dẫn đến hành vi khác. Hãy xem điều gì xảy ra nếu bạn cố gắng cập nhật Grace và Alan theo cách sau:

Java
Map<String, Object> userNicknameUpdates = new HashMap<>();
userNicknameUpdates.put("alanisawesome", new User(null, null, "Alan The Machine"));
userNicknameUpdates.put("gracehop", new User(null, null, "Amazing Grace"));

usersRef.updateChildrenAsync(userNicknameUpdates);
Node.js
const usersRef = ref.child('users');
usersRef.update({
  'alanisawesome': {
    'nickname': 'Alan The Machine'
  },
  'gracehop': {
    'nickname': 'Amazing Grace'
  }
});
Python
users_ref.update({
    'alanisawesome': {
        'nickname': 'Alan The Machine'
    },
    'gracehop': {
        'nickname': 'Amazing Grace'
    }
})
Tiến hành
if err := usersRef.Update(ctx, map[string]interface{}{
	"alanisawesome": &User{Nickname: "Alan The Machine"},
	"gracehop":      &User{Nickname: "Amazing Grace"},
}); err != nil {
	log.Fatalln("Error updating children:", err)
}

Điều này dẫn đến nhiều hành vi khác nhau, cụ thể là ghi đè toàn bộ nút /users:

{
  "users": {
    "alanisawesome": {
      "nickname": "Alan The Machine"
    },
    "gracehop": {
      "nickname": "Amazing Grace"
    }
  }
}

Thêm một lệnh gọi lại hoàn thành

Trong Node.js và SDK quản trị Java, nếu muốn biết thời điểm dữ liệu của mình đã được xác nhận, bạn có thể thêm một lệnh gọi lại hoàn tất. Cả phương thức đặt và cập nhật trong những SDK này đều thực hiện lệnh gọi lại hoàn thành không bắt buộc. Lệnh gọi lại này được gọi khi lệnh ghi được xác nhận vào cơ sở dữ liệu. Nếu cuộc gọi không thành công đối với một số do đó, lệnh gọi lại được truyền một đối tượng lỗi cho biết lý do xảy ra lỗi. Trong các SDK dành cho quản trị viên Python và Go, tất cả phương thức ghi đều bị chặn. Tức là các phương thức ghi không trả về cho đến khi lượt ghi được cam kết với cơ sở dữ liệu.

Java
DatabaseReference dataRef = ref.child("data");
dataRef.setValue("I'm writing data", new DatabaseReference.CompletionListener() {
  @Override
  public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
    if (databaseError != null) {
      System.out.println("Data could not be saved " + databaseError.getMessage());
    } else {
      System.out.println("Data saved successfully.");
    }
  }
});
Node.js
dataRef.set('I\'m writing data', (error) => {
  if (error) {
    console.log('Data could not be saved.' + error);
  } else {
    console.log('Data saved successfully.');
  }
});

Đang lưu danh sách dữ liệu

Khi tạo danh sách dữ liệu, điều quan trọng là phải lưu ý đến tính chất nhiều người dùng của hầu hết các ứng dụng và hãy điều chỉnh cấu trúc danh sách cho phù hợp. Mở rộng ví dụ trên, hãy thêm bài đăng trên blog vào ứng dụng của bạn. Thông tin bản năng đầu tiên có thể là sử dụng tập hợp set để lưu trữ thành phần con cùng với chỉ mục số nguyên tự động tăng dần, chẳng hạn như như sau:

// NOT RECOMMENDED - use push() instead!
{
  "posts": {
    "0": {
      "author": "gracehop",
      "title": "Announcing COBOL, a New Programming Language"
    },
    "1": {
      "author": "alanisawesome",
      "title": "The Turing Machine"
    }
  }
}

Nếu người dùng thêm bài đăng mới, thì bài đăng đó sẽ được lưu trữ dưới dạng /posts/2. Phương pháp này sẽ hiệu quả nếu chỉ có một tác giả đang thêm bài đăng nhưng trong ứng dụng viết blog cộng tác của bạn, nhiều người dùng có thể thêm bài đăng cùng một lúc. Nếu hai tác giả viết vào /posts/2 cùng lúc, sau đó một trong số các bài đăng sẽ bị người kia xoá.

Để giải quyết vấn đề này, ứng dụng Firebase cung cấp hàm push() để tạo khoá duy nhất cho mỗi nhà xuất bản con mới. Bằng cách sử dụng các khoá con duy nhất, một số khách hàng có thể thêm trẻ vào cùng một vị trí cùng lúc mà không phải lo lắng về xung đột khi ghi.

Java
public static class Post {

  public String author;
  public String title;

  public Post(String author, String title) {
    // ...
  }

}

DatabaseReference postsRef = ref.child("posts");

DatabaseReference newPostRef = postsRef.push();
newPostRef.setValueAsync(new Post("gracehop", "Announcing COBOL, a New Programming Language"));

// We can also chain the two calls together
postsRef.push().setValueAsync(new Post("alanisawesome", "The Turing Machine"));
Node.js
const newPostRef = postsRef.push();
newPostRef.set({
  author: 'gracehop',
  title: 'Announcing COBOL, a New Programming Language'
});

// we can also chain the two calls together
postsRef.push().set({
  author: 'alanisawesome',
  title: 'The Turing Machine'
});
Python
posts_ref = ref.child('posts')

new_post_ref = posts_ref.push()
new_post_ref.set({
    'author': 'gracehop',
    'title': 'Announcing COBOL, a New Programming Language'
})

# We can also chain the two calls together
posts_ref.push().set({
    'author': 'alanisawesome',
    'title': 'The Turing Machine'
})
Tiến hành

// Post is a json-serializable type.
type Post struct {
	Author string `json:"author,omitempty"`
	Title  string `json:"title,omitempty"`
}

postsRef := ref.Child("posts")

newPostRef, err := postsRef.Push(ctx, nil)
if err != nil {
	log.Fatalln("Error pushing child node:", err)
}

if err := newPostRef.Set(ctx, &Post{
	Author: "gracehop",
	Title:  "Announcing COBOL, a New Programming Language",
}); err != nil {
	log.Fatalln("Error setting value:", err)
}

// We can also chain the two calls together
if _, err := postsRef.Push(ctx, &Post{
	Author: "alanisawesome",
	Title:  "The Turing Machine",
}); err != nil {
	log.Fatalln("Error pushing child node:", err)
}

Khoá duy nhất dựa trên dấu thời gian, vì vậy, các mục trong danh sách sẽ tự động được sắp xếp theo trình tự thời gian. Vì Firebase tạo một khoá duy nhất cho mỗi bài đăng trên blog nên sẽ không có xung đột khi ghi nếu có nhiều người dùng thêm bài đăng cùng một lúc. Dữ liệu cơ sở dữ liệu của bạn bây giờ có dạng như sau:

{
  "posts": {
    "-JRHTHaIs-jNPLXOQivY": {
      "author": "gracehop",
      "title": "Announcing COBOL, a New Programming Language"
    },
    "-JRHTHaKuITFIhnj02kE": {
      "author": "alanisawesome",
      "title": "The Turing Machine"
    }
  }
}

Trong JavaScript, Python và Go, mẫu gọi push() rồi gọi ngay set() là phổ biến đến mức SDK Firebase cho phép bạn kết hợp chúng bằng cách truyền dữ liệu cần đặt trực tiếp thành push() như sau:

Java
// No Java equivalent
Node.js
// This is equivalent to the calls to push().set(...) above
postsRef.push({
  author: 'gracehop',
  title: 'Announcing COBOL, a New Programming Language'
});;
Python
# This is equivalent to the calls to push().set(...) above
posts_ref.push({
    'author': 'gracehop',
    'title': 'Announcing COBOL, a New Programming Language'
})
Tiến hành
if _, err := postsRef.Push(ctx, &Post{
	Author: "gracehop",
	Title:  "Announcing COBOL, a New Programming Language",
}); err != nil {
	log.Fatalln("Error pushing child node:", err)
}

Lấy khoá duy nhất được tạo bởi push()

Việc gọi push() sẽ trả về một tham chiếu đến đường dẫn dữ liệu mới. Bạn có thể sử dụng đường dẫn này để lấy khoá hoặc thiết lập dữ liệu vào đường dẫn đó. Mã sau đây sẽ trả về dữ liệu tương tự như ví dụ trên, nhưng bây giờ chúng ta sẽ có quyền truy cập vào khoá duy nhất đã được tạo:

Java
// Generate a reference to a new location and add some data using push()
DatabaseReference pushedPostRef = postsRef.push();

// Get the unique ID generated by a push()
String postId = pushedPostRef.getKey();
Node.js
// Generate a reference to a new location and add some data using push()
const newPostRef = postsRef.push();

// Get the unique key generated by push()
const postId = newPostRef.key;
Python
# Generate a reference to a new location and add some data using push()
new_post_ref = posts_ref.push()

# Get the unique key generated by push()
post_id = new_post_ref.key
Tiến hành
// Generate a reference to a new location and add some data using Push()
newPostRef, err := postsRef.Push(ctx, nil)
if err != nil {
	log.Fatalln("Error pushing child node:", err)
}

// Get the unique key generated by Push()
postID := newPostRef.Key

Như bạn có thể thấy, bạn có thể lấy giá trị của khoá duy nhất qua tệp tham chiếu push().

Trong phần tiếp theo về Truy xuất dữ liệu, chúng ta sẽ tìm hiểu cách đọc dữ liệu này từ cơ sở dữ liệu Firebase.

Lưu dữ liệu giao dịch

Khi làm việc với dữ liệu phức tạp có thể bị hỏng do các thao tác sửa đổi đồng thời, chẳng hạn như bộ đếm gia tăng, SDK cung cấp thao tác giao dịch.

Trong Java và Node.js, bạn cung cấp cho hoạt động giao dịch 2 lệnh gọi lại: hàm cập nhật và lệnh gọi lại hoàn thành không bắt buộc. Trong Python và Go, thao tác giao dịch sẽ chặn và do đó chỉ chấp nhận chức năng cập nhật.

Hàm cập nhật lấy trạng thái hiện tại của dữ liệu làm đối số và sẽ trả về trạng thái mong muốn mới mà bạn muốn viết. Ví dụ: nếu bạn muốn tăng số lượng lượt tán thành trên một bài đăng cụ thể trên blog, bạn sẽ viết một giao dịch như sau:

Java
DatabaseReference upvotesRef = ref.child("server/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes");
upvotesRef.runTransaction(new Transaction.Handler() {
  @Override
  public Transaction.Result doTransaction(MutableData mutableData) {
    Integer currentValue = mutableData.getValue(Integer.class);
    if (currentValue == null) {
      mutableData.setValue(1);
    } else {
      mutableData.setValue(currentValue + 1);
    }

    return Transaction.success(mutableData);
  }

  @Override
  public void onComplete(
      DatabaseError databaseError, boolean committed, DataSnapshot dataSnapshot) {
    System.out.println("Transaction completed");
  }
});
Node.js
const upvotesRef = db.ref('server/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes');
upvotesRef.transaction((current_value) => {
  return (current_value || 0) + 1;
});
Python
def increment_votes(current_value):
    return current_value + 1 if current_value else 1

upvotes_ref = db.reference('server/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes')
try:
    new_vote_count = upvotes_ref.transaction(increment_votes)
    print('Transaction completed')
except db.TransactionAbortedError:
    print('Transaction failed to commit')
Tiến hành
fn := func(t db.TransactionNode) (interface{}, error) {
	var currentValue int
	if err := t.Unmarshal(&currentValue); err != nil {
		return nil, err
	}
	return currentValue + 1, nil
}

ref := client.NewRef("server/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes")
if err := ref.Transaction(ctx, fn); err != nil {
	log.Fatalln("Transaction failed to commit:", err)
}

Ví dụ ở trên sẽ kiểm tra xem bộ đếm là null hay chưa được tăng, vì các giao dịch có thể được gọi bằng null nếu không có giá trị mặc định nào được ghi.

Nếu mã ở trên đã được chạy mà không có hàm giao dịch và hai ứng dụng khách đã cố gắng tăng hàm này đồng thời, cả hai sẽ ghi 1 dưới dạng giá trị mới, dẫn đến một mức tăng thay vì hai.

Kết nối mạng và ghi ngoại tuyến

Các ứng dụng Node.js và Java của Firebase duy trì phiên bản nội bộ của riêng mình cho mọi dữ liệu đang hoạt động. Khi dữ liệu được ghi, được ghi vào phiên bản cục bộ này trước tiên. Sau đó, ứng dụng sẽ đồng bộ hoá dữ liệu đó với cơ sở dữ liệu và với các khách hàng khác dựa trên 'nỗ lực tối đa' cơ sở.

Do đó, mọi hoạt động ghi vào cơ sở dữ liệu sẽ kích hoạt các sự kiện cục bộ ngay lập tức, trước khi bất kỳ dữ liệu nào được ghi vào cơ sở dữ liệu. Điều này có nghĩa là khi bạn viết một ứng dụng bằng Firebase, ứng dụng của bạn vẫn sẽ thích ứng bất kể độ trễ mạng hoặc khả năng kết nối Internet.

Khi kết nối được thiết lập lại, chúng tôi sẽ nhận nhóm sự kiện thích hợp để khách hàng "bắt kịp" với trạng thái máy chủ hiện tại mà không phải viết bất kỳ mã tuỳ chỉnh nào.

Bảo mật dữ liệu của bạn

Cơ sở dữ liệu theo thời gian thực của Firebase có ngôn ngữ bảo mật cho phép bạn xác định người dùng nào có quyền đọc và ghi vào các nút khác nhau của dữ liệu của bạn. Bạn có thể đọc thêm về vấn đề này trong phần Bảo mật dữ liệu của bạn.