Tiết kiệm dữ liệu

Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.

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

Cách lưu dữ liệu

bộ Ghi hoặc thay thế dữ liệu vào một đường dẫn xác định , như messages/users/<username>
cập nhật Cập nhật một số khóa cho một đườ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 ra một khóa 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 bởi các bản cập nhật đồng thời

Tiết kiệm dữ liệu

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

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')
Đi
// 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 bằng một tên người dùng duy nhất và chúng tôi cũng sẽ lưu trữ tên đầy đủ và ngày sinh của họ. Vì mỗi người dùng sẽ có một tên người dùng duy nhất, nên sử dụng phương thức set ở đây thay vì phương thức push vì bạn đã có khóa và không cần tạo.

Đầu tiên, hãy tạo một tham chiếu cơ sở dữ liệu đến dữ liệu người dùng của bạn. Sau đó, sử dụng set() / setValue() để lưu một đối tượng người dùng vào cơ sở dữ liệu 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ể chuyển tập hợp một chuỗi, số, boolean, null , mảng hoặc bất kỳ đối tượng JSON nào. Vượt qua null sẽ xóa dữ liệu tại vị trí được chỉ định. Trong trường hợp này, bạn sẽ chuyển cho nó một đố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'
    }
})
Đi

// 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 ánh xạ tới các vị trí con của cơ sở dữ liệu theo kiểu lồng nhau. Bây giờ nếu bạn điều hướng đế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í con:

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'
})
Đi
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ết đồng thời cả hai giá trị dưới dạng một đối tượng và ghi chúng riêng biệt vào các vị trí con - sẽ dẫn đến việc lưu cùng một dữ liệ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 khách hàng đang xem dữ liệu, trong khi ví dụ thứ hai sẽ kích hoạt hai sự kiện. Điều quan trọng cần lưu ý là nếu dữ liệu đã tồn tại ở usersRef , thì phương pháp đầu tiên sẽ ghi đè lên nó, nhưng phương pháp thứ hai sẽ chỉ sửa đổi giá trị của từng nút con riêng biệt trong khi vẫn giữ nguyên các nút con khác của usersRef .

Cập nhật dữ liệu đã lưu

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

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'
})
Đi
hopperRef := usersRef.Child("gracehop")
if err := hopperRef.Update(ctx, map[string]interface{}{
	"nickname": "Amazing Grace",
}); err != nil {
	log.Fatalln("Error updating child:", err)
}

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

Cơ sở dữ liệu thời gian thực của Firebase cũng hỗ trợ các bản cập nhật đa đườ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 các giá trị tại nhiều vị trí trong cơ sở dữ liệu của bạn cùng một lúc, một tính năng mạnh mẽ cho phép giúp bạn chuẩn hóa dữ liệu của mình . Sử dụng 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 một lúc:

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'
})
Đi
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 bản cập nhật này, cả Alan và Grace đã được thêm biệt danh của họ:

{
  "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 các đối tượng bằng cách ghi các đối tượng với các đường dẫn đi kèm sẽ dẫn đến các hành vi khác nhau. Hãy xem điều gì sẽ xảy ra nếu bạn cố gắng cập nhật Grace và Alan theo cách này:

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'
    }
})
Đi
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 hành vi khác nhau, cụ thể là ghi đè toàn bộ /users nút:

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

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

Trong Node.js và Java Admin SDKs, nếu bạn muốn biết khi nào dữ liệu của mình đã được cam kết, bạn có thể thêm lệnh gọi lại hoàn tất. Cả hai phương thức thiết lập và cập nhật trong các SDK này đều có một lệnh gọi lại hoàn thành tùy chọn được gọi khi quá trình ghi đã được cam kết với cơ sở dữ liệu. Nếu cuộc gọi không thành công vì lý do nào đó, cuộc gọi lại được chuyển một đối tượng lỗi cho biết lý do tại sao lỗi xảy ra. Trong các SDK quản trị Python và Go, tất cả các phương thức ghi đều bị chặn. Có nghĩa là, các phương thức ghi sẽ không trả về cho đến khi các phương thức ghi được cam kết vào 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.');
  }
});

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 ghi nhớ tính chất đa người dùng của hầu hết các ứng dụng và điều chỉnh cấu trúc danh sách của bạn cho phù hợp. Mở rộng ví dụ trên, hãy thêm các bài đăng trên blog vào ứng dụng của bạn. Bản năng đầu tiên của bạn có thể là sử dụng set để lưu trữ con với các chỉ mục số nguyên tự động tăng dần, 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 một bài viết mới, nó sẽ được lưu trữ dưới dạng /posts/2 . Điều này sẽ hoạt động nếu chỉ một tác giả duy nhất 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 tới /posts/2 đồng thời, thì một trong hai bài viết sẽ bị người kia xóa.

Để giải quyết vấn đề này, các ứng dụng khách Firebase cung cấp một hàm push() để tạo một khóa duy nhất cho mỗi phần tử con mới . Bằng cách sử dụng các khóa con duy nhất, một số ứng dụng khách có thể thêm các khóa con vào cùng một vị trí tại cùng một thời điểm mà không cần lo lắng về xung đột 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'
})
Đi

// 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)
}

Khóa 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 thứ tự thời gian. Vì Firebase tạo một khóa duy nhất cho mỗi bài đăng trên blog, nên sẽ không có xung đột ghi nào xảy ra nếu 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ờ trông giố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, kiểu gọi push() và sau đó gọi ngay set() phổ biến đến mức SDK Firebase cho phép bạn kết hợp chúng bằng cách chuyển trực tiếp dữ liệu được đặt sang 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'
})
Đi
if _, err := postsRef.Push(ctx, &Post{
	Author: "gracehop",
	Title:  "Announcing COBOL, a New Programming Language",
}); err != nil {
	log.Fatalln("Error pushing child node:", err)
}

Nhận khóa 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 mà bạn có thể sử dụng để lấy khóa hoặc thiết lập dữ liệu cho nó. Đoạn mã sau sẽ dẫn đến dữ liệu giống như ví dụ trên, nhưng bây giờ chúng ta sẽ có quyền truy cập vào khóa 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
Đi
// 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 thấy, bạn có thể lấy giá trị của khóa duy nhất từ ​​tham chiếu push() của mình.

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 bởi các sửa đổi đồng thời, chẳng hạn như bộ đếm gia tăng, SDK cung cấp một hoạt động giao dịch .

Trong Java và Node.js, bạn cung cấp cho hoạt động giao dịch hai lệnh gọi lại: một hàm cập nhật và một lệnh gọi lại hoàn thành tùy chọn. Trong Python và Go, hoạt động giao dịch đang bị chặn và do đó nó 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 ghi. Ví dụ: nếu bạn muốn tăng số phiếu ủng hộ trên một bài đăng blog cụ thể, 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')
Đi
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 kiểm tra xem liệu bộ đếm là null hay chưa được tăng lên, vì các giao dịch có thể được gọi với null nếu không có giá trị mặc định nào được ghi.

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

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

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

Kết quả là, tất cả các lần 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 ứng dụng bằng Firebase, ứng dụng của bạn sẽ vẫn đáp ứng bất kể độ trễ mạng hoặc kết nối Internet.

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

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

Cơ sở dữ liệu 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 truy cập đọc và ghi vào các nút khác nhau trong dữ liệu của bạn. Bạn có thể đọc thêm về nó trong Bảo mật dữ liệu của bạn .